[#337] added timezone loading from backend (#386)

* [#337] added timezone loading from backend

* [#337] changed data structure to allow patching timezone while keeping other datetime params value

* [#337] fixed gap, event create modale sync with settings and added checkbox

* [#337] isBrowserDefaultTimezone is set to true when API return null timezone

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2025-12-04 15:14:43 +01:00
committed by GitHub
parent d8f5f9ae46
commit 193c1a7166
23 changed files with 1021 additions and 109 deletions
+18 -6
View File
@@ -49,6 +49,7 @@ import { CalendarEvent } from "./EventsTypes";
import EventUpdateModal from "./EventUpdateModal";
import { useI18n } from "cozy-ui/transpiled/react/providers/I18n";
import { userAttendee } from "../User/userDataTypes";
import { browserDefaultTimeZone } from "../../utils/timezone";
export default function EventPreviewModal({
eventId,
@@ -67,8 +68,8 @@ export default function EventPreviewModal({
const dispatch = useAppDispatch();
const calendars = useAppSelector((state) => state.calendars);
const timezone =
useAppSelector((state) => state.calendars.timeZone) ??
Intl.DateTimeFormat().resolvedOptions().timeZone;
useAppSelector((state) => state.settings.timeZone) ??
browserDefaultTimeZone;
const calendarList = Object.values(
useAppSelector((state) => state.calendars.list)
);
@@ -456,10 +457,10 @@ export default function EventPreviewModal({
)}
</Box>
<Typography color="text.secondaryContainer" gutterBottom>
{formatDate(event.start, t, event.allday)}
{formatDate(event.start, t, timezone, event.allday)}
{event.end &&
formatEnd(event.start, event.end, t, event.allday) &&
` ${formatEnd(event.start, event.end, t, event.allday)} ${!event.allday ? getTimezoneOffset(timezone, new Date(event.start)) : ""}`}
formatEnd(event.start, event.end, t, timezone, event.allday) &&
` ${formatEnd(event.start, event.end, t, timezone, event.allday)} ${!event.allday ? getTimezoneOffset(timezone, new Date(event.start)) : ""}`}
</Typography>
</>
}
@@ -952,13 +953,19 @@ function makeRecurrenceString(
return recur.join(", ");
}
function formatDate(date: Date | string, t: Function, allday?: boolean) {
function formatDate(
date: Date | string,
t: Function,
timeZone: string,
allday?: boolean
) {
if (allday) {
return new Date(date).toLocaleDateString(t("locale"), {
year: "numeric",
month: "long",
weekday: "long",
day: "numeric",
timeZone,
});
} else {
return new Date(date).toLocaleString(t("locale"), {
@@ -968,6 +975,7 @@ function formatDate(date: Date | string, t: Function, allday?: boolean) {
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZone,
});
}
}
@@ -976,6 +984,7 @@ function formatEnd(
start: Date | string,
end: Date | string,
t: Function,
timeZone: string,
allday?: boolean
) {
const startDate = new Date(start);
@@ -993,12 +1002,14 @@ function formatEnd(
year: "numeric",
month: "short",
day: "numeric",
timeZone,
});
} else {
if (sameDay) {
return endDate.toLocaleTimeString(t("locale"), {
hour: "2-digit",
minute: "2-digit",
timeZone,
});
}
return endDate.toLocaleString(t("locale"), {
@@ -1007,6 +1018,7 @@ function formatEnd(
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZone,
});
}
}
+4 -6
View File
@@ -41,6 +41,7 @@ import {
restoreFormDataFromTemp,
EventFormState,
} from "../../utils/eventFormTempStorage";
import { browserDefaultTimeZone } from "../../utils/timezone";
function EventPopover({
open,
@@ -84,17 +85,14 @@ function EventPopover({
const timezoneList = useMemo(() => {
const zones = Object.keys(TIMEZONES.zones).sort();
const browserTz = resolveTimezone(
Intl.DateTimeFormat().resolvedOptions().timeZone
);
const browserTz = resolveTimezone(browserDefaultTimeZone);
return { zones, browserTz, getTimezoneOffset };
}, []);
const calendarTimezone = useAppSelector((state) => state.calendars.timeZone);
const calendarTimezone = useAppSelector((state) => state.settings.timeZone);
const resolvedCalendarTimezone = useMemo(() => {
const tz =
calendarTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
const tz = calendarTimezone || browserDefaultTimeZone;
return resolveTimezone(tz);
}, [calendarTimezone]);
+6 -11
View File
@@ -41,6 +41,7 @@ import {
EventFormState,
EventFormContext,
} from "../../utils/eventFormTempStorage";
import { browserDefaultTimeZone } from "../../utils/timezone";
function EventUpdateModal({
eventId,
@@ -114,9 +115,7 @@ function EventUpdateModal({
const timezoneList = useMemo(() => {
const zones = Object.keys(TIMEZONES.zones).sort();
const browserTz = resolveTimezone(
Intl.DateTimeFormat().resolvedOptions().timeZone
);
const browserTz = resolveTimezone(browserDefaultTimeZone);
const getTimezoneOffset = (tzName: string): string => {
const resolvedTz = resolveTimezone(tzName);
@@ -161,7 +160,7 @@ function EventUpdateModal({
const [busy, setBusy] = useState("OPAQUE");
const [eventClass, setEventClass] = useState("PUBLIC");
const [timezone, setTimezone] = useState(
resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)
resolveTimezone(browserDefaultTimeZone)
);
const [newCalId, setNewCalId] = useState(calId);
const defaultCalendarId = useMemo(
@@ -196,9 +195,7 @@ function EventUpdateModal({
setAlarm("");
setEventClass("PUBLIC");
setBusy("OPAQUE");
setTimezone(
resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)
);
setTimezone(resolveTimezone(browserDefaultTimeZone));
setHasVideoConference(false);
setMeetingLink(null);
}, [defaultCalendarId]);
@@ -230,7 +227,7 @@ function EventUpdateModal({
// Get event's original timezone
const eventTimezone = event.timezone
? resolveTimezone(event.timezone)
: resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone);
: resolveTimezone(browserDefaultTimeZone);
// Format dates based on all-day status
if (event.start) {
@@ -298,9 +295,7 @@ function EventUpdateModal({
const resolvedTimezone = resolveTimezone(event.timezone);
setTimezone(resolvedTimezone);
} else {
const browserTz = resolveTimezone(
Intl.DateTimeFormat().resolvedOptions().timeZone
);
const browserTz = resolveTimezone(browserDefaultTimeZone);
setTimezone(browserTz);
}
setHasVideoConference(event.x_openpass_videoconference ? true : false);
+3 -5
View File
@@ -4,6 +4,7 @@ import { CalendarEvent, RepetitionObject } from "./EventsTypes";
import { userAttendee } from "../User/userDataTypes";
import { formatDateTimeInTimezone } from "../../components/Event/utils/dateTimeFormatters";
import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtils";
import { browserDefaultTimeZone } from "../../utils/timezone";
export interface TimezoneListResult {
zones: string[];
@@ -13,9 +14,7 @@ export interface TimezoneListResult {
export function createTimezoneList(): TimezoneListResult {
const zones = Object.keys(TIMEZONES.zones).sort();
const browserTz = resolveTimezone(
Intl.DateTimeFormat().resolvedOptions().timeZone
);
const browserTz = resolveTimezone(browserDefaultTimeZone);
const getTimezoneOffset = (tzName: string): string => {
const resolvedTz = resolveTimezone(tzName);
@@ -101,8 +100,7 @@ export function populateFormFromEvent(
// Get event's timezone for formatting
const eventTimezone = event.timezone
? resolveTimezone(event.timezone)
: calendarTimezone ||
resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone);
: calendarTimezone || resolveTimezone(browserDefaultTimeZone);
// Format dates based on all-day status and timezone
if (event.start) {