@remit/web-client 0.0.44 → 0.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/mail/MessageActionMenu.tsx +24 -63
- package/src/components/mail/MobileMessageBar.tsx +8 -10
- package/src/hooks/useEscalatedActions.test.ts +36 -0
- package/src/hooks/useEscalatedActions.ts +21 -20
- package/src/hooks/useMoveMessages.ts +8 -4
- package/src/lib/thread-list-cache.test.ts +104 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.45",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
|
|
6
6
|
"exports": {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
messageBulkOperationsUpdateFlagsMutation,
|
|
3
3
|
threadDetailOperationsListThreadMessagesQueryKey,
|
|
4
|
-
threadOperationsListThreadsQueryKey,
|
|
5
|
-
threadOperationsSearchThreadsQueryKey,
|
|
6
4
|
} from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
7
5
|
import type { RemitImapThreadMessageResponse } from "@remit/api-http-client/types.gen.ts";
|
|
8
6
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
@@ -25,9 +23,18 @@ import { useErrorBanners } from "@/components/ui/ErrorBannerProvider";
|
|
|
25
23
|
import { ErrorState } from "@/components/ui/ErrorState";
|
|
26
24
|
import { formatErrorDetail } from "@/components/ui/error-banners";
|
|
27
25
|
import { useDeleteMessages } from "@/hooks/useDeleteMessages";
|
|
26
|
+
import { setReadOnItems } from "@/hooks/useMarkAsRead";
|
|
28
27
|
import { useMoveMessages } from "@/hooks/useMoveMessages";
|
|
29
28
|
import { useToggleTrusted } from "@/hooks/useToggleTrusted";
|
|
30
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
cancelThreadListQueries,
|
|
31
|
+
invalidateThreadListQueries,
|
|
32
|
+
patchThreadListQueries,
|
|
33
|
+
restoreThreadListQueries,
|
|
34
|
+
snapshotThreadListQueries,
|
|
35
|
+
type ThreadListSnapshotEntry,
|
|
36
|
+
threadListCacheKeys,
|
|
37
|
+
} from "@/lib/thread-list-cache";
|
|
31
38
|
import { MoveToTrigger } from "./MoveToTrigger";
|
|
32
39
|
|
|
33
40
|
interface ThreadMessagesData {
|
|
@@ -35,13 +42,6 @@ interface ThreadMessagesData {
|
|
|
35
42
|
[key: string]: unknown;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
/**
|
|
39
|
-
* Either shape a thread list/search query caches — the infinite mailbox list
|
|
40
|
-
* or a single-shot page. `setQueriesData` matches by key prefix, so the
|
|
41
|
-
* optimistic updater sees both.
|
|
42
|
-
*/
|
|
43
|
-
type ThreadsListData = ThreadListCache;
|
|
44
|
-
|
|
45
45
|
interface SnapshotEntry<T> {
|
|
46
46
|
queryKey: readonly unknown[];
|
|
47
47
|
data: T;
|
|
@@ -49,10 +49,9 @@ interface SnapshotEntry<T> {
|
|
|
49
49
|
|
|
50
50
|
interface MarkUnreadContext {
|
|
51
51
|
threadMessagesPrefix: readonly unknown[];
|
|
52
|
-
|
|
53
|
-
threadsSearchPrefix: readonly unknown[];
|
|
52
|
+
listPrefixes: ReadonlyArray<readonly unknown[]>;
|
|
54
53
|
previousThreadMessages: SnapshotEntry<ThreadMessagesData>[];
|
|
55
|
-
previousThreadsList:
|
|
54
|
+
previousThreadsList: ThreadListSnapshotEntry[];
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
interface MessageActionMenuProps {
|
|
@@ -118,17 +117,11 @@ export const MessageActionMenu = ({
|
|
|
118
117
|
threadDetailOperationsListThreadMessagesQueryKey({
|
|
119
118
|
path: { threadId },
|
|
120
119
|
});
|
|
121
|
-
const
|
|
122
|
-
path: { mailboxId },
|
|
123
|
-
});
|
|
124
|
-
const threadsSearchPrefix = threadOperationsSearchThreadsQueryKey({
|
|
125
|
-
path: { mailboxId },
|
|
126
|
-
});
|
|
120
|
+
const listPrefixes = threadListCacheKeys([mailboxId]);
|
|
127
121
|
|
|
128
122
|
await Promise.all([
|
|
129
123
|
queryClient.cancelQueries({ queryKey: threadMessagesPrefix }),
|
|
130
|
-
queryClient
|
|
131
|
-
queryClient.cancelQueries({ queryKey: threadsSearchPrefix }),
|
|
124
|
+
cancelThreadListQueries(queryClient, listPrefixes),
|
|
132
125
|
]);
|
|
133
126
|
|
|
134
127
|
const previousThreadMessages = queryClient
|
|
@@ -139,18 +132,10 @@ export const MessageActionMenu = ({
|
|
|
139
132
|
)
|
|
140
133
|
.map(([queryKey, data]) => ({ queryKey, data }));
|
|
141
134
|
|
|
142
|
-
const previousThreadsList =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
queryKey: threadsSearchPrefix,
|
|
147
|
-
}),
|
|
148
|
-
)
|
|
149
|
-
.filter(
|
|
150
|
-
(entry): entry is [readonly unknown[], ThreadsListData] =>
|
|
151
|
-
entry[1] !== undefined,
|
|
152
|
-
)
|
|
153
|
-
.map(([queryKey, data]) => ({ queryKey, data }));
|
|
135
|
+
const previousThreadsList = snapshotThreadListQueries(
|
|
136
|
+
queryClient,
|
|
137
|
+
listPrefixes,
|
|
138
|
+
);
|
|
154
139
|
|
|
155
140
|
queryClient.setQueriesData<ThreadMessagesData>(
|
|
156
141
|
{ queryKey: threadMessagesPrefix },
|
|
@@ -158,37 +143,18 @@ export const MessageActionMenu = ({
|
|
|
158
143
|
if (!old) return old;
|
|
159
144
|
return {
|
|
160
145
|
...old,
|
|
161
|
-
items: old.items
|
|
162
|
-
targetIds.has(item.messageId)
|
|
163
|
-
? { ...item, isRead: isReadNext }
|
|
164
|
-
: item,
|
|
165
|
-
),
|
|
146
|
+
items: setReadOnItems(old.items, targetIds, isReadNext),
|
|
166
147
|
};
|
|
167
148
|
},
|
|
168
149
|
);
|
|
169
150
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
items.map((item) =>
|
|
173
|
-
targetIds.has(item.messageId)
|
|
174
|
-
? { ...item, isRead: isReadNext }
|
|
175
|
-
: item,
|
|
176
|
-
),
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
queryClient.setQueriesData(
|
|
180
|
-
{ queryKey: threadsListPrefix },
|
|
181
|
-
patchListData,
|
|
182
|
-
);
|
|
183
|
-
queryClient.setQueriesData(
|
|
184
|
-
{ queryKey: threadsSearchPrefix },
|
|
185
|
-
patchListData,
|
|
151
|
+
patchThreadListQueries(queryClient, listPrefixes, (items) =>
|
|
152
|
+
setReadOnItems(items, targetIds, isReadNext),
|
|
186
153
|
);
|
|
187
154
|
|
|
188
155
|
return {
|
|
189
156
|
threadMessagesPrefix,
|
|
190
|
-
|
|
191
|
-
threadsSearchPrefix,
|
|
157
|
+
listPrefixes,
|
|
192
158
|
previousThreadMessages,
|
|
193
159
|
previousThreadsList,
|
|
194
160
|
};
|
|
@@ -198,9 +164,7 @@ export const MessageActionMenu = ({
|
|
|
198
164
|
for (const entry of context.previousThreadMessages) {
|
|
199
165
|
queryClient.setQueryData(entry.queryKey, entry.data);
|
|
200
166
|
}
|
|
201
|
-
|
|
202
|
-
queryClient.setQueryData(entry.queryKey, entry.data);
|
|
203
|
-
}
|
|
167
|
+
restoreThreadListQueries(queryClient, context.previousThreadsList);
|
|
204
168
|
}
|
|
205
169
|
const isReadNext = variables.body.isRead ?? true;
|
|
206
170
|
pushError({
|
|
@@ -214,10 +178,7 @@ export const MessageActionMenu = ({
|
|
|
214
178
|
queryClient.invalidateQueries({
|
|
215
179
|
queryKey: context.threadMessagesPrefix,
|
|
216
180
|
});
|
|
217
|
-
queryClient
|
|
218
|
-
queryClient.invalidateQueries({
|
|
219
|
-
queryKey: context.threadsSearchPrefix,
|
|
220
|
-
});
|
|
181
|
+
invalidateThreadListQueries(queryClient, context.listPrefixes);
|
|
221
182
|
},
|
|
222
183
|
});
|
|
223
184
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
messageBulkOperationsUpdateFlagsMutation,
|
|
3
3
|
threadDetailOperationsListThreadMessagesQueryKey,
|
|
4
|
-
threadOperationsListThreadsQueryKey,
|
|
5
|
-
threadOperationsSearchThreadsQueryKey,
|
|
6
4
|
} from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
7
5
|
import { MobileMessageActionBar, type PopoverMenuItem } from "@remit/ui";
|
|
8
6
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
@@ -11,6 +9,10 @@ import { useCallback } from "react";
|
|
|
11
9
|
import { useDeleteMessages } from "@/hooks/useDeleteMessages";
|
|
12
10
|
import { useMoveMessages } from "@/hooks/useMoveMessages";
|
|
13
11
|
import { useToggleTrusted } from "@/hooks/useToggleTrusted";
|
|
12
|
+
import {
|
|
13
|
+
invalidateThreadListQueries,
|
|
14
|
+
threadListCacheKeys,
|
|
15
|
+
} from "@/lib/thread-list-cache";
|
|
14
16
|
import { MoveToTrigger } from "./MoveToTrigger";
|
|
15
17
|
|
|
16
18
|
interface MobileMessageBarProps {
|
|
@@ -67,14 +69,10 @@ export const MobileMessageBar = ({
|
|
|
67
69
|
path: { threadId },
|
|
68
70
|
}),
|
|
69
71
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
queryKey: threadOperationsSearchThreadsQueryKey({
|
|
75
|
-
path: { mailboxId },
|
|
76
|
-
}),
|
|
77
|
-
});
|
|
72
|
+
invalidateThreadListQueries(
|
|
73
|
+
queryClient,
|
|
74
|
+
threadListCacheKeys([mailboxId]),
|
|
75
|
+
);
|
|
78
76
|
},
|
|
79
77
|
});
|
|
80
78
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { unifiedThreadOperationsListAllThreadsQueryKey } from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
4
|
+
import { threadListCacheKeys } from "@/lib/thread-list-cache";
|
|
5
|
+
import { mailboxesTouchedBy } from "./useEscalatedActions.js";
|
|
6
|
+
|
|
7
|
+
describe("mailboxesTouchedBy", () => {
|
|
8
|
+
test("a delete or mark-read run touches only the mailbox it ran over", () => {
|
|
9
|
+
assert.deepStrictEqual(mailboxesTouchedBy({ kind: "delete" }, "mb1"), [
|
|
10
|
+
"mb1",
|
|
11
|
+
]);
|
|
12
|
+
assert.deepStrictEqual(mailboxesTouchedBy({ kind: "markRead" }, "mb1"), [
|
|
13
|
+
"mb1",
|
|
14
|
+
]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("a move run also touches the destination", () => {
|
|
18
|
+
assert.deepStrictEqual(
|
|
19
|
+
mailboxesTouchedBy({ kind: "move", destinationMailboxId: "mb2" }, "mb1"),
|
|
20
|
+
["mb1", "mb2"],
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("its cache keys reach the unified listing the daily brief reads", () => {
|
|
25
|
+
const keys = threadListCacheKeys(
|
|
26
|
+
mailboxesTouchedBy({ kind: "delete" }, "mb1"),
|
|
27
|
+
);
|
|
28
|
+
assert.ok(
|
|
29
|
+
keys.some(
|
|
30
|
+
(key) =>
|
|
31
|
+
JSON.stringify(key) ===
|
|
32
|
+
JSON.stringify(unifiedThreadOperationsListAllThreadsQueryKey()),
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mailboxOperationsListMailboxesQueryKey,
|
|
3
|
-
threadOperationsListThreadsQueryKey,
|
|
4
|
-
threadOperationsSearchThreadsQueryKey,
|
|
5
|
-
} from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
1
|
+
import { mailboxOperationsListMailboxesQueryKey } from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
6
2
|
import {
|
|
7
3
|
messageBulkOperationsDeleteMessages,
|
|
8
4
|
messageBulkOperationsMoveMessages,
|
|
@@ -28,6 +24,10 @@ import {
|
|
|
28
24
|
runChunkedAction,
|
|
29
25
|
runPredicateAction,
|
|
30
26
|
} from "@/lib/bulk-actions";
|
|
27
|
+
import {
|
|
28
|
+
invalidateThreadListQueries,
|
|
29
|
+
threadListCacheKeys,
|
|
30
|
+
} from "@/lib/thread-list-cache";
|
|
31
31
|
|
|
32
32
|
/** The predicate a search-scoped run re-issues on every page — the same
|
|
33
33
|
* filters the visible list is searching with, minus pagination/count knobs. */
|
|
@@ -102,6 +102,18 @@ export interface UseEscalatedActionsResult {
|
|
|
102
102
|
) => Promise<BulkRunOutcome>;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* The mailboxes whose cached listings a bulk run affects: the mailbox it ran
|
|
107
|
+
* over, plus a move's destination, which gains the messages the source loses.
|
|
108
|
+
*/
|
|
109
|
+
export const mailboxesTouchedBy = (
|
|
110
|
+
action: EscalatedAction,
|
|
111
|
+
mailboxId: string,
|
|
112
|
+
): string[] =>
|
|
113
|
+
action.kind === "move"
|
|
114
|
+
? [mailboxId, action.destinationMailboxId]
|
|
115
|
+
: [mailboxId];
|
|
116
|
+
|
|
105
117
|
export const useEscalatedActions = ({
|
|
106
118
|
mailboxId,
|
|
107
119
|
accountId,
|
|
@@ -182,21 +194,10 @@ export const useEscalatedActions = ({
|
|
|
182
194
|
|
|
183
195
|
const invalidateAfterRun = useCallback(
|
|
184
196
|
(action: EscalatedAction) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
queryKey: threadOperationsSearchThreadsQueryKey({
|
|
190
|
-
path: { mailboxId },
|
|
191
|
-
}),
|
|
192
|
-
});
|
|
193
|
-
if (action.kind === "move") {
|
|
194
|
-
queryClient.invalidateQueries({
|
|
195
|
-
queryKey: threadOperationsListThreadsQueryKey({
|
|
196
|
-
path: { mailboxId: action.destinationMailboxId },
|
|
197
|
-
}),
|
|
198
|
-
});
|
|
199
|
-
}
|
|
197
|
+
invalidateThreadListQueries(
|
|
198
|
+
queryClient,
|
|
199
|
+
threadListCacheKeys(mailboxesTouchedBy(action, mailboxId)),
|
|
200
|
+
);
|
|
200
201
|
if (accountId) {
|
|
201
202
|
queryClient.invalidateQueries({
|
|
202
203
|
queryKey: mailboxOperationsListMailboxesQueryKey({
|
|
@@ -80,10 +80,14 @@ export const useMoveMessages = ({
|
|
|
80
80
|
path: { threadId },
|
|
81
81
|
})
|
|
82
82
|
: [];
|
|
83
|
-
// The source
|
|
84
|
-
// backs the daily brief — moving from the
|
|
85
|
-
// there too, not only from the per-mailbox
|
|
86
|
-
|
|
83
|
+
// The source and destination mailboxes' lists plus the unified
|
|
84
|
+
// cross-account listing that backs the daily brief — moving from the
|
|
85
|
+
// brief has to remove the row there too, not only from the per-mailbox
|
|
86
|
+
// lists (#140, part of #149).
|
|
87
|
+
const listPrefixes = threadListCacheKeys([
|
|
88
|
+
mailboxId,
|
|
89
|
+
variables.body.destinationMailboxId,
|
|
90
|
+
]);
|
|
87
91
|
|
|
88
92
|
// Only cancel the thread-messages query when we actually have a
|
|
89
93
|
// threadId — passing an empty queryKey here would match every
|
|
@@ -5,7 +5,15 @@ import {
|
|
|
5
5
|
threadOperationsSearchThreadsQueryKey,
|
|
6
6
|
unifiedThreadOperationsListAllThreadsQueryKey,
|
|
7
7
|
} from "@remit/api-http-client/@tanstack/react-query.gen.ts";
|
|
8
|
-
import {
|
|
8
|
+
import type { RemitImapThreadMessageResponse } from "@remit/api-http-client/types.gen.ts";
|
|
9
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
10
|
+
import {
|
|
11
|
+
invalidateThreadListQueries,
|
|
12
|
+
patchThreadListQueries,
|
|
13
|
+
restoreThreadListQueries,
|
|
14
|
+
snapshotThreadListQueries,
|
|
15
|
+
threadListCacheKeys,
|
|
16
|
+
} from "./thread-list-cache.js";
|
|
9
17
|
|
|
10
18
|
describe("threadListCacheKeys", () => {
|
|
11
19
|
test("resolves each mailbox's list and search caches plus the unified one", () => {
|
|
@@ -42,3 +50,98 @@ describe("threadListCacheKeys", () => {
|
|
|
42
50
|
assert.strictEqual(keys.length, 3);
|
|
43
51
|
});
|
|
44
52
|
});
|
|
53
|
+
|
|
54
|
+
const thread = (
|
|
55
|
+
overrides: Partial<RemitImapThreadMessageResponse> & { messageId: string },
|
|
56
|
+
): RemitImapThreadMessageResponse =>
|
|
57
|
+
({
|
|
58
|
+
threadId: "t1",
|
|
59
|
+
threadMessageId: `tm-${overrides.messageId}`,
|
|
60
|
+
mailboxId: "mb1",
|
|
61
|
+
subject: "s",
|
|
62
|
+
fromName: "n",
|
|
63
|
+
fromEmail: "e",
|
|
64
|
+
sentDate: "2025-01-01T00:00:00Z",
|
|
65
|
+
snippet: "",
|
|
66
|
+
hasAttachment: false,
|
|
67
|
+
hasStars: false,
|
|
68
|
+
isRead: false,
|
|
69
|
+
...overrides,
|
|
70
|
+
}) as RemitImapThreadMessageResponse;
|
|
71
|
+
|
|
72
|
+
/** The single-shot page the daily brief and Flagged cache under the unified key. */
|
|
73
|
+
const briefKey = unifiedThreadOperationsListAllThreadsQueryKey();
|
|
74
|
+
|
|
75
|
+
/** The brief's search variant caches under the same key plus query options. */
|
|
76
|
+
const briefSearchKey = unifiedThreadOperationsListAllThreadsQueryKey({
|
|
77
|
+
query: { query: "invoice", limit: 50 },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/** The infinite shape a mailbox list caches. */
|
|
81
|
+
const mailboxKey = threadOperationsListThreadsQueryKey({
|
|
82
|
+
path: { mailboxId: "mb1" },
|
|
83
|
+
query: { order: "desc" },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const seed = () => {
|
|
87
|
+
const queryClient = new QueryClient();
|
|
88
|
+
queryClient.setQueryData(briefKey, { items: [thread({ messageId: "m1" })] });
|
|
89
|
+
queryClient.setQueryData(briefSearchKey, {
|
|
90
|
+
items: [thread({ messageId: "m1" })],
|
|
91
|
+
});
|
|
92
|
+
queryClient.setQueryData(mailboxKey, {
|
|
93
|
+
pages: [{ items: [thread({ messageId: "m1" })] }],
|
|
94
|
+
pageParams: [undefined],
|
|
95
|
+
});
|
|
96
|
+
return queryClient;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const briefItems = (queryClient: QueryClient, key: readonly unknown[]) =>
|
|
100
|
+
queryClient.getQueryData<{ items: RemitImapThreadMessageResponse[] }>(key)
|
|
101
|
+
?.items ?? [];
|
|
102
|
+
|
|
103
|
+
describe("mutating from the daily brief", () => {
|
|
104
|
+
test("patches the unified listing the brief reads, not only the mailbox list", () => {
|
|
105
|
+
const queryClient = seed();
|
|
106
|
+
patchThreadListQueries(queryClient, threadListCacheKeys(["mb1"]), (items) =>
|
|
107
|
+
items.map((item) => ({ ...item, isRead: true })),
|
|
108
|
+
);
|
|
109
|
+
assert.strictEqual(briefItems(queryClient, briefKey)[0].isRead, true);
|
|
110
|
+
assert.strictEqual(briefItems(queryClient, briefSearchKey)[0].isRead, true);
|
|
111
|
+
const pages = queryClient.getQueryData<{
|
|
112
|
+
pages: Array<{ items: RemitImapThreadMessageResponse[] }>;
|
|
113
|
+
}>(mailboxKey);
|
|
114
|
+
assert.strictEqual(pages?.pages[0].items[0].isRead, true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("removes a deleted row from the brief's page", () => {
|
|
118
|
+
const queryClient = seed();
|
|
119
|
+
patchThreadListQueries(queryClient, threadListCacheKeys(["mb1"]), (items) =>
|
|
120
|
+
items.filter((item) => item.messageId !== "m1"),
|
|
121
|
+
);
|
|
122
|
+
assert.deepStrictEqual(briefItems(queryClient, briefKey), []);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("restores the brief's page when the mutation fails", () => {
|
|
126
|
+
const queryClient = seed();
|
|
127
|
+
const prefixes = threadListCacheKeys(["mb1"]);
|
|
128
|
+
const snapshot = snapshotThreadListQueries(queryClient, prefixes);
|
|
129
|
+
patchThreadListQueries(queryClient, prefixes, () => []);
|
|
130
|
+
restoreThreadListQueries(queryClient, snapshot);
|
|
131
|
+
assert.strictEqual(briefItems(queryClient, briefKey).length, 1);
|
|
132
|
+
assert.strictEqual(briefItems(queryClient, briefSearchKey).length, 1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("invalidates the brief's listing so the next read refetches", () => {
|
|
136
|
+
const queryClient = seed();
|
|
137
|
+
invalidateThreadListQueries(queryClient, threadListCacheKeys(["mb1"]));
|
|
138
|
+
assert.strictEqual(
|
|
139
|
+
queryClient.getQueryState(briefKey)?.isInvalidated,
|
|
140
|
+
true,
|
|
141
|
+
);
|
|
142
|
+
assert.strictEqual(
|
|
143
|
+
queryClient.getQueryState(mailboxKey)?.isInvalidated,
|
|
144
|
+
true,
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
});
|