diff --git a/src/components/calendar/MonthCalendar.tsx b/src/components/calendar/MonthCalendar.tsx index d633635..fb65709 100644 --- a/src/components/calendar/MonthCalendar.tsx +++ b/src/components/calendar/MonthCalendar.tsx @@ -14,50 +14,43 @@ import { useParams } from 'react-router'; import type { CalendarState } from 'react-stately'; import { useFirstDayOfWeek } from '@hooks'; -import type { OccurrenceFilters } from '@models'; -import { useUser, useHabits, useTraits, useCalendarRangeChange } from '@stores'; +import { useCalendarRangeChange } from '@stores'; +import MonthCalendarFilters from './MonthCalendarFilters'; import MonthCalendarGrid from './MonthCalendarGrid'; -import MonthCalendarHeader from './MonthCalendarHeader'; +import MonthCalendarNavigation from './MonthCalendarNavigation'; type MonthCalendarProps = { state: CalendarState; }; const MonthCalendar = ({ state }: MonthCalendarProps) => { - const { user } = useUser(); const changeCalendarRange = useCalendarRangeChange(); - const habits = useHabits(); - const traits = useTraits(); const params = useParams(); const { locale } = useLocale(); - const [filters, setFilters] = React.useState({ - habitIds: new Set(), - traitIds: new Set(), - }); const { firstDayOfWeek } = useFirstDayOfWeek(); - const [fetchedMonthYear, setFetchedMonthYear] = React.useState(''); - - const derivedFilters = React.useMemo(() => { - return { - habitIds: new Set( - Object.values(habits).map((habit) => { - return habit.id.toString(); - }) - ), - traitIds: new Set( - Object.values(traits).map((trait) => { - return trait.id.toString(); - }) - ), - }; - }, [habits, traits]); React.useEffect(() => { - if (!filters.habitIds.size || !filters.traitIds.size) { - setFilters(derivedFilters); - } - }, [derivedFilters, filters.habitIds.size, filters.traitIds.size]); + const focusedDateTime = toCalendarDateTime(state.focusedDate); + + const rangeStart = startOfWeek( + startOfMonth(focusedDateTime), + locale, + firstDayOfWeek + ); + const rangeEnd = endOfWeek( + endOfMonth(focusedDateTime), + locale, + firstDayOfWeek + ).set({ + hour: 23, + millisecond: 999, + minute: 59, + second: 59, + }); + + changeCalendarRange([rangeStart, rangeEnd]); + }, [state.focusedDate, firstDayOfWeek, changeCalendarRange, locale]); React.useEffect(() => { const currentMonth = startOfMonth(today(state.timeZone)); @@ -77,50 +70,15 @@ const MonthCalendar = ({ state }: MonthCalendarProps) => { if (state.focusedDate.toString() !== paramsDate.toString()) { state.setFocusedDate(toCalendarDate(paramsDate)); } - - if (!user || fetchedMonthYear === paramsDate.toString()) { - return; - } - - setFetchedMonthYear(paramsDate.toString()); - - const paramsDateTime = toCalendarDateTime(paramsDate); - - const rangeStart = startOfWeek( - startOfMonth(paramsDateTime), - locale, - firstDayOfWeek - ); - const rangeEnd = endOfWeek( - endOfMonth(paramsDateTime), - locale, - firstDayOfWeek - ).set({ - hour: 23, - millisecond: 999, - minute: 59, - second: 59, - }); - - changeCalendarRange([rangeStart, rangeEnd]); - }, [ - user, - changeCalendarRange, - params, - state, - locale, - fetchedMonthYear, - firstDayOfWeek, - ]); + }, [params, state]); return ( <> - - +
+ + +
+ ); }; diff --git a/src/components/calendar/MonthCalendarFilters.tsx b/src/components/calendar/MonthCalendarFilters.tsx index 7f37a2d..0338d3f 100644 --- a/src/components/calendar/MonthCalendarFilters.tsx +++ b/src/components/calendar/MonthCalendarFilters.tsx @@ -6,27 +6,30 @@ import { SelectItem, SelectSection, } from '@heroui/react'; -import { motion } from 'framer-motion'; +import { motion, AnimatePresence } from 'framer-motion'; import groupBy from 'lodash.groupby'; import React from 'react'; import { TraitChip, CrossPlatformHorizontalScroll } from '@components'; -import type { Habit, Trait, OccurrenceFilters } from '@models'; +import { useScreenWidth } from '@hooks'; +import type { Habit, Trait } from '@models'; import { StorageBuckets } from '@models'; import { getPublicUrl } from '@services'; -import { useHabits, useTraits } from '@stores'; - -export type MonthCalendarFiltersProps = { - filters: OccurrenceFilters; - onFilterChange: (filters: OccurrenceFilters) => void; -}; +import { + useUser, + useHabits, + useTraits, + useCalendarFilters, + useCalendarFiltersChange, +} from '@stores'; -const MonthCalendarFilters = ({ - filters, - onFilterChange, -}: MonthCalendarFiltersProps) => { +const MonthCalendarFilters = () => { const habits = useHabits(); const traits = useTraits(); + const filters = useCalendarFilters(); + const { user } = useUser(); + const { isDesktop } = useScreenWidth(); + const changeCalendarFilters = useCalendarFiltersChange(); const { isOpen: isHabitsFilterSelectOpen, onOpenChange: onHabitsFilterSelectOpenChange, @@ -37,19 +40,19 @@ const MonthCalendarFilters = ({ } = useDisclosure(); const areAllHabitsSelected = React.useMemo(() => { - if (filters.habitIds.size === 1 && filters.habitIds.has('')) { + if (filters.habitIds.length === 1 && filters.habitIds.includes('')) { return false; } - return filters.habitIds.size === Object.keys(habits).length; + return filters.habitIds.length === Object.keys(habits).length; }, [filters.habitIds, habits]); const areAllTraitsSelected = React.useMemo(() => { - if (filters.traitIds.size === 1 && filters.traitIds.has('')) { + if (filters.traitIds.length === 1 && filters.traitIds.includes('')) { return false; } - return filters.traitIds.size === Object.keys(traits).length; + return filters.traitIds.length === Object.keys(traits).length; }, [filters.traitIds, traits]); const habitsByTraitName = React.useMemo(() => { @@ -63,9 +66,9 @@ const MonthCalendarFilters = ({ > = (event) => { if (event.target.value.includes('toggle-all')) { if (areAllHabitsSelected) { - onFilterChange({ + changeCalendarFilters({ ...filters, - habitIds: new Set(['']), + habitIds: [''], }); return; @@ -75,17 +78,17 @@ const MonthCalendarFilters = ({ return id !== ''; }); - onFilterChange({ + changeCalendarFilters({ ...filters, - habitIds: new Set(allHabitIds), + habitIds: allHabitIds, }); return; } - onFilterChange({ + changeCalendarFilters({ ...filters, - habitIds: new Set(event.target.value.split(',')), + habitIds: event.target.value.split(','), }); }; @@ -94,9 +97,9 @@ const MonthCalendarFilters = ({ > = (event) => { if (event.target.value.includes('toggle-all')) { if (areAllTraitsSelected) { - onFilterChange({ + changeCalendarFilters({ ...filters, - traitIds: new Set(['']), + traitIds: [''], }); return; @@ -106,202 +109,212 @@ const MonthCalendarFilters = ({ return id !== ''; }); - onFilterChange({ + changeCalendarFilters({ ...filters, - traitIds: new Set(allTraitIds), + traitIds: allTraitIds, }); return; } - onFilterChange({ + changeCalendarFilters({ ...filters, - traitIds: new Set(event.target.value.split(',')), + traitIds: event.target.value.split(','), }); }; return ( - - {!!Object.keys(habits).length && ( - ) => { + return ( + + {selectedHabits.map(({ key }) => { + if (typeof key !== 'string' || !habits[key]) { + return null; + } + + const { iconPath, id, name } = habits[key]; + + return ( + + {`${name} + + ); + })} + + ); + }} + > + <> + {!!Object.keys(habits).length && ( + + + + {areAllHabitsSelected ? 'Unselect' : 'Select'} all + + + )} + {Object.entries(habitsByTraitName).map( + ([traitName, habits], index, array) => { + if (!habits?.length) { return null; } - const { iconPath, id, name } = habits[key]; - return ( - - {`${name} - + + {habits.map((habit) => { + return ( + +
+ {habit.name} + {habit.name} +
+
+ ); + })} +
); - })} - - ); - }} - > - <> - {!!Object.keys(habits).length && ( - - 1 - } - /> - {areAllHabitsSelected ? 'Unselect' : 'Select'} all - - )} - {Object.entries(habitsByTraitName).map( - ([traitName, habits], index, array) => { - if (!habits?.length) { - return null; } + )} + + + - )} - -
+ return ; + })} + + ); + }} + > + <> + {!!Object.keys(traits).length && ( + + + + {areAllTraitsSelected ? 'Unselect' : 'Select'} all + + + )} + + {Object.values(traits).map((trait) => { + return {trait.name}; + })} + + + + + )} + ); }; diff --git a/src/components/calendar/MonthCalendarGrid.tsx b/src/components/calendar/MonthCalendarGrid.tsx index da5909e..1a5685d 100644 --- a/src/components/calendar/MonthCalendarGrid.tsx +++ b/src/components/calendar/MonthCalendarGrid.tsx @@ -10,19 +10,24 @@ import { Link } from 'react-router'; import type { CalendarState } from 'react-stately'; import { useScreenWidth, useFirstDayOfWeek } from '@hooks'; -import type { OccurrenceFilters } from '@models'; -import { useWeekNotes, useOccurrences, useNoteDrawerActions } from '@stores'; +import { + useWeekNotes, + useOccurrences, + useCalendarFilters, + useNoteDrawerActions, +} from '@stores'; import { getISOWeek } from '@utils'; import type { CellPosition } from './MonthCalendarCell'; import MonthCalendarCell from './MonthCalendarCell'; type CalendarGridProps = { - filters: OccurrenceFilters; + // filters: OccurrenceFilters; state: CalendarState; }; -const MonthCalendarGrid = ({ filters, state }: CalendarGridProps) => { +const MonthCalendarGrid = ({ state }: CalendarGridProps) => { + const filters = useCalendarFilters(); const occurrences = useOccurrences(); const { firstDayOfWeek } = useFirstDayOfWeek(); const { gridProps, weekDays } = useCalendarGrid( @@ -211,9 +216,9 @@ const MonthCalendarGrid = ({ filters, state }: CalendarGridProps) => { return ( o.occurredAt.compare(startDate) >= 0 && o.occurredAt.compare(endDate) <= 0 && - filters.habitIds.has(o.habitId.toString()) && - filters.traitIds.has( - o.habit.trait.id.toString() || '' + filters.habitIds.includes(o.habitId) && + filters.traitIds.includes( + o.habit.trait.id || '' ) ); } diff --git a/src/components/calendar/MonthCalendarHeader.tsx b/src/components/calendar/MonthCalendarHeader.tsx deleted file mode 100644 index 85b1600..0000000 --- a/src/components/calendar/MonthCalendarHeader.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { AnimatePresence } from 'framer-motion'; -import React from 'react'; -import type { CalendarState } from 'react-stately'; - -import { useScreenWidth } from '@hooks'; -import type { OccurrenceFilters } from '@models'; -import { useUser, useHabits, useTraits } from '@stores'; - -import MonthCalendarFilters from './MonthCalendarFilters'; -import MonthCalendarNavigation from './MonthCalendarNavigation'; - -export type MonthCalendarHeaderProps = { - filters: OccurrenceFilters; - state: CalendarState; - onFilterChange: (filters: OccurrenceFilters) => void; -}; - -const MonthCalendarHeader = ({ - filters, - onFilterChange, - state, -}: MonthCalendarHeaderProps) => { - const habits = useHabits(); - const traits = useTraits(); - const { user } = useUser(); - const { isDesktop } = useScreenWidth(); - const [isFilteringShownOnMobile, setIsFilteringShownOnMobile] = - React.useState(false); - - const shouldRenderFilters = React.useMemo(() => { - if (!isDesktop && !isFilteringShownOnMobile) { - return false; - } - - if (Object.keys(habits).length <= 1 && Object.keys(traits).length <= 1) { - return false; - } - - return Boolean(user); - }, [habits, isFilteringShownOnMobile, isDesktop, traits, user]); - - const isFilterToggleVisible = React.useMemo(() => { - if (Object.keys(habits).length <= 1 && Object.keys(traits).length <= 1) { - return false; - } - - return Boolean(user); - }, [habits, traits, user]); - - return ( -
- { - setIsFilteringShownOnMobile((prev) => { - return !prev; - }); - }} - /> - - {shouldRenderFilters && ( - - )} - -
- ); -}; - -export default MonthCalendarHeader; diff --git a/src/components/calendar/MonthCalendarNavigation.tsx b/src/components/calendar/MonthCalendarNavigation.tsx index a90cf60..f51960f 100644 --- a/src/components/calendar/MonthCalendarNavigation.tsx +++ b/src/components/calendar/MonthCalendarNavigation.tsx @@ -16,23 +16,26 @@ import { Link, useNavigate } from 'react-router'; import type { CalendarState } from 'react-stately'; import { useScreenWidth } from '@hooks'; -import { useNoteDrawerActions, useOccurrenceDrawerActions } from '@stores'; +import { + useUser, + useCalendarFilters, + useNoteDrawerActions, + useCalendarFiltersChange, + useOccurrenceDrawerActions, +} from '@stores'; export type MonthCalendarNavigationProps = { - isFilterToggleVisible: boolean; state: CalendarState; - onToggleFilters: () => void; }; const YEARS = Array.from({ length: 31 }, (_, i) => { return 2000 + i; }); -const MonthCalendarNavigation = ({ - isFilterToggleVisible, - onToggleFilters, - state, -}: MonthCalendarNavigationProps) => { +const MonthCalendarNavigation = ({ state }: MonthCalendarNavigationProps) => { + const filters = useCalendarFilters(); + const changeCalendarFilters = useCalendarFiltersChange(); + const { user } = useUser(); const { isDesktop, isMobile, screenWidth } = useScreenWidth(); const [monthSelectValue, setMonthSelectValue] = React.useState( new Set([]) @@ -83,6 +86,13 @@ const MonthCalendarNavigation = ({ navigate(`/calendar/month/${year}/${month}/1`); }; + const toggleFiltersVisibility = () => { + changeCalendarFilters({ + ...filters, + isShownOnMobile: !filters.isShownOnMobile, + }); + }; + return (
@@ -114,15 +124,15 @@ const MonthCalendarNavigation = ({
- {!isDesktop && isFilterToggleVisible && ( + {!isDesktop && !!user && (