Skip to content

Commit

Permalink
refactor(habits): unify state provider (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
domhhv authored Oct 22, 2024
1 parent bf5969e commit a8f2752
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 640 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"db:diff": "supabase db diff",
"db:migration:up": "supabase migration up",
"db:migration:new": "supabase migration new",
"db:gen-types": "supabase gen types --lang=typescript --local > supabase/database.types.ts"
"db:gen-types": "supabase gen types --lang=typescript --schema public auth storage --local > supabase/database.types.ts"
},
"lint-staged": {
"**/*.{ts,tsx}": [
Expand Down Expand Up @@ -109,7 +109,7 @@
"prettier": "3.1.1",
"prettier-plugin-tailwindcss": "^0.6.6",
"rollup-plugin-visualizer": "^5.12.0",
"supabase": "1.206.0",
"supabase": "1.207.9",
"tailwindcss": "^3.4.10",
"ts-jest": "^29.1.2",
"typescript": "5.6.3",
Expand Down
38 changes: 13 additions & 25 deletions src/components/habit/add-habit/AddHabitDialogButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AddCustomTraitModal, VisuallyHiddenInput } from '@components';
import { useHabits, useSnackbar, useTraits } from '@context';
import { useHabits, useTraits } from '@context';
import { useTextField, useFileField } from '@hooks';
import {
Button,
Expand All @@ -14,13 +14,11 @@ import {
Textarea,
} from '@nextui-org/react';
import { CloudArrowUp, Plus } from '@phosphor-icons/react';
import { StorageBuckets, uploadFile } from '@services';
import { useUser } from '@supabase/auth-helpers-react';
import React from 'react';

const AddHabitDialogButton = () => {
const user = useUser();
const { showSnackbar } = useSnackbar();
const { traits } = useTraits();
const { fetchingHabits, addingHabit, addHabit } = useHabits();
const [open, setOpen] = React.useState(false);
Expand All @@ -44,31 +42,21 @@ const AddHabitDialogButton = () => {
};

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

await addHabit(
{
name,
description,
userId: user?.id || '',
traitId: traitId as unknown as number,
};

let iconPath = '';
userId: user.id,
traitId: +traitId,
},
icon
);

if (icon) {
iconPath = `${user?.id}/icon.name`;
await uploadFile(StorageBuckets.HABIT_ICONS, iconPath, icon);
}

await addHabit(habit);
} catch (error) {
console.error(error);

showSnackbar('Something went wrong while adding your habit', {
color: 'danger',
});
} finally {
handleDialogClose();
}
handleDialogClose();
};

return (
Expand Down
24 changes: 10 additions & 14 deletions src/components/habit/edit-habit/EditHabitDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ const EditHabitDialog = ({
const [description, handleDescriptionChange, , setDescription] =
useTextField();
const [traitId, setTraitId] = React.useState('');
const [isUpdating, setIsUpdating] = React.useState(false);
const { updateHabit } = useHabits();
const { updateHabit, habitIdBeingUpdated } = useHabits();
const { traits } = useTraits();
const user = useUser();

Expand All @@ -59,24 +58,21 @@ const EditHabitDialog = ({
};

const handleSubmit = async () => {
const updatedAt = new Date();
updatedAt.setMilliseconds(0);
updatedAt.setSeconds(0);
setIsUpdating(true);
const newHabit = {
if (!user) {
return null;
}

await updateHabit(habit.id, user.id, {
name,
description,
traitId: +traitId,
userId: user?.id as string,
iconPath: habit.iconPath,
createdAt: habit.createdAt,
updatedAt: updatedAt.toISOString(),
};
await updateHabit(habit.id, newHabit);
setIsUpdating(false);
});

handleClose();
};

const isUpdating = habitIdBeingUpdated === habit.id;

return (
<Modal
isOpen={open}
Expand Down
61 changes: 9 additions & 52 deletions src/components/habit/habits-page/HabitIconCell.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { VisuallyHiddenInput } from '@components';
import { useHabits, useSnackbar } from '@context';
import { useHabits } from '@context';
import { type Habit } from '@models';
import { Button, Tooltip } from '@nextui-org/react';
import { StorageBuckets, updateFile, uploadFile } from '@services';
import { useUser } from '@supabase/auth-helpers-react';
import { getHabitIconUrl } from '@utils';
import React from 'react';
Expand All @@ -12,62 +11,20 @@ type HabitIconCellProps = {
};

const HabitIconCell = ({ habit }: HabitIconCellProps) => {
const { showSnackbar } = useSnackbar();
const { updateHabit } = useHabits();
const user = useUser();
const iconUrl = getHabitIconUrl(habit.iconPath);

const handleFileChange: React.ChangeEventHandler<HTMLInputElement> = async (
event
) => {
const iconFile = event.target.files?.[0];
if (iconFile) {
const existingIconPath = habit.iconPath;

try {
const split = iconFile.name.split('.');
const extension = split[split.length - 1];
const iconPath = `${user?.id}/habit-id-${habit.id}.${extension}`;

if (existingIconPath) {
const { error } = await updateFile(
StorageBuckets.HABIT_ICONS,
iconPath,
iconFile
);

if (error) {
throw error;
}

await updateHabit(habit.id, { ...habit, iconPath });

showSnackbar('Icon replaced!', {
color: 'success',
});
} else {
const { data, error } = await uploadFile(
StorageBuckets.HABIT_ICONS,
iconPath,
iconFile
);

if (error) {
throw error;
}
const handleFileChange: React.ChangeEventHandler<HTMLInputElement> = async ({
target: { files },
}) => {
if (!user || !files) {
return null;
}

await updateHabit(habit.id, { ...habit, iconPath: data.path });
const [iconFile] = files;

showSnackbar('Icon uploaded!', {
color: 'success',
});
}
} catch (e) {
showSnackbar((e as Error).message || 'Failed to upload icon', {
color: 'danger',
});
}
}
await updateHabit(habit.id, user.id, {}, iconFile);
};

return (
Expand Down
9 changes: 3 additions & 6 deletions src/components/habit/habits-page/HabitsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ const habitColumns = [

const HabitsPage = () => {
const user = useUser();
const { habits, removeHabit } = useHabits();
const { habits, removeHabit, habitIdBeingDeleted } = useHabits();
const { removeOccurrencesByHabitId } = useOccurrences();
const [habitToEdit, setHabitToEdit] = React.useState<Habit | null>(null);
const [habitToRemove, setHabitToRemove] = React.useState<Habit | null>(null);
const [isRemovingHabit, setIsRemovingHabit] = React.useState(false);

useDocumentTitle('My Habits | Habitrack');

Expand All @@ -85,11 +84,9 @@ const HabitsPage = () => {
return null;
}

setIsRemovingHabit(true);
await removeHabit(habitToRemove.id);
await removeHabit(habitToRemove);
removeOccurrencesByHabitId(habitToRemove.id);
setHabitToRemove(null);
setIsRemovingHabit(false);
};

const handleEditStart = (habit: Habit) => {
Expand Down Expand Up @@ -201,7 +198,7 @@ const HabitsPage = () => {
heading="Delete habit"
onConfirm={handleRemovalConfirmed}
onCancel={handleRemovalCancel}
loading={isRemovingHabit}
loading={habitIdBeingDeleted === habitToRemove?.id}
>
<div>
Are you sure you want to delete <strong>{habitToRemove?.name}</strong>{' '}
Expand Down
17 changes: 11 additions & 6 deletions src/context/Habits/HabitsContext.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import type { Habit, HabitsMap } from '@models';
import { type HabitsInsert, type HabitsUpdate } from '@services';
import type { Habit, HabitsInsert, HabitsUpdate } from '@models';
import React from 'react';

type HabitsContextType = {
habitIdBeingUpdated: number | null;
habitIdBeingDeleted: number | null;
addingHabit: boolean;
fetchingHabits: boolean;
habits: Habit[];
habitsMap: HabitsMap;
addHabit: (habit: HabitsInsert) => Promise<Habit>;
removeHabit: (habitId: number) => Promise<void>;
updateHabit: (habitId: number, habit: HabitsUpdate) => Promise<Habit>;
addHabit: (habit: HabitsInsert, icon?: File | null) => Promise<void>;
removeHabit: (habit: Habit) => Promise<void>;
updateHabit: (
habitId: number,
userId: string,
habit: HabitsUpdate,
icon?: File | null
) => Promise<void>;
};

export const HabitsContext = React.createContext<HabitsContextType | null>(
Expand Down
Loading

0 comments on commit a8f2752

Please sign in to comment.