frontend: linting hooks/api's
Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago
0e69b3981ab74bef567c19220d0b6dc95b9e909f Browse filesParents
2d01897Changed files
6
Additions
+74
Deletions
-56
frontend/hooks/useMoodEntries.queries.ts
1 1
import {
2 2
useQuery,
3 3
useMutation,
4 4
useQueryClient,
5 5
useInfiniteQuery,
6 -
} from '@tanstack/react-query';
6 +
} from "@tanstack/react-query";
7 7
8 -
import { apiClient, MoodEntry, CreateMoodEntryPayload } from '@/lib/api';
8 +
import { apiClient, MoodEntry, CreateMoodEntryPayload } from "@/lib/api";
9 9
10 10
export const moodKeys = {
11 -
all: () => ['mood-entries'] as const,
12 -
lists: () => [...moodKeys.all(), 'list'] as const,
13 -
list: (filters?: MoodEntryFilters) => [...moodKeys.lists(), filters] as const,
14 -
detail: (id: string) => [...moodKeys.all(), 'detail', id] as const,
11 +
all: () => ["mood-entries"] as const,
12 +
lists: () => [...moodKeys.all(), "list"] as const,
13 +
list: (filters?: MoodEntryFilters) => [...moodKeys.lists(), filters] as const,
14 +
detail: (id: string) => [...moodKeys.all(), "detail", id] as const,
15 15
};
16 16
17 17
interface MoodEntryFilters {
18 -
from?: string; // ISO date
19 -
to?: string; // ISO date
18 +
from?: string; // ISO date
19 +
to?: string; // ISO date
20 20
limit?: number;
21 21
}
22 22
23 23
// Queries
24 24
27 27
export const useMoodEntries = (filters?: MoodEntryFilters) => {
28 28
return useQuery({
29 29
queryKey: moodKeys.list(filters),
30 30
queryFn: () => apiClient.getMoodEntries(filters),
31 31
});
32 -
}
32 +
};
33 33
34 34
// Infinite scroll version
35 35
export const useMoodEntriesInfinite = (limit = 20) => {
36 36
return useInfiniteQuery({
37 -
queryKey: [...moodKeys.lists(), 'infinite'],
37 +
queryKey: [...moodKeys.lists(), "infinite"],
38 38
queryFn: ({ pageParam = 1 }) =>
39 39
apiClient.getMoodEntries({ page: pageParam, limit }),
40 40
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
41 41
initialPageParam: 1,
42 42
});
43 -
}
43 +
};
44 44
45 45
// Single entry detail
46 46
export const useMoodEntry = (id: string) => {
47 47
return useQuery({
48 48
queryKey: moodKeys.detail(id),
49 49
queryFn: () => apiClient.getMoodEntry(id),
50 50
enabled: !!id,
51 51
});
52 -
}
52 +
};
53 53
54 54
// MUTATIONS
55 55
56 56
// Create a new mood entry
57 57
// Optimistically adds to cache, rolls back on failure
95 95
// On success, replace optimistic record with real one from server
96 96
onSuccess: () => {
97 97
queryClient.invalidateQueries({ queryKey: moodKeys.lists() });
98 98
},
99 99
});
100 -
}
100 +
};
101 101
102 102
// Delete a mood entry with optimistic removal
103 103
export const useDeleteMoodEntry = () => {
104 104
const queryClient = useQueryClient();
105 105
109 109
onMutate: async (id) => {
110 110
await queryClient.cancelQueries({ queryKey: moodKeys.lists() });
111 111
const previous = queryClient.getQueryData(moodKeys.list());
112 112
113 113
queryClient.setQueryData(moodKeys.list(), (old: MoodEntry[] = []) =>
114 -
old.filter((e) => e.id !== id)
114 +
old.filter((e) => e.id !== Number(id)),
115 115
);
116 116
117 117
return { previous };
118 118
},
119 119
125 125
126 126
onSuccess: () => {
127 127
queryClient.invalidateQueries({ queryKey: moodKeys.lists() });
128 128
},
129 129
});
130 -
}
130 +
};
frontend/hooks/useSleepRecord.queries.ts
1 -
import {
2 -
useQuery,
3 -
useMutation,
4 -
useQueryClient
5 -
} from '@tanstack/react-query';
1 +
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
6 2
7 -
import { apiClient, SleepRecord, SleepRecordPayload } from '@/lib/api';
3 +
import { apiClient, SleepRecord, SleepRecordPayload } from "@/lib/api";
8 4
9 5
export const sleepRecordKeys = {
10 -
all: () => ['sleep-records'] as const,
11 -
lists: () => [...sleepRecordKeys.all(), 'list'] as const,
12 -
list: (filters?: SleepRecordFilters) => [...sleepRecordKeys.lists(), filters] as const,
13 -
detail: (id: string) => [...sleepRecordKeys.all(), 'detail', id] as const,
6 +
all: () => ["sleep-records"] as const,
7 +
lists: () => [...sleepRecordKeys.all(), "list"] as const,
8 +
list: (filters?: SleepRecordFilters) =>
9 +
[...sleepRecordKeys.lists(), filters] as const,
10 +
detail: (id: string) => [...sleepRecordKeys.all(), "detail", id] as const,
14 11
};
15 12
16 13
interface SleepRecordFilters {
17 -
from?: string; // ISO date
18 -
to?: string; // ISO date
14 +
from?: string; // ISO date
15 +
to?: string; // ISO date
19 16
limit?: number;
20 17
}
21 18
22 19
export const useSleepRecords = (filters?: SleepRecordFilters) => {
23 20
return useQuery({
37 34
await queryClient.cancelQueries({ queryKey: sleepRecordKeys.lists() });
38 35
39 36
const previous = queryClient.getQueryData(sleepRecordKeys.list());
40 37
41 38
// TODO: might as well create a shared attrs that we're keeping it
42 -
queryClient.setQueryData(sleepRecordKeys, (old: SleepRecord[] = []) => [
43 -
{
44 -
...payload,
45 -
id: `temp-${Date.now()}`,
46 -
createdAt: new Date().toISOString(),
47 -
_optimistic: true,
48 -
},
49 -
...old,
50 -
]);
39 +
queryClient.setQueryData(
40 +
sleepRecordKeys.list(),
41 +
(old: SleepRecord[] = []) => [
42 +
{
43 +
...payload,
44 +
id: `temp-${Date.now()}`,
45 +
createdAt: new Date().toISOString(),
46 +
_optimistic: true,
47 +
},
48 +
...old,
49 +
],
50 +
);
51 51
52 52
return { previous };
53 53
},
54 54
55 55
onError: (_err, _payload, context) => {
60 60
61 61
onSuccess: () => {
62 62
queryClient.invalidateQueries({ queryKey: sleepRecordKeys.lists() });
63 63
},
64 64
});
65 -
}
65 +
};
frontend/hooks/useTrigger.queries.ts
1 -
import {
2 -
useQuery,
3 -
useMutation,
4 -
useQueryClient,
5 -
useInfiniteQuery,
6 -
} from "@tanstack/react-query";
1 +
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
7 2
8 3
import { apiClient, Trigger, CreateTriggerPayload } from "@/lib/api";
9 4
10 5
export const triggerKeys = {
11 6
all: () => ["triggers"] as const,
99 94
onMutate: async (id) => {
100 95
await queryClient.cancelQueries({ queryKey: triggerKeys.lists() });
101 96
const previous = queryClient.getQueryData(triggerKeys.list());
102 97
103 98
queryClient.setQueryData(triggerKeys.list(), (old: Trigger[] = []) =>
104 -
old.filter((e) => e.id !== id),
99 +
old.filter((e) => e.id !== Number(id)),
105 100
);
106 101
107 102
return { previous };
108 103
},
109 104
116 111
onSuccess: () => {
117 112
queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
118 113
},
119 114
});
120 115
};
frontend/lib/api/client.ts
1 1
import * as SecureStore from "expo-secure-store";
2 2
import {
3 3
User,
4 4
AuthResponse,
5 5
VerifyTokenResponse,
6 6
SignUpPayload,
7 7
ResetPasswordRequestResponse,
8 8
ResetPasswordResponse,
9 9
UserUpdatePayload,
10 -
UserUpdateResponse,
11 -
MoodComponentPayload,
12 -
MoodEntryPayload,
10 +
CreateMoodEntryPayload,
13 11
SleepRecordPayload,
12 +
MoodEntry,
13 +
SleepRecord,
14 +
SleepRecordResponse,
15 +
MoodEntryResponse,
16 +
Trigger,
17 +
TriggerResponse,
14 18
CreateTriggerPayload,
19 +
ActivateResponse,
15 20
PaginatedResponse,
16 21
} from "@/lib/api/types";
17 22
18 23
const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;
19 24
const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY;
20 25
21 26
class ApiClient {
22 27
private async getStoredToken(): Promise<string | null> {
23 28
try {
24 -
return await SecureStore.getItemAsync(TOKEN_KEY);
29 +
return await SecureStore.getItemAsync(TOKEN_KEY!);
25 30
} catch (error) {
26 31
console.error("Failed to get stored token:", error);
27 32
return null;
28 33
}
29 34
}
30 35
31 36
private async storeToken(token: string): Promise<void> {
32 37
try {
33 -
await SecureStore.setItemAsync(TOKEN_KEY, token);
38 +
await SecureStore.setItemAsync(TOKEN_KEY!, token);
34 39
} catch (error) {
35 40
console.error("Failed to store token:", error);
36 41
throw error;
37 42
}
38 43
}
39 44
40 45
private async removeToken(): Promise<void> {
41 46
try {
42 -
await SecureStore.deleteItemAsync(TOKEN_KEY);
47 +
await SecureStore.deleteItemAsync(TOKEN_KEY!);
43 48
} catch (error) {
44 49
console.error("Failed to remove token:", error);
45 50
}
46 51
}
47 52
210 215
return this.request(`/moods/${id}`, { method: "DELETE" });
211 216
}
212 217
213 218
// Sleep Record
214 219
async createSleepRecord(
215 -
sleepRecord: CreateSleepRecordPayload,
220 +
sleepRecord: SleepRecordPayload,
216 221
): Promise<SleepRecordResponse> {
217 222
return await this.request(`/sleep_records`, {
218 223
method: "POST",
219 224
body: JSON.stringify({ sleepRecord }),
220 225
});
268 273
return this.request(`/triggers/${id}`, { method: "DELETE" });
269 274
}
270 275
}
271 276
272 277
export const apiClient = new ApiClient();
frontend/lib/api/index.ts
1 -
export {
2 -
apiClient
3 -
} from '@/lib/api/client';
1 +
export { apiClient } from "@/lib/api/client";
4 2
5 3
export type {
6 4
User,
7 5
AuthResponse,
6 +
PaginatedResponse,
7 +
// Mood
8 +
MoodEntry,
8 9
CreateMoodEntryPayload,
9 10
MoodComponentPayload,
10 -
PaginatedResponse,
11 +
// User
11 12
UserUpdateResponse,
13 +
// Sleep
12 14
SleepRecordPayload,
13 -
} from '@/lib/api/types';
15 +
SleepRecord,
16 +
// Trigger
17 +
Trigger,
18 +
CreateTriggerPayload,
19 +
} from "@/lib/api/types";
frontend/lib/api/types.ts
1 1
export interface User {
2 2
id: number;
3 3
email: string;
4 4
firstName: string;
5 5
lastName?: string;
127 127
entries: T[];
128 128
total: number;
129 129
page: number;
130 130
nextPage: number | null;
131 131
}
132 +
133 +
export interface MoodEntryResponse {
134 +
moodEntry: MoodEntry;
135 +
}
136 +
137 +
export interface SleepRecordResponse {
138 +
sleepRecord: SleepRecord;
139 +
}
140 +
141 +
export interface TriggerResponse {
142 +
trigger: Trigger;
143 +
}