mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-29 18:26:34 +02:00
♻️ Switch to using new startTime column (#1041)
This commit is contained in:
parent
bbbbf5ff49
commit
a3e26bcc9a
7 changed files with 57 additions and 137 deletions
|
@ -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,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue