Skip to content

Commit 858f584

Browse files
committed
Save status filter as user feed table preferences
1 parent dc35e25 commit 858f584

File tree

5 files changed

+91
-2
lines changed

5 files changed

+91
-2
lines changed

services/backend-api/client/src/contexts/UserFeedStatusFilterContext.tsx

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1-
import { PropsWithChildren, createContext, useMemo, useState } from "react";
1+
import {
2+
PropsWithChildren,
3+
createContext,
4+
useCallback,
5+
useEffect,
6+
useMemo,
7+
useRef,
8+
useState,
9+
} from "react";
210
import { UserFeedComputedStatus } from "../features/feed/types";
11+
import { useUpdateUserMe, useUserMe } from "../features/discordUser/hooks";
12+
13+
const PREFERENCE_DEBOUNCE_MS = 500;
314

415
interface ContextProps {
516
setStatusFilters: (statuses: UserFeedComputedStatus[]) => void;
@@ -12,7 +23,49 @@ export const UserFeedStatusFilterContext = createContext<ContextProps>({
1223
});
1324

1425
export const UserFeedStatusFilterProvider = ({ children }: PropsWithChildren<{}>) => {
15-
const [statusFilters, setStatusFilters] = useState<UserFeedComputedStatus[]>([]);
26+
const { data: userMe } = useUserMe();
27+
const { mutateAsync: updateUser } = useUpdateUserMe();
28+
29+
const savedStatusFilters = userMe?.result?.preferences?.feedListStatusFilters?.statuses;
30+
const hasInitialized = useRef(false);
31+
32+
const [statusFilters, setStatusFiltersState] = useState<UserFeedComputedStatus[]>([]);
33+
34+
// Initialize from saved preference (only on first load)
35+
useEffect(() => {
36+
if (savedStatusFilters && savedStatusFilters.length > 0 && !hasInitialized.current) {
37+
hasInitialized.current = true;
38+
setStatusFiltersState(savedStatusFilters as UserFeedComputedStatus[]);
39+
}
40+
}, [savedStatusFilters]);
41+
42+
// Save preference when it changes (debounced)
43+
useEffect(() => {
44+
const timeoutId = setTimeout(() => {
45+
const currentFilters = savedStatusFilters || [];
46+
const hasChanges =
47+
statusFilters.length !== currentFilters.length ||
48+
statusFilters.some((status, idx) => status !== currentFilters[idx]);
49+
50+
if (hasChanges) {
51+
updateUser({
52+
details: {
53+
preferences: {
54+
feedListStatusFilters: {
55+
statuses: statusFilters,
56+
},
57+
},
58+
},
59+
});
60+
}
61+
}, PREFERENCE_DEBOUNCE_MS);
62+
63+
return () => clearTimeout(timeoutId);
64+
}, [statusFilters, savedStatusFilters, updateUser]);
65+
66+
const setStatusFilters = useCallback((statuses: UserFeedComputedStatus[]) => {
67+
setStatusFiltersState(statuses);
68+
}, []);
1669

1770
const value = useMemo(
1871
() => ({

services/backend-api/client/src/features/discordUser/api/updateUserMe.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export interface UpdateUserMeInput {
2424
feedListColumnOrder?: {
2525
columns: string[];
2626
};
27+
feedListStatusFilters?: {
28+
statuses: string[];
29+
};
2730
};
2831
};
2932
}

services/backend-api/client/src/features/discordUser/types/UserMe.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export const UserMeSchema = object({
2626
feedListColumnOrder: object({
2727
columns: array(string().required()).required(),
2828
}).optional(),
29+
feedListStatusFilters: object({
30+
statuses: array(string().required()).required(),
31+
}).optional(),
2932
}).default({}),
3033
subscription: object({
3134
product: object({

services/backend-api/src/features/users/dto/update-me-input.dto.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ class FeedListColumnOrderDto {
9696
columns: string[];
9797
}
9898

99+
class FeedListStatusFiltersDto {
100+
@IsArray()
101+
@IsString({ each: true })
102+
@IsIn(["OK", "REQUIRES_ATTENTION", "MANUALLY_DISABLED", "RETRYING"], {
103+
each: true,
104+
})
105+
statuses: string[];
106+
}
107+
99108
class UpdateMeDtoPreferencesDto {
100109
@IsBoolean()
101110
@IsOptional()
@@ -133,6 +142,11 @@ class UpdateMeDtoPreferencesDto {
133142
@ValidateNested()
134143
@Type(() => FeedListColumnOrderDto)
135144
feedListColumnOrder?: FeedListColumnOrderDto;
145+
146+
@IsOptional()
147+
@ValidateNested()
148+
@Type(() => FeedListStatusFiltersDto)
149+
feedListStatusFilters?: FeedListStatusFiltersDto;
136150
}
137151

138152
export class UpdateMeDto {

services/backend-api/src/features/users/entities/user.entity.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ export const UserFeedListColumnOrderSchema = SchemaFactory.createForClass(
5959
UserFeedListColumnOrder
6060
);
6161

62+
@Schema({
63+
timestamps: false,
64+
_id: false,
65+
})
66+
export class UserFeedListStatusFilters {
67+
@Prop({ type: [String] })
68+
statuses: string[];
69+
}
70+
71+
export const UserFeedListStatusFiltersSchema = SchemaFactory.createForClass(
72+
UserFeedListStatusFilters
73+
);
74+
6275
@Schema({
6376
timestamps: false,
6477
_id: false,
@@ -84,6 +97,9 @@ export class UserPreferences {
8497

8598
@Prop({ type: UserFeedListColumnOrderSchema })
8699
feedListColumnOrder?: UserFeedListColumnOrder;
100+
101+
@Prop({ type: UserFeedListStatusFiltersSchema })
102+
feedListStatusFilters?: UserFeedListStatusFilters;
87103
}
88104

89105
export const UserPreferencesSchema =

0 commit comments

Comments
 (0)