Skip to content

Commit 87dd103

Browse files
committed
added caching for habits
1 parent 07c2a89 commit 87dd103

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

src/components/habit/break-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const HabitBreakDialog: React.FC<React.PropsWithChildren> = ({ children }
2727
const handleBreak = () => {
2828
toast.promise(
2929
breakHabit
30-
.mutateAsync(habit)
30+
.mutateAsync()
3131
.then(() => setOpen(false))
3232
.then(() => navigate({ to: "/" })),
3333
{

src/components/ordering/provider.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,13 @@ const reorder = (
3636

3737
if (isReordering) return ordered
3838

39-
ordered.sort((a, b) => {
39+
return ordered.sort((a, b) => {
4040
const aCompleted = completed.has(a)
4141
const bCompleted = completed.has(b)
4242
if (aCompleted && bCompleted) return 0
43-
if (aCompleted) return 1
44-
return -1
43+
if (aCompleted) return -1
44+
return 1
4545
})
46-
return ordered
4746
}
4847

4948
const orderChanged = (a: string[], b: string[]): boolean => {

src/lib/git.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { type Collection } from "@/proto/models/v1/models_pb"
1+
import {
2+
type Collection,
3+
CollectionSchema,
4+
type Habit,
5+
HabitSchema,
6+
} from "@/proto/models/v1/models_pb"
7+
import { fromJsonString, toJsonString } from "@bufbuild/protobuf"
28
import type { components } from "@octokit/openapi-types"
39
import type { Octokit } from "octokit"
410
import useLocalStorage from "use-local-storage"
@@ -13,6 +19,30 @@ type Stored<T> =
1319
}
1420
| undefined
1521

22+
type StoredJSON = {
23+
updatedAt: Date
24+
data: string
25+
}
26+
27+
const useStoredProto = <T>(toJSON: (data: T) => string, fromJSON: (data: string) => T) => {
28+
const serializer = (data: Stored<T> | undefined): string => {
29+
if (data === undefined) return ""
30+
return JSON.stringify({
31+
updatedAt: data.updatedAt,
32+
data: toJSON(data.data),
33+
} as StoredJSON)
34+
}
35+
const parser = (raw: string): Stored<T> | undefined => {
36+
if (raw === "") return undefined
37+
const parsed = JSON.parse(raw) as StoredJSON
38+
return {
39+
updatedAt: new Date(parsed.updatedAt),
40+
data: fromJSON(parsed.data),
41+
}
42+
}
43+
return { serializer, parser }
44+
}
45+
1646
export const useStoredRepos = () =>
1747
useLocalStorage<Repo[] | undefined>("habbitses/repos/v1", undefined, {
1848
syncData: true,
@@ -29,8 +59,21 @@ export const useStoredRepoContent = () =>
2959
})
3060

3161
export const useStoredCollection = () =>
32-
useLocalStorage<Stored<Collection>>("habbitses/collection/v1", undefined, {
62+
useLocalStorage<Stored<Collection>>("habbitses/collection/v2", undefined, {
63+
syncData: true,
64+
...useStoredProto<Collection>(
65+
(d) => toJsonString(CollectionSchema, d),
66+
(s) => fromJsonString(CollectionSchema, s),
67+
),
68+
})
69+
70+
export const useStoredHabit = (name: string) =>
71+
useLocalStorage<Stored<Habit>>(`habbitses/habits/v1/${name}`, undefined, {
3372
syncData: true,
73+
...useStoredProto<Habit>(
74+
(d) => toJsonString(HabitSchema, d),
75+
(s) => fromJsonString(HabitSchema, s),
76+
),
3477
})
3578

3679
export function* iterHabitNames(content: RepoContent) {

src/lib/queries.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useStoredAccountContext } from "@/components/auth/account-context"
22
import { useCollectionContext } from "@/components/collection/context"
3+
import { useHabitContext } from "@/components/habit/context"
34
import { useOrderingContext } from "@/components/ordering/context"
45
import { useRepoContentContext } from "@/components/repo/content-context"
56
import { useRepoContext } from "@/components/repo/context"
@@ -21,7 +22,13 @@ import { toast } from "sonner"
2122
import { decode, encode } from "uint8-to-base64"
2223

2324
import { type StoredAccount, useStoredAccount } from "./auth"
24-
import { type RepoContent, useStoredCollection, useStoredRepoContent, useStoredRepos } from "./git"
25+
import {
26+
type RepoContent,
27+
useStoredCollection,
28+
useStoredHabit,
29+
useStoredRepoContent,
30+
useStoredRepos,
31+
} from "./git"
2532

2633
export const client = new QueryClient()
2734

@@ -280,12 +287,15 @@ export const useUpdateHabit = (name: string) => {
280287
}
281288

282289
export const useBreakHabit = () => {
290+
const { habit } = useHabitContext()
283291
const account = useStoredAccountContext()
284292
const repo = useRepoContext()
285293
const octokit = useOctokit()
286294

295+
const [, setStored] = useStoredHabit(habit.name)
296+
287297
return useMutation({
288-
mutationFn: (habit: Habit) => {
298+
mutationFn: () => {
289299
if (repo === undefined) throw new Error("repo is not selected")
290300

291301
return octokit.rest.repos.deleteFile({
@@ -296,8 +306,11 @@ export const useBreakHabit = () => {
296306
sha: habit.sha,
297307
})
298308
},
299-
onSettled: async (_, __, habit) => {
309+
onSuccess: () => {
310+
setStored(undefined)
300311
client.removeQueries({ queryKey: ["repo", repo.id, "habit", habit.name] })
312+
},
313+
onSettled: async () => {
301314
await client.invalidateQueries({ queryKey: ["repo", repo.id, "content"] })
302315
},
303316
retry: handleError("Failed to break a habit", { maxFailures: 0 }),
@@ -318,6 +331,8 @@ export const useHabit = (name: string) => {
318331
const octokit = useOctokit()
319332
const { recordCompletion } = useOrderingContext()
320333

334+
const [stored, setStored] = useStoredHabit(name)
335+
321336
return useQuery({
322337
queryKey: ["repo", repo.id, "habit", name],
323338
queryFn: async ({ signal }) => {
@@ -336,8 +351,11 @@ export const useHabit = (name: string) => {
336351

337352
recordCompletion(habit)
338353

354+
setStored({ data: habit, updatedAt: new Date() })
339355
return habit
340356
},
357+
initialData: stored?.data,
358+
initialDataUpdatedAt: stored?.updatedAt?.getTime?.(),
341359
staleTime: 5 * 60 * 1000, // todo
342360
retry: handleError(`Failed to fetch habit data: ${name}`),
343361
})

0 commit comments

Comments
 (0)