@remit/web-client 0.0.153 → 0.0.155

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.
@@ -31,10 +31,9 @@ export type SearchMirrorTarget =
31
31
  * When a query *goes* active it also closes the reading pane (#539): an open
32
32
  * message from the pre-search list is not meaningful in the search result set.
33
33
  * The close is a navigation to the list route, which unmatches the thread that
34
- * was open under it plus, until the remaining lists move their selection into
35
- * the path, dropping the params they still read it from. Any other write keeps
36
- * the address it found: mirroring a query the reader is editing, or clearing
37
- * one, must not shut the conversation they are reading.
34
+ * was open under it. Any other write keeps the address it found: mirroring a
35
+ * query the reader is editing, or clearing one, must not shut the conversation
36
+ * they are reading.
38
37
  *
39
38
  * Only on that transition, though — tapping a search result commits the same `q`
40
39
  * with the open thread, so when the URL already says the query the conversation
@@ -71,13 +70,6 @@ export function useSearchMirror(target: SearchMirrorTarget): void {
71
70
  const search = (prev: Record<string, unknown>) => ({
72
71
  ...prev,
73
72
  q: committedQuery || undefined,
74
- ...(queryGoesActive
75
- ? {
76
- selectedMessageId: undefined,
77
- selectedThreadId: undefined,
78
- selectedMailboxId: undefined,
79
- }
80
- : {}),
81
73
  });
82
74
  if (!queryGoesActive) {
83
75
  navigate({ to: ".", search, replace: true });
@@ -16,13 +16,12 @@ const listSearch = z.object({ q: z.string().optional() });
16
16
  * The brief's open thread and the message inside it are path segments
17
17
  * (`/mail/brief/<thread>/<message>`), so the query carries nothing but the
18
18
  * search. An old link's selection params are dropped here, which is what
19
- * "tolerated and ignored" means until the remaining lists follow.
19
+ * "tolerated and ignored" means.
20
20
  */
21
21
  export const briefSearchSchema = listSearch.extend({});
22
22
 
23
- export const flaggedSearchSchema = listSearch.extend({
24
- selectedMessageId: z.string().optional(),
25
- });
23
+ /** The same, for `/mail/flagged/<thread>/<message>`. */
24
+ export const flaggedSearchSchema = listSearch.extend({});
26
25
 
27
26
  /**
28
27
  * The outbox's open message is a path segment
@@ -32,11 +32,14 @@ import { Route as MailMailboxIdThreadIdRouteImport } from './routes/mail/$mailbo
32
32
  import { Route as MailBriefIndexRouteImport } from './routes/mail/brief/index'
33
33
  import { Route as MailBriefThreadIdRouteImport } from './routes/mail/brief/$threadId'
34
34
  import { Route as MailFlaggedIndexRouteImport } from './routes/mail/flagged/index'
35
+ import { Route as MailFlaggedThreadIdRouteImport } from './routes/mail/flagged/$threadId'
35
36
  import { Route as MailOutboxIndexRouteImport } from './routes/mail/outbox/index'
36
37
  import { Route as MailMailboxIdThreadIdIndexRouteImport } from './routes/mail/$mailboxId/$threadId/index'
37
38
  import { Route as MailMailboxIdThreadIdMessageIdRouteImport } from './routes/mail/$mailboxId/$threadId/$messageId'
38
39
  import { Route as MailBriefThreadIdIndexRouteImport } from './routes/mail/brief/$threadId/index'
39
40
  import { Route as MailBriefThreadIdMessageIdRouteImport } from './routes/mail/brief/$threadId/$messageId'
41
+ import { Route as MailFlaggedThreadIdIndexRouteImport } from './routes/mail/flagged/$threadId/index'
42
+ import { Route as MailFlaggedThreadIdMessageIdRouteImport } from './routes/mail/flagged/$threadId/$messageId'
40
43
  import { Route as MailOutboxDraftOutboxMessageIdRouteImport } from './routes/mail/outbox/draft/$outboxMessageId'
41
44
 
42
45
  const IndexRoute = IndexRouteImport.update({
@@ -154,6 +157,11 @@ const MailFlaggedIndexRoute = MailFlaggedIndexRouteImport.update({
154
157
  path: '/',
155
158
  getParentRoute: () => MailFlaggedRoute,
156
159
  } as any)
160
+ const MailFlaggedThreadIdRoute = MailFlaggedThreadIdRouteImport.update({
161
+ id: '/$threadId',
162
+ path: '/$threadId',
163
+ getParentRoute: () => MailFlaggedRoute,
164
+ } as any)
157
165
  const MailOutboxIndexRoute = MailOutboxIndexRouteImport.update({
158
166
  id: '/',
159
167
  path: '/',
@@ -182,6 +190,18 @@ const MailBriefThreadIdMessageIdRoute =
182
190
  path: '/$messageId',
183
191
  getParentRoute: () => MailBriefThreadIdRoute,
184
192
  } as any)
193
+ const MailFlaggedThreadIdIndexRoute =
194
+ MailFlaggedThreadIdIndexRouteImport.update({
195
+ id: '/',
196
+ path: '/',
197
+ getParentRoute: () => MailFlaggedThreadIdRoute,
198
+ } as any)
199
+ const MailFlaggedThreadIdMessageIdRoute =
200
+ MailFlaggedThreadIdMessageIdRouteImport.update({
201
+ id: '/$messageId',
202
+ path: '/$messageId',
203
+ getParentRoute: () => MailFlaggedThreadIdRoute,
204
+ } as any)
185
205
  const MailOutboxDraftOutboxMessageIdRoute =
186
206
  MailOutboxDraftOutboxMessageIdRouteImport.update({
187
207
  id: '/draft/$outboxMessageId',
@@ -210,15 +230,18 @@ export interface FileRoutesByFullPath {
210
230
  '/settings/': typeof SettingsIndexRoute
211
231
  '/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdRouteWithChildren
212
232
  '/mail/brief/$threadId': typeof MailBriefThreadIdRouteWithChildren
233
+ '/mail/flagged/$threadId': typeof MailFlaggedThreadIdRouteWithChildren
213
234
  '/mail/$mailboxId/': typeof MailMailboxIdIndexRoute
214
235
  '/mail/brief/': typeof MailBriefIndexRoute
215
236
  '/mail/flagged/': typeof MailFlaggedIndexRoute
216
237
  '/mail/outbox/': typeof MailOutboxIndexRoute
217
238
  '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
218
239
  '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
240
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
219
241
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
220
242
  '/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
221
243
  '/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
244
+ '/mail/flagged/$threadId/': typeof MailFlaggedThreadIdIndexRoute
222
245
  }
223
246
  export interface FileRoutesByTo {
224
247
  '/': typeof IndexRoute
@@ -239,9 +262,11 @@ export interface FileRoutesByTo {
239
262
  '/mail/outbox': typeof MailOutboxIndexRoute
240
263
  '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
241
264
  '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
265
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
242
266
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
243
267
  '/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdIndexRoute
244
268
  '/mail/brief/$threadId': typeof MailBriefThreadIdIndexRoute
269
+ '/mail/flagged/$threadId': typeof MailFlaggedThreadIdIndexRoute
245
270
  }
246
271
  export interface FileRoutesById {
247
272
  __root__: typeof rootRouteImport
@@ -265,15 +290,18 @@ export interface FileRoutesById {
265
290
  '/settings/': typeof SettingsIndexRoute
266
291
  '/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdRouteWithChildren
267
292
  '/mail/brief/$threadId': typeof MailBriefThreadIdRouteWithChildren
293
+ '/mail/flagged/$threadId': typeof MailFlaggedThreadIdRouteWithChildren
268
294
  '/mail/$mailboxId/': typeof MailMailboxIdIndexRoute
269
295
  '/mail/brief/': typeof MailBriefIndexRoute
270
296
  '/mail/flagged/': typeof MailFlaggedIndexRoute
271
297
  '/mail/outbox/': typeof MailOutboxIndexRoute
272
298
  '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
273
299
  '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
300
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
274
301
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
275
302
  '/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
276
303
  '/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
304
+ '/mail/flagged/$threadId/': typeof MailFlaggedThreadIdIndexRoute
277
305
  }
278
306
  export interface FileRouteTypes {
279
307
  fileRoutesByFullPath: FileRoutesByFullPath
@@ -298,15 +326,18 @@ export interface FileRouteTypes {
298
326
  | '/settings/'
299
327
  | '/mail/$mailboxId/$threadId'
300
328
  | '/mail/brief/$threadId'
329
+ | '/mail/flagged/$threadId'
301
330
  | '/mail/$mailboxId/'
302
331
  | '/mail/brief/'
303
332
  | '/mail/flagged/'
304
333
  | '/mail/outbox/'
305
334
  | '/mail/$mailboxId/$threadId/$messageId'
306
335
  | '/mail/brief/$threadId/$messageId'
336
+ | '/mail/flagged/$threadId/$messageId'
307
337
  | '/mail/outbox/draft/$outboxMessageId'
308
338
  | '/mail/$mailboxId/$threadId/'
309
339
  | '/mail/brief/$threadId/'
340
+ | '/mail/flagged/$threadId/'
310
341
  fileRoutesByTo: FileRoutesByTo
311
342
  to:
312
343
  | '/'
@@ -327,9 +358,11 @@ export interface FileRouteTypes {
327
358
  | '/mail/outbox'
328
359
  | '/mail/$mailboxId/$threadId/$messageId'
329
360
  | '/mail/brief/$threadId/$messageId'
361
+ | '/mail/flagged/$threadId/$messageId'
330
362
  | '/mail/outbox/draft/$outboxMessageId'
331
363
  | '/mail/$mailboxId/$threadId'
332
364
  | '/mail/brief/$threadId'
365
+ | '/mail/flagged/$threadId'
333
366
  id:
334
367
  | '__root__'
335
368
  | '/'
@@ -352,15 +385,18 @@ export interface FileRouteTypes {
352
385
  | '/settings/'
353
386
  | '/mail/$mailboxId/$threadId'
354
387
  | '/mail/brief/$threadId'
388
+ | '/mail/flagged/$threadId'
355
389
  | '/mail/$mailboxId/'
356
390
  | '/mail/brief/'
357
391
  | '/mail/flagged/'
358
392
  | '/mail/outbox/'
359
393
  | '/mail/$mailboxId/$threadId/$messageId'
360
394
  | '/mail/brief/$threadId/$messageId'
395
+ | '/mail/flagged/$threadId/$messageId'
361
396
  | '/mail/outbox/draft/$outboxMessageId'
362
397
  | '/mail/$mailboxId/$threadId/'
363
398
  | '/mail/brief/$threadId/'
399
+ | '/mail/flagged/$threadId/'
364
400
  fileRoutesById: FileRoutesById
365
401
  }
366
402
  export interface RootRouteChildren {
@@ -533,6 +569,13 @@ declare module '@tanstack/react-router' {
533
569
  preLoaderRoute: typeof MailFlaggedIndexRouteImport
534
570
  parentRoute: typeof MailFlaggedRoute
535
571
  }
572
+ '/mail/flagged/$threadId': {
573
+ id: '/mail/flagged/$threadId'
574
+ path: '/$threadId'
575
+ fullPath: '/mail/flagged/$threadId'
576
+ preLoaderRoute: typeof MailFlaggedThreadIdRouteImport
577
+ parentRoute: typeof MailFlaggedRoute
578
+ }
536
579
  '/mail/outbox/': {
537
580
  id: '/mail/outbox/'
538
581
  path: '/'
@@ -568,6 +611,20 @@ declare module '@tanstack/react-router' {
568
611
  preLoaderRoute: typeof MailBriefThreadIdMessageIdRouteImport
569
612
  parentRoute: typeof MailBriefThreadIdRoute
570
613
  }
614
+ '/mail/flagged/$threadId/': {
615
+ id: '/mail/flagged/$threadId/'
616
+ path: '/'
617
+ fullPath: '/mail/flagged/$threadId/'
618
+ preLoaderRoute: typeof MailFlaggedThreadIdIndexRouteImport
619
+ parentRoute: typeof MailFlaggedThreadIdRoute
620
+ }
621
+ '/mail/flagged/$threadId/$messageId': {
622
+ id: '/mail/flagged/$threadId/$messageId'
623
+ path: '/$messageId'
624
+ fullPath: '/mail/flagged/$threadId/$messageId'
625
+ preLoaderRoute: typeof MailFlaggedThreadIdMessageIdRouteImport
626
+ parentRoute: typeof MailFlaggedThreadIdRoute
627
+ }
571
628
  '/mail/outbox/draft/$outboxMessageId': {
572
629
  id: '/mail/outbox/draft/$outboxMessageId'
573
630
  path: '/draft/$outboxMessageId'
@@ -634,11 +691,26 @@ const MailBriefRouteWithChildren = MailBriefRoute._addFileChildren(
634
691
  MailBriefRouteChildren,
635
692
  )
636
693
 
694
+ interface MailFlaggedThreadIdRouteChildren {
695
+ MailFlaggedThreadIdMessageIdRoute: typeof MailFlaggedThreadIdMessageIdRoute
696
+ MailFlaggedThreadIdIndexRoute: typeof MailFlaggedThreadIdIndexRoute
697
+ }
698
+
699
+ const MailFlaggedThreadIdRouteChildren: MailFlaggedThreadIdRouteChildren = {
700
+ MailFlaggedThreadIdMessageIdRoute: MailFlaggedThreadIdMessageIdRoute,
701
+ MailFlaggedThreadIdIndexRoute: MailFlaggedThreadIdIndexRoute,
702
+ }
703
+
704
+ const MailFlaggedThreadIdRouteWithChildren =
705
+ MailFlaggedThreadIdRoute._addFileChildren(MailFlaggedThreadIdRouteChildren)
706
+
637
707
  interface MailFlaggedRouteChildren {
708
+ MailFlaggedThreadIdRoute: typeof MailFlaggedThreadIdRouteWithChildren
638
709
  MailFlaggedIndexRoute: typeof MailFlaggedIndexRoute
639
710
  }
640
711
 
641
712
  const MailFlaggedRouteChildren: MailFlaggedRouteChildren = {
713
+ MailFlaggedThreadIdRoute: MailFlaggedThreadIdRouteWithChildren,
642
714
  MailFlaggedIndexRoute: MailFlaggedIndexRoute,
643
715
  }
644
716
 
@@ -0,0 +1,19 @@
1
+ /**
2
+ * /mail/flagged/$threadId/$messageId — the same conversation, with one of its
3
+ * messages expanded and scrolled to.
4
+ *
5
+ * A message is not addressable on its own: `GET /messages/{messageId}` answers
6
+ * with no thread, so the pane has nothing to fetch by. The segment names which
7
+ * message inside the thread the reader pointed at, and the surface it renders is
8
+ * the thread's.
9
+ */
10
+ import { createFileRoute } from "@tanstack/react-router";
11
+ import { FlaggedPane } from "@/components/mail/FlaggedPane";
12
+
13
+ function FlaggedMessagePane() {
14
+ return <FlaggedPane.Reading />;
15
+ }
16
+
17
+ export const Route = createFileRoute("/mail/flagged/$threadId/$messageId")({
18
+ component: FlaggedMessagePane,
19
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The thread with no message named. The pane opens on the newest message, the
3
+ * same as for a thread whose row the reader never pointed at.
4
+ */
5
+ import { createFileRoute } from "@tanstack/react-router";
6
+ import { FlaggedPane } from "@/components/mail/FlaggedPane";
7
+
8
+ function FlaggedThreadPane() {
9
+ return <FlaggedPane.Reading />;
10
+ }
11
+
12
+ export const Route = createFileRoute("/mail/flagged/$threadId/")({
13
+ component: FlaggedThreadPane,
14
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * /mail/flagged/$threadId — a conversation open in the flagged list's reading
3
+ * pane.
4
+ *
5
+ * Starred mail spans accounts and folders, and the thread is still the whole
6
+ * address: the folder its mail is filed in comes from the thread's own data,
7
+ * since `GET /threads/{threadId}/messages` answers with rows that each name
8
+ * their mailbox.
9
+ *
10
+ * A layout, because the message segment nests under it.
11
+ */
12
+ import { createFileRoute, Outlet } from "@tanstack/react-router";
13
+
14
+ export const Route = createFileRoute("/mail/flagged/$threadId")({
15
+ component: Outlet,
16
+ });
@@ -2,7 +2,9 @@
2
2
  * /mail/flagged — the Flagged virtual mailbox, one of the four list layouts.
3
3
  *
4
4
  * A flat starred list across accounts; same slots as the brief, intelligence
5
- * rail included.
5
+ * rail included. The reading pane is the `Outlet`, so what it shows is whatever
6
+ * route matched below this one, and the open thread and the message inside it
7
+ * are the segments there rather than a selection read out of the query.
6
8
  */
7
9
  import {
8
10
  createFileRoute,
@@ -14,6 +16,7 @@ import { FlaggedPane } from "@/components/mail/FlaggedPane";
14
16
  import { ErrorState } from "@/components/ui/ErrorState";
15
17
  import { useSearchMirror } from "@/hooks/useSearchMirror";
16
18
  import { flaggedSearchSchema } from "@/lib/mail-search";
19
+ import { useOpenThreadPath } from "@/routing";
17
20
 
18
21
  const FlaggedError = ({ error, reset }: ErrorComponentProps) => (
19
22
  <div className="flex h-full items-center justify-center bg-canvas p-4">
@@ -26,17 +29,17 @@ const FlaggedError = ({ error, reset }: ErrorComponentProps) => (
26
29
  );
27
30
 
28
31
  function FlaggedLayout() {
29
- const { selectedMessageId } = Route.useSearch();
32
+ const thread = useOpenThreadPath();
30
33
  useSearchMirror({ to: "/mail/flagged" });
31
34
 
32
35
  return (
33
- <FlaggedPane selectedMessageId={selectedMessageId}>
36
+ <FlaggedPane thread={thread}>
34
37
  <MailShell
35
38
  phone={<FlaggedPane.Phone />}
36
39
  list={<FlaggedPane.List />}
37
40
  reading={<Outlet />}
38
41
  intelligence={<FlaggedPane.Intelligence />}
39
- hasThread={Boolean(selectedMessageId)}
42
+ hasThread={Boolean(thread)}
40
43
  />
41
44
  </FlaggedPane>
42
45
  );
@@ -39,6 +39,14 @@ export function useOpenThreadPath(): OpenThreadPath | undefined {
39
39
  from: "/mail/brief/$threadId/$messageId",
40
40
  shouldThrow: false,
41
41
  });
42
+ const flaggedThread = useParams({
43
+ from: "/mail/flagged/$threadId",
44
+ shouldThrow: false,
45
+ });
46
+ const flaggedMessage = useParams({
47
+ from: "/mail/flagged/$threadId/$messageId",
48
+ shouldThrow: false,
49
+ });
42
50
  const mailboxThread = useParams({
43
51
  from: "/mail/$mailboxId/$threadId",
44
52
  shouldThrow: false,
@@ -48,8 +56,12 @@ export function useOpenThreadPath(): OpenThreadPath | undefined {
48
56
  shouldThrow: false,
49
57
  });
50
58
 
51
- const threadId = briefThread?.threadId ?? mailboxThread?.threadId;
52
- const messageId = briefMessage?.messageId ?? mailboxMessage?.messageId;
59
+ const threadId =
60
+ briefThread?.threadId ?? flaggedThread?.threadId ?? mailboxThread?.threadId;
61
+ const messageId =
62
+ briefMessage?.messageId ??
63
+ flaggedMessage?.messageId ??
64
+ mailboxMessage?.messageId;
53
65
 
54
66
  return useMemo(
55
67
  () => (threadId ? { threadId, messageId } : undefined),