Skip to content

Commit 08165ad

Browse files
authored
impr(friends): remember sorting of friends list (@fehmer) (#7062)
Store sorting of the friends list in local storage and apply on load
1 parent b5755fa commit 08165ad

File tree

3 files changed

+47
-23
lines changed

3 files changed

+47
-23
lines changed

frontend/src/ts/pages/friends.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { secondsToString } from "../utils/date-and-time";
1717
import { PersonalBest } from "@monkeytype/schemas/shared";
1818
import Format from "../utils/format";
1919
import { getHtmlByUserFlags } from "../controllers/user-flag-controller";
20-
import { SortedTable } from "../utils/sorted-table";
20+
import { SortedTable, SortSchema } from "../utils/sorted-table";
2121
import { getAvatarElement } from "../utils/discord-avatar";
2222
import { formatTypingStatsRatio } from "../utils/misc";
2323
import { getLanguageDisplayString } from "../utils/strings";
@@ -28,6 +28,7 @@ import * as AuthEvent from "../observables/auth-event";
2828
import { Connection } from "@monkeytype/schemas/connections";
2929
import { Friend, UserNameSchema } from "@monkeytype/schemas/users";
3030
import * as Loader from "../elements/loader";
31+
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
3132

3233
const pageElement = $(".page.pageFriends");
3334

@@ -95,7 +96,7 @@ const addFriendModal = new SimpleModal({
9596
const result = await addFriend(receiverName);
9697

9798
if (result === true) {
98-
return { status: 1, message: `Request send to ${receiverName}` };
99+
return { status: 1, message: `Request sent to ${receiverName}` };
99100
}
100101

101102
let status: -1 | 0 | 1 = -1;
@@ -230,7 +231,11 @@ function updateFriends(): void {
230231
table: ".pageFriends .friends table",
231232
data: friendsList,
232233
buildRow: buildFriendRow,
233-
initialSort: { property: "name", descending: false },
234+
persistence: new LocalStorageWithSchema({
235+
key: "friendsListSort",
236+
schema: SortSchema,
237+
fallback: { property: "name", descending: false },
238+
}),
234239
});
235240
} else {
236241
friendsTable.setData(friendsList);

frontend/src/ts/pages/profile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ $(".page.pageProfile").on("click", ".profile .addFriendButton", async () => {
251251
const result = await addFriend(friendName);
252252

253253
if (result === true) {
254-
Notifications.add(`Request send to ${friendName}`);
254+
Notifications.add(`Request sent to ${friendName}`);
255255
$(".profile .details .addFriendButton").addClass("disabled");
256256
} else {
257257
Notifications.add(result, -1);

frontend/src/ts/utils/sorted-table.ts

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1-
type Sort = {
2-
property: string;
3-
descending: boolean;
1+
import { z } from "zod";
2+
3+
export const SortSchema = z.object({
4+
property: z.string(),
5+
descending: z.boolean(),
6+
});
7+
export type Sort = z.infer<typeof SortSchema>;
8+
9+
type Persistence = {
10+
get: () => Sort;
11+
set: (sort: Sort) => boolean;
412
};
513

614
type SortedTableOptions<T> = {
715
table: string;
816
data?: T[];
917
buildRow: (entry: T) => HTMLTableRowElement;
10-
initialSort?: Sort;
11-
};
18+
} & (
19+
| { initialSort?: Sort; persistence?: never }
20+
| { persistence?: Persistence; initialSort?: never }
21+
);
22+
1223
export class SortedTable<T> {
13-
protected data: { source: T; element?: HTMLTableRowElement }[];
24+
protected data: { source: T; element?: HTMLTableRowElement }[] = [];
1425
private table: JQuery<HTMLTableElement>;
1526
private buildRow: (entry: T) => HTMLTableRowElement;
1627
private sort?: Sort;
28+
private persistence?: Persistence;
1729

18-
constructor({ table, data, buildRow, initialSort }: SortedTableOptions<T>) {
19-
this.table = $(table);
30+
constructor(options: SortedTableOptions<T>) {
31+
this.table = $(options.table);
2032
if (this.table === undefined) {
21-
throw new Error(`No element found for ${table}`);
33+
throw new Error(`No element found for ${options.table}`);
2234
}
2335

24-
this.buildRow = buildRow;
25-
this.data = [];
26-
if (data !== undefined) {
27-
this.setData(data);
36+
this.buildRow = options.buildRow;
37+
38+
if (options.data !== undefined) {
39+
this.setData(options.data);
2840
}
2941

30-
if (initialSort !== undefined) {
31-
this.sort = initialSort;
42+
if ("persistence" in options && options.persistence !== undefined) {
43+
this.persistence = options.persistence;
44+
this.sort = this.persistence.get();
45+
this.doSort();
46+
} else if ("initialSort" in options && options.initialSort !== undefined) {
47+
this.sort = options.initialSort;
3248
this.doSort();
3349
}
3450

@@ -43,20 +59,23 @@ export class SortedTable<T> {
4359
target.dataset["sortDefaultDirection"] === "desc";
4460
if (property === undefined) return;
4561

46-
if (this.sort === undefined || property !== this.sort.property) {
47-
this.sort = { property, descending: defaultDirection };
62+
let updatedSort = this.sort;
63+
64+
if (updatedSort === undefined || property !== updatedSort.property) {
65+
updatedSort = { property, descending: defaultDirection };
4866
} else {
49-
this.sort.descending = !this.sort?.descending;
67+
updatedSort.descending = !updatedSort?.descending;
5068
}
69+
this.setSort(updatedSort);
5170

52-
this.doSort();
5371
this.updateBody();
5472
};
5573
}
5674
}
5775

5876
public setSort(sort: Partial<Sort>): void {
5977
this.sort = { ...this.sort, ...sort } as Sort;
78+
this.persistence?.set(this.sort);
6079
this.doSort();
6180
}
6281

0 commit comments

Comments
 (0)