fix(ui): duplicate schedule warning (#295)
* fix(ui): duplicate schedule warning * fix(ui): duplicate schedule warning * fix(ui): duplicate schedules * fix(ui): schedule limit loophole refactored * fix(ui): schedule bypass hooks * fix(ui): useEnforceScheduleLimit hook created * fix(ui): added useCallback to hook * fix(ui): updated jsdoc comment on hook * fix(ui): updated jsdoc comments on hook
This commit is contained in:
@@ -7,6 +7,7 @@ import { openReportWindow } from '@shared/util/openReportWindow';
|
|||||||
import Divider from '@views/components/common/Divider';
|
import Divider from '@views/components/common/Divider';
|
||||||
import List from '@views/components/common/List';
|
import List from '@views/components/common/List';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
|
||||||
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
|
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
|
||||||
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
|
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
|
||||||
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
||||||
@@ -62,6 +63,13 @@ export default function PopupMain(): JSX.Element {
|
|||||||
// const [isRefreshing, setIsRefreshing] = useState(false);
|
// const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
const [funny, setFunny] = useState<string>('');
|
const [funny, setFunny] = useState<string>('');
|
||||||
|
|
||||||
|
const enforceScheduleLimit = useEnforceScheduleLimit();
|
||||||
|
const handleAddSchedule = () => {
|
||||||
|
if (enforceScheduleLimit()) {
|
||||||
|
createSchedule('New Schedule');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const randomIndex = Math.floor(Math.random() * splashText.length);
|
const randomIndex = Math.floor(Math.random() * splashText.length);
|
||||||
setFunny(
|
setFunny(
|
||||||
@@ -128,7 +136,7 @@ export default function PopupMain(): JSX.Element {
|
|||||||
variant='filled'
|
variant='filled'
|
||||||
color='ut-burntorange'
|
color='ut-burntorange'
|
||||||
className='h-fit p-0 btn'
|
className='h-fit p-0 btn'
|
||||||
onClick={() => createSchedule('New Schedule')}
|
onClick={handleAddSchedule}
|
||||||
>
|
>
|
||||||
<AddSchedule className='h-6 w-6' />
|
<AddSchedule className='h-6 w-6' />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ import { Button } from '@views/components/common/Button';
|
|||||||
import List from '@views/components/common/List';
|
import List from '@views/components/common/List';
|
||||||
import ScheduleListItem from '@views/components/common/ScheduleListItem';
|
import ScheduleListItem from '@views/components/common/ScheduleListItem';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
|
||||||
import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules';
|
import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import AddSchedule from '~icons/material-symbols/add';
|
import AddSchedule from '~icons/material-symbols/add';
|
||||||
|
|
||||||
import { usePrompt } from '../common/DialogProvider/DialogProvider';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a component that displays a list of schedules.
|
* Renders a component that displays a list of schedules.
|
||||||
*
|
*
|
||||||
@@ -19,32 +18,12 @@ import { usePrompt } from '../common/DialogProvider/DialogProvider';
|
|||||||
*/
|
*/
|
||||||
export function CalendarSchedules() {
|
export function CalendarSchedules() {
|
||||||
const [, schedules] = useSchedules();
|
const [, schedules] = useSchedules();
|
||||||
const showDialog = usePrompt();
|
|
||||||
|
|
||||||
|
const enforceScheduleLimit = useEnforceScheduleLimit();
|
||||||
const handleAddSchedule = () => {
|
const handleAddSchedule = () => {
|
||||||
if (schedules.length >= 10) {
|
if (enforceScheduleLimit()) {
|
||||||
showDialog({
|
createSchedule('New Schedule');
|
||||||
title: `You have 10 active schedules!`,
|
|
||||||
|
|
||||||
description: (
|
|
||||||
<>
|
|
||||||
To encourage organization,{' '}
|
|
||||||
<span className='text-ut-burntorange'>please consider removing some unused schedules</span> you
|
|
||||||
may have.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
|
||||||
buttons: close => (
|
|
||||||
<Button variant='filled' color='ut-burntorange' onClick={close}>
|
|
||||||
I Understand
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createSchedule('New Schedule');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import duplicateSchedule from '@pages/background/lib/duplicateSchedule';
|
|||||||
import renameSchedule from '@pages/background/lib/renameSchedule';
|
import renameSchedule from '@pages/background/lib/renameSchedule';
|
||||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
|
||||||
import useSchedules from '@views/hooks/useSchedules';
|
import useSchedules from '@views/hooks/useSchedules';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
@@ -34,6 +35,12 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
|
|||||||
const [editorValue, setEditorValue] = useState(schedule.name);
|
const [editorValue, setEditorValue] = useState(schedule.name);
|
||||||
|
|
||||||
const showDialog = usePrompt();
|
const showDialog = usePrompt();
|
||||||
|
const enforceScheduleLimit = useEnforceScheduleLimit();
|
||||||
|
const handleDuplicateSchedule = (scheduleId: string) => {
|
||||||
|
if (enforceScheduleLimit()) {
|
||||||
|
duplicateSchedule(scheduleId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const editorRef = React.useRef<HTMLInputElement>(null);
|
const editorRef = React.useRef<HTMLInputElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -180,7 +187,7 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
|
|||||||
<Text
|
<Text
|
||||||
as='button'
|
as='button'
|
||||||
variant='small'
|
variant='small'
|
||||||
onClick={() => duplicateSchedule(schedule.id)}
|
onClick={() => handleDuplicateSchedule(schedule.id)}
|
||||||
className='w-full rounded bg-transparent p-2 text-left data-[focus]:bg-gray-200/40'
|
className='w-full rounded bg-transparent p-2 text-left data-[focus]:bg-gray-200/40'
|
||||||
>
|
>
|
||||||
Duplicate
|
Duplicate
|
||||||
|
|||||||
45
src/views/hooks/useEnforceScheduleLimit.tsx
Normal file
45
src/views/hooks/useEnforceScheduleLimit.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import useSchedules from '@views/hooks/useSchedules';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { Button } from '../components/common/Button';
|
||||||
|
import { usePrompt } from '../components/common/DialogProvider/DialogProvider';
|
||||||
|
|
||||||
|
const SCHEDULE_LIMIT = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook that creates a function that enforces a maximum amount of schedules
|
||||||
|
*
|
||||||
|
* If a new schedule can be created without exceeding the limit, the function returns true
|
||||||
|
* Otherwise, display a prompt explaining the limit, and returns false
|
||||||
|
*
|
||||||
|
* @returns a function, () => boolean
|
||||||
|
*/
|
||||||
|
export function useEnforceScheduleLimit(): () => boolean {
|
||||||
|
const [, schedules] = useSchedules();
|
||||||
|
const showDialog = usePrompt();
|
||||||
|
|
||||||
|
return useCallback(() => {
|
||||||
|
if (schedules.length >= SCHEDULE_LIMIT) {
|
||||||
|
showDialog({
|
||||||
|
title: `You have ${SCHEDULE_LIMIT} active schedules!`,
|
||||||
|
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
To encourage organization,{' '}
|
||||||
|
<span className='text-ut-burntorange'>please consider removing some unused schedules</span> you
|
||||||
|
may have.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
|
||||||
|
buttons: close => (
|
||||||
|
<Button variant='filled' color='ut-burntorange' onClick={close}>
|
||||||
|
I Understand
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, [schedules, showDialog]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user