♻️ Switch to using new startTime column (#1041)

This commit is contained in:
Luke Vella 2024-03-17 11:21:54 +07:00 committed by GitHub
parent bbbbf5ff49
commit a3e26bcc9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 57 additions and 137 deletions

View file

@ -16,8 +16,16 @@ import { usePoll } from "@/components/poll-context";
import { Trans } from "@/components/trans";
import { encodeDateOption } from "@/utils/date-time-utils";
const convertOptionToString = (option: { start: Date; duration: number }) => {
const start = dayjs(option.start).utc();
const convertOptionToString = (
option: { startTime: Date; duration: number },
timeZone: string | null,
) => {
let start = dayjs(option.startTime);
if (timeZone) {
start = start.tz(timeZone);
} else {
start = start.utc();
}
return option.duration === 0
? start.format("YYYY-MM-DD")
: `${start.format("YYYY-MM-DDTHH:mm:ss")}/${start
@ -37,12 +45,26 @@ const Page = () => {
const redirectBackToPoll = () => {
router.push(pollLink);
};
let firstDate = dayjs(poll.options[0]?.startTime);
if (poll.timeZone) {
firstDate = firstDate.tz(poll.timeZone);
} else {
firstDate = firstDate.utc();
}
const form = useForm<PollOptionsData>({
defaultValues: {
navigationDate: dayjs(poll.options[0]?.start).utc().format("YYYY-MM-DD"),
navigationDate: firstDate.format("YYYY-MM-DD"),
view: "month",
options: poll.options.map((option) => {
const start = dayjs(option.start).utc();
let start = dayjs(option.startTime);
if (poll.timeZone) {
start = start.tz(poll.timeZone);
} else {
start = start.utc();
}
return option.duration > 0
? {
type: "timeSlot",
@ -68,13 +90,16 @@ const Page = () => {
onSubmit={form.handleSubmit((data) => {
const encodedOptions = data.options.map(encodeDateOption);
const optionsToDelete = poll.options.filter((option) => {
return !encodedOptions.includes(convertOptionToString(option));
return !encodedOptions.includes(
convertOptionToString(option, data.timeZone),
);
});
const optionsToAdd = encodedOptions.filter(
(encodedOption) =>
!poll.options.find(
(o) => convertOptionToString(o) === encodedOption,
(o) =>
convertOptionToString(o, data.timeZone) === encodedOption,
),
);

View file

@ -5,9 +5,7 @@ import { TrashIcon } from "lucide-react";
import { useTranslation } from "next-i18next";
import React from "react";
import { useUser } from "@/components/user-provider";
import {
decodeOptions,
getDuration,
ParsedDateOption,
ParsedTimeSlotOption,
@ -223,27 +221,15 @@ function createOptionsContextValue(
export const OptionsProvider = (props: React.PropsWithChildren) => {
const { poll } = usePoll();
const { timeZone: targetTimeZone, timeFormat } = useDayjs();
const { isInternalUser } = useUser();
const options = React.useMemo(() => {
let res: OptionsContextValue;
if (isInternalUser) {
res = createOptionsContextValue(
poll.options,
targetTimeZone,
poll.timeZone,
);
} else {
// @deprecated - Stop using this method and drop the start column from the database in favor of startTime
res = decodeOptions(
poll.options,
poll.timeZone,
targetTimeZone,
timeFormat,
);
}
return res;
}, [isInternalUser, poll.options, poll.timeZone, targetTimeZone, timeFormat]);
return createOptionsContextValue(
poll.options,
targetTimeZone,
poll.timeZone,
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [poll.options, poll.timeZone, targetTimeZone, timeFormat]);
return (
<OptionsContext.Provider value={options}>

View file

@ -18,7 +18,8 @@ import { DateIcon } from "@/components/date-icon";
import { useParticipants } from "@/components/participants-provider";
import { ConnectedScoreSummary } from "@/components/poll/score-summary";
import { VoteSummaryProgressBar } from "@/components/vote-summary-progress-bar";
import { useDateFormatter, usePoll } from "@/contexts/poll";
import { usePoll } from "@/contexts/poll";
import { useDayjs } from "@/utils/dayjs";
const formSchema = z.object({
selectedOptionId: z.string(),
@ -69,6 +70,7 @@ export const FinalizePollForm = ({
const poll = usePoll();
const [max, setMax] = React.useState(pageSize);
const { adjustTimeZone } = useDayjs();
const scoreByOptionId = useScoreByOptionId();
const { participants } = useParticipants();
@ -96,7 +98,6 @@ export const FinalizePollForm = ({
return { ...option, votes: scoreByOptionId[option.id] };
});
const dateFormatter = useDateFormatter();
const form = useForm<FinalizeFormData>({
defaultValues: {
selectedOptionId: options[0].id,
@ -127,10 +128,16 @@ export const FinalizePollForm = ({
className="grid gap-2"
>
{options.slice(0, max).map((option) => {
const start = dateFormatter(option.start);
const end = dateFormatter(
dayjs(option.start).add(option.duration, "minute"),
const start = adjustTimeZone(
option.startTime,
!poll.timeZone,
);
const end = adjustTimeZone(
dayjs(option.startTime).add(option.duration, "minute"),
!poll.timeZone,
);
return (
<label
key={option.id}

View file

@ -27,17 +27,7 @@ export const UserContext = React.createContext<{
} | null>(null);
export const useUser = () => {
const value = useRequiredContext(UserContext, "UserContext");
return { ...value, isInternalUser: value.user.email?.endsWith("@rallly.co") };
};
export const useAuthenticatedUser = () => {
const { user, ...rest } = useRequiredContext(UserContext, "UserContext");
if (user.isGuest) {
throw new Error("Forget to prefetch user identity");
}
return { user, ...rest };
return useRequiredContext(UserContext, "UserContext");
};
export const IfAuthenticated = (props: { children?: React.ReactNode }) => {

View file

@ -1,8 +1,6 @@
import dayjs, { Dayjs } from "dayjs";
import { useParams } from "next/navigation";
import React from "react";
import { useDayjs } from "@/utils/dayjs";
import { trpc } from "@/utils/trpc/client";
export const usePoll = () => {
@ -23,16 +21,3 @@ export const usePoll = () => {
return pollQuery.data;
};
export const useDateFormatter = () => {
const { timeZone } = usePoll();
const { timeZone: preferredTimeZone } = useDayjs();
return (date: Date | Dayjs) => {
if (timeZone) {
return dayjs(date).utc().tz(timeZone, true).tz(preferredTimeZone);
}
return dayjs(date).utc();
};
};

View file

@ -1,4 +1,3 @@
import { TimeFormat } from "@rallly/database";
import dayjs from "dayjs";
import soft from "timezone-soft";
@ -9,8 +8,6 @@ import {
TimeOption,
} from "../components/forms/poll-options-form";
type Option = { id: string; start: Date; duration: number };
export function parseIanaTimezone(timezone: string): {
region: string;
city: string;
@ -86,80 +83,6 @@ export const getDuration = (startTime: dayjs.Dayjs, endTime: dayjs.Dayjs) => {
return res;
};
export const decodeOptions = (
options: Option[],
timeZone: string | null,
targetTimeZone: string,
timeFormat: TimeFormat, // TODO (Luke Vella) [2022-06-28]: Need to pass timeFormat so that we recalculate the options when timeFormat changes. There is definitely a better way to do this
):
| { pollType: "date"; options: ParsedDateOption[] }
| { pollType: "timeSlot"; options: ParsedTimeSlotOption[] } => {
const pollType = options.some(({ duration }) => duration > 0)
? "timeSlot"
: "date";
if (pollType === "timeSlot") {
return {
pollType,
options: options.map((option) =>
parseTimeSlotOption(option, timeZone, targetTimeZone, timeFormat),
),
};
} else {
return {
pollType,
options: options.map((option) => parseDateOption(option)),
};
}
};
export const parseDateOption = (option: Option): ParsedDateOption => {
const date = dayjs(option.start).utc();
return {
type: "date",
optionId: option.id,
day: date.format("D"),
dow: date.format("ddd"),
month: date.format("MMM"),
year: date.format("YYYY"),
};
};
export const parseTimeSlotOption = (
option: Option,
timeZone: string | null,
targetTimeZone: string,
timeFormat: TimeFormat,
): ParsedTimeSlotOption => {
const adjustTimeZone = (date: string) => {
if (!timeZone) {
return dayjs(date);
}
return dayjs.tz(date, timeZone).tz(targetTimeZone);
};
const start = dayjs(option.start).utc().format("YYYY-MM-DD HH:mm");
const startDate = adjustTimeZone(start);
const endDate = adjustTimeZone(
dayjs(start).add(option.duration, "minute").format("YYYY-MM-DD HH:mm"),
);
return {
type: "timeSlot",
optionId: option.id,
startTime: startDate.format(timeFormat === "hours12" ? "h:mm A" : "HH:mm"),
endTime: endDate.format(timeFormat === "hours12" ? "h:mm A" : "HH:mm"),
day: startDate.format("D"),
dow: startDate.format("ddd"),
month: startDate.format("MMM"),
duration: getDuration(startDate, endDate),
year: startDate.format("YYYY"),
};
};
export const removeAllOptionsForDay = (
options: DateTimeOption[],
date: Date,

View file

@ -217,8 +217,12 @@ export const DayjsProvider: React.FunctionComponent<{
);
const adjustTimeZone = React.useCallback(
(date: dayjs.ConfigType, keepLocalTime = false) => {
return dayjs(date).tz(preferredTimeZone, keepLocalTime);
(date: dayjs.ConfigType, localTime = false) => {
if (!localTime) {
return dayjs(date).tz(preferredTimeZone);
} else {
return dayjs(date).utc();
}
},
[preferredTimeZone],
);