revise url format

This commit is contained in:
0x464e 2026-03-19 16:33:30 +02:00
parent ec3fc782a8
commit 05d29ebc93
No known key found for this signature in database
GPG Key ID: E6D221DF6CBFBFFA
3 changed files with 45 additions and 15 deletions

View File

@ -24,6 +24,7 @@ import {
import {
parseRecordingReviewLink,
RECORDING_REVIEW_START_PARAM,
RECORDING_REVIEW_TIMEZONE_PARAM,
} from "@/utils/recordingReviewUrl";
import EventView from "@/views/events/EventView";
import MotionSearchView from "@/views/motion-search/MotionSearchView";
@ -248,6 +249,7 @@ export default function Events() {
useEffect(() => {
const timestamp = searchParams.get(RECORDING_REVIEW_START_PARAM);
const timezone = searchParams.get(RECORDING_REVIEW_TIMEZONE_PARAM);
if (!timestamp) {
return;
@ -257,7 +259,7 @@ export default function Events() {
? decodeURIComponent(location.hash.substring(1))
: null;
const reviewLink = parseRecordingReviewLink(camera, timestamp);
const reviewLink = parseRecordingReviewLink(camera, timestamp, timezone);
if (!reviewLink) {
navigate(location.pathname + location.hash, {

View File

@ -1,19 +1,39 @@
import { formatInTimeZone, fromZonedTime } from "date-fns-tz";
export const RECORDING_REVIEW_START_PARAM = "start_time";
export const RECORDING_REVIEW_TIMEZONE_PARAM = "timezone";
export type RecordingReviewLinkState = {
camera: string;
timestamp: number;
};
function formatRecordingReviewTimestamp(
timestamp: number,
timezone: string | undefined,
): string {
const date = new Date(Math.floor(timestamp) * 1000);
if (timezone) {
return formatInTimeZone(date, timezone, "yyyy-MM-dd'T'HH:mm:ss");
}
return formatInTimeZone(date, "UTC", "yyyy-MM-dd'T'HH:mm:ss'Z'");
}
export function parseRecordingReviewLink(
camera: string | null,
start: string | null,
timezone: string | null,
): RecordingReviewLinkState | undefined {
if (!camera || !start) {
return undefined;
}
const parsedTimestamp = Number(start);
const parsedDate = timezone
? fromZonedTime(start, timezone)
: new Date(start);
const parsedTimestamp = parsedDate.getTime() / 1000;
if (!Number.isFinite(parsedTimestamp)) {
return undefined;
@ -28,15 +48,19 @@ export function parseRecordingReviewLink(
export function createRecordingReviewUrl(
pathname: string,
state: RecordingReviewLinkState,
timezone?: string,
): string {
const url = new URL(window.location.href);
url.pathname = pathname;
url.hash = state.camera;
url.search = "";
url.searchParams.set(
RECORDING_REVIEW_START_PARAM,
`${Math.floor(state.timestamp)}`,
const url = new URL(globalThis.location.href);
const formattedTimestamp = formatRecordingReviewTimestamp(
state.timestamp,
timezone,
);
const normalizedPathname = pathname.startsWith("/")
? pathname
: `/${pathname}`;
const timezoneParam = timezone
? `&${RECORDING_REVIEW_TIMEZONE_PARAM}=${encodeURIComponent(timezone)}`
: "";
return url.toString();
return `${url.origin}${normalizedPathname}?${RECORDING_REVIEW_START_PARAM}=${formattedTimestamp}${timezoneParam}#${encodeURIComponent(state.camera)}`;
}

View File

@ -334,16 +334,20 @@ export function RecordingView({
}, [startTime, manuallySetCurrentTime]);
const onCopyReviewLink = useCallback(() => {
const reviewUrl = createRecordingReviewUrl(location.pathname, {
camera: mainCamera,
timestamp: Math.floor(currentTime),
});
const reviewUrl = createRecordingReviewUrl(
location.pathname,
{
camera: mainCamera,
timestamp: Math.floor(currentTime),
},
config?.ui.timezone,
);
copy(reviewUrl);
toast.success(t("toast.copyUrlToClipboard", { ns: "common" }), {
position: "top-center",
});
}, [location.pathname, mainCamera, currentTime, t]);
}, [location.pathname, mainCamera, currentTime, config?.ui.timezone, t]);
useEffect(() => {
if (!scrubbing) {