Skip to content

Commit

Permalink
refactor(traits): unify state provider (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
domhhv authored Oct 21, 2024
1 parent 87b1845 commit bf5969e
Show file tree
Hide file tree
Showing 12 changed files with 44 additions and 63 deletions.
7 changes: 3 additions & 4 deletions src/components/calendar/CalendarHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,12 @@ const CalendarHeader = ({
onResetFocusedDate,
}: CalendarHeaderProps) => {
const { habits } = useHabits();
const { allTraits } = useTraits();
const { traits } = useTraits();
const { filteredBy, filterBy } = useOccurrences();
const user = useUser();
const screenSize = useScreenSize();

const shouldRenderFilters =
!!user && habits.length > 0 && allTraits.length > 0;
const shouldRenderFilters = !!user && habits.length > 0 && traits.length > 0;

const handleMonthChange: React.ChangeEventHandler<HTMLSelectElement> = (
event
Expand Down Expand Up @@ -173,7 +172,7 @@ const CalendarHeader = ({
crossOffset: screenSize < 1280 ? -75 : 0,
}}
>
{allTraits.map((trait) => (
{traits.map((trait) => (
<SelectItem key={trait.id}>{trait.name}</SelectItem>
))}
</Select>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jest.mock('@context', () => ({
useHabits: jest.fn().mockReturnValue({ updateHabit: jest.fn() }),
useSnackbar: jest.fn().mockReturnValue({ showSnackbar: jest.fn() }),
useTraits: jest.fn().mockReturnValue({
allTraits: [{ id: 1, slug: 'trait-slug', name: 'Trait' }],
traits: [{ id: 1, slug: 'trait-slug', name: 'Trait' }],
}),
}));

Expand Down
4 changes: 2 additions & 2 deletions src/components/habit/add-habit/AddHabitDialogButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import React from 'react';
const AddHabitDialogButton = () => {
const user = useUser();
const { showSnackbar } = useSnackbar();
const { allTraits } = useTraits();
const { traits } = useTraits();
const { fetchingHabits, addingHabit, addHabit } = useHabits();
const [open, setOpen] = React.useState(false);
const [name, handleNameChange, clearName] = useTextField();
Expand Down Expand Up @@ -116,7 +116,7 @@ const AddHabitDialogButton = () => {
selectedKeys={[traitId]}
data-testid="habit-select"
>
{allTraits.map((trait) => (
{traits.map((trait) => (
<SelectItem
key={trait.id.toString()}
onClick={(e) => {
Expand Down
6 changes: 5 additions & 1 deletion src/components/habit/add-trait/AddCustomTraitModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ const AddCustomTraitModal = ({ open, onClose }: AddCustomTraitModalProps) => {
};

const handleAdd = async () => {
if (!user) {
return null;
}

await addTrait({
name: label,
description,
slug,
color,
userId: null,
userId: user.id,
});

handleDialogClose();
Expand Down
4 changes: 2 additions & 2 deletions src/components/habit/edit-habit/EditHabitDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jest.mock('@hooks', () => ({
jest.mock('@context', () => ({
useHabits: jest.fn().mockReturnValue({ updateHabit: jest.fn() }),
useTraits: jest.fn().mockReturnValue({
allTraits: [{ id: 1, label: 'Trait label', slug: 'trait-slug' }],
traits: [{ id: 1, label: 'Trait label', slug: 'trait-slug' }],
}),
TraitsProvider: jest.fn(({ children }) => children),
}));
Expand Down Expand Up @@ -102,7 +102,7 @@ describe(EditHabitDialog.name, () => {
const mockUpdateHabit = jest.fn();
(useHabits as jest.Mock).mockReturnValue({ updateHabit: mockUpdateHabit });
(useTraits as jest.Mock).mockReturnValue({
allTraits: [{ id: 1, label: 'Trait label', slug: 'trait-slug' }],
traits: [{ id: 1, label: 'Trait label', slug: 'trait-slug' }],
});
const { getByRole, getByLabelText } = render(
<TraitsProvider>
Expand Down
4 changes: 2 additions & 2 deletions src/components/habit/edit-habit/EditHabitDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const EditHabitDialog = ({
const [traitId, setTraitId] = React.useState('');
const [isUpdating, setIsUpdating] = React.useState(false);
const { updateHabit } = useHabits();
const { allTraits } = useTraits();
const { traits } = useTraits();
const user = useUser();

React.useEffect(() => {
Expand Down Expand Up @@ -108,7 +108,7 @@ const EditHabitDialog = ({
selectedKeys={[traitId]}
data-testid="habit-select"
>
{allTraits.map((trait) => (
{traits.map((trait) => (
<SelectItem
key={trait.id.toString()}
onClick={() => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/habit/habits-page/HabitsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jest.mock('@context', () => ({
TraitsProvider: jest.fn(({ children }) => children),
HabitsProvider: jest.fn(({ children }) => children),
useTraits: jest.fn().mockReturnValue({
allTraits: [],
traits: [],
}),
useHabits: jest.fn(),
useSnackbar: jest.fn().mockReturnValue({}),
Expand Down
8 changes: 3 additions & 5 deletions src/context/Occurrences/OccurrencesProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type OccurrenceFilters = {
const OccurrencesProvider = ({ children, rangeStart, rangeEnd }: Props) => {
const { showSnackbar } = useSnackbar();
const { habits } = useHabits();
const { allTraits } = useTraits();
const { traits } = useTraits();

const [addingOccurrence, setAddingOccurrence] = React.useState(false);
const [fetchingOccurrences, setFetchingOccurrences] = React.useState(false);
Expand Down Expand Up @@ -67,14 +67,12 @@ const OccurrencesProvider = ({ children, rangeStart, rangeEnd }: Props) => {

React.useEffect(() => {
const initialFilteredHabitIds = habits.map((habit) => habit.id.toString());
const initialFilteredTraitIds = allTraits.map((trait) =>
trait.id.toString()
);
const initialFilteredTraitIds = traits.map((trait) => trait.id.toString());
setFilteredBy({
habitIds: new Set(initialFilteredHabitIds),
traitIds: new Set(initialFilteredTraitIds),
});
}, [habits, allTraits]);
}, [habits, traits]);

React.useEffect(() => {
setOccurrences(
Expand Down
6 changes: 2 additions & 4 deletions src/context/Traits/TraitsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { type TraitsInsert } from '@services';
import React from 'react';

type TraitsContextType = {
traits: Trait[];
fetchingTraits: boolean;
addingTrait: boolean;
addTrait: (trait: TraitsInsert) => Promise<void>;
allTraits: Trait[];
publicTraits: Trait[];
userTraits: Trait[];
fetchingTraits: boolean;
};

export const TraitsContext = React.createContext<TraitsContextType | null>(
Expand Down
37 changes: 13 additions & 24 deletions src/context/Traits/TraitsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,28 @@ import { TraitsContext, useSnackbar } from '@context';
import { useDataFetch } from '@hooks';
import type { Trait } from '@models';
import { listTraits, createTrait, type TraitsInsert } from '@services';
import { useUser } from '@supabase/auth-helpers-react';
import { makeTestTrait } from '@tests';
import React, { type ReactNode } from 'react';

const testTraits = [
makeTestTrait({ name: 'Test Good Trait', color: '#2AF004' }),
makeTestTrait({ name: 'Test Bad Trait', color: '#F6F6F6' }),
];

const TraitsProvider = ({ children }: { children: ReactNode }) => {
const [publicTraits, setPublicTraits] = React.useState<Trait[]>([]);
const [userTraits, setUserTraits] = React.useState<Trait[]>([
makeTestTrait({ name: 'Test Good Trait', color: '#2AF004' }),
makeTestTrait({ name: 'Test Bad Trait', color: '#F6F6F6' }),
]);
const [traits, setTraits] = React.useState<Trait[]>(testTraits);
const [fetchingTraits, setFetchingTraits] = React.useState(false);
const [addingTrait, setAddingTrait] = React.useState(false);
const { showSnackbar } = useSnackbar();
const user = useUser();

const clearTraits = React.useCallback(() => {
setPublicTraits([]);
setUserTraits([]);
setTraits([]);
}, []);

const fetchTraits = React.useCallback(async () => {
setFetchingTraits(true);

const traits = await listTraits();

const publicTraits = traits.filter((trait: Trait) => !trait.userId);
const userTraits = traits.filter((trait: Trait) => trait.userId);
setPublicTraits(publicTraits);
setUserTraits(userTraits);
setTraits(traits);
setFetchingTraits(false);
}, []);

Expand All @@ -44,9 +37,9 @@ const TraitsProvider = ({ children }: { children: ReactNode }) => {
try {
setAddingTrait(true);

const newTrait = await createTrait({ ...trait, userId: user!.id });
const newTrait = await createTrait(trait);

setUserTraits((prevUserTraits) => [...prevUserTraits, newTrait]);
setTraits((prevUserTraits) => [...prevUserTraits, newTrait]);

showSnackbar('Trait added successfully', {
color: 'success',
Expand All @@ -67,21 +60,17 @@ const TraitsProvider = ({ children }: { children: ReactNode }) => {
setAddingTrait(false);
}
},
[showSnackbar, user]
[showSnackbar]
);

const value = React.useMemo(() => {
const allTraits = [...publicTraits, ...userTraits];

return {
addingTrait,
allTraits,
publicTraits,
userTraits,
traits,
fetchingTraits,
addTrait,
};
}, [addingTrait, publicTraits, userTraits, fetchingTraits, addTrait]);
}, [addingTrait, traits, fetchingTraits, addTrait]);

return (
<TraitsContext.Provider value={value}>{children}</TraitsContext.Provider>
Expand Down
15 changes: 5 additions & 10 deletions src/models/trait.model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
export type Trait = {
id: number;
name: string;
description: string | null;
slug: string;
userId: string | null;
createdAt: string;
updatedAt: string | null;
color: string;
};
import { type CamelCasedPropertiesDeep } from 'type-fest';

import { type Tables } from '../../supabase/database.types';

export type Trait = CamelCasedPropertiesDeep<Tables<'traits'>>;
12 changes: 5 additions & 7 deletions src/models/user-account.model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export type Account = {
id: string;
email: string;
createdAt: string;
updatedAt: string | null;
name: string | null;
};
import { type CamelCasedPropertiesDeep } from 'type-fest';

import { type Tables } from '../../supabase/database.types';

export type Account = CamelCasedPropertiesDeep<Tables<'accounts'>>;

0 comments on commit bf5969e

Please sign in to comment.