@remit/web-client 0.0.166 → 0.0.168

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.
Files changed (47) hide show
  1. package/package.json +1 -1
  2. package/src/components/compose/ComposeForm.tsx +91 -29
  3. package/src/components/compose/ComposeProvider.tsx +19 -124
  4. package/src/components/compose/ConversationCompose.tsx +57 -0
  5. package/src/components/compose/FullCompose.tsx +29 -18
  6. package/src/components/compose/MobileComposeSheet.tsx +14 -18
  7. package/src/components/compose/compose-send-stops-autosave.render.test.ts +7 -9
  8. package/src/components/compose/compose-starts-a-second-message.render.test.ts +170 -0
  9. package/src/components/compose/compose-title.ts +10 -0
  10. package/src/components/compose/mobile-header-stays-expanded.render.test.ts +5 -13
  11. package/src/components/layout/ComposeFab.tsx +6 -15
  12. package/src/components/layout/MailShell.tsx +9 -1
  13. package/src/components/layout/MailTopBar.tsx +3 -6
  14. package/src/components/mail/BriefPane.tsx +49 -13
  15. package/src/components/mail/ConversationView.tsx +82 -77
  16. package/src/components/mail/DraftsView.tsx +12 -14
  17. package/src/components/mail/FlaggedPane.tsx +49 -13
  18. package/src/components/mail/MailboxPane.tsx +77 -167
  19. package/src/components/mail/OutboxPane.tsx +3 -9
  20. package/src/components/mail/conversation-reply-reach.render.test.ts +81 -28
  21. package/src/hooks/useSaveDraft.ts +11 -0
  22. package/src/hooks/useSearchMirror.ts +12 -1
  23. package/src/hooks/useThreadActions.ts +8 -17
  24. package/src/lib/mail-route.test.ts +35 -1
  25. package/src/lib/mail-route.ts +2 -1
  26. package/src/routeTree.gen.ts +220 -15
  27. package/src/routes/mail/$mailboxId/$threadId/$messageId/$mode.{-$outboxMessageId}.tsx +14 -0
  28. package/src/routes/mail/$mailboxId/$threadId/$messageId.tsx +4 -0
  29. package/src/routes/mail/$mailboxId/compose.{-$outboxMessageId}.tsx +14 -0
  30. package/src/routes/mail/brief/$threadId/$messageId/$mode.{-$outboxMessageId}.tsx +26 -0
  31. package/src/routes/mail/brief/$threadId/$messageId.tsx +4 -0
  32. package/src/routes/mail/brief/compose.{-$outboxMessageId}.tsx +22 -0
  33. package/src/routes/mail/flagged/$threadId/$messageId/$mode.{-$outboxMessageId}.tsx +14 -0
  34. package/src/routes/mail/flagged/$threadId/$messageId.tsx +4 -0
  35. package/src/routes/mail/flagged/compose.{-$outboxMessageId}.tsx +13 -0
  36. package/src/routes/mail/outbox/compose.{-$outboxMessageId}.tsx +17 -0
  37. package/src/routes/mail.tsx +12 -8
  38. package/src/routing/browsed-list.ts +22 -0
  39. package/src/routing/compose-press-opens.render.test.ts +179 -0
  40. package/src/routing/compose.ts +205 -0
  41. package/src/routing/index.ts +20 -0
  42. package/src/routing/reply.ts +282 -0
  43. package/src/components/compose/InlineCompose.tsx +0 -37
  44. package/src/components/compose/compose-clears-open-thread.render.test.ts +0 -312
  45. package/src/hooks/useComposeTargetMailbox.ts +0 -75
  46. package/src/lib/compose-routes.test.ts +0 -46
  47. package/src/lib/compose-routes.ts +0 -25
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * useThreadActions — the reading pane's verbs for one open thread.
3
3
  *
4
- * Delete, move, star and the compose requests (reply / reply-all / forward),
5
- * over the same mutation hooks the mailbox list uses. The mailbox view keys
6
- * them by its route; the brief and Flagged are cross-account, so they key by
7
- * the open thread's own `mailboxId` / `accountId` (#149).
4
+ * Delete, move and star, over the same mutation hooks the mailbox list uses.
5
+ * The mailbox view keys them by its route; the brief and Flagged are
6
+ * cross-account, so they key by the open thread's own `mailboxId` /
7
+ * `accountId` (#149).
8
+ *
9
+ * Answering a message is not here: reply, reply-all and forward are a segment
10
+ * under the message, so they are a navigation rather than a verb a pane holds.
8
11
  */
9
12
  import type { RemitImapThreadMessageResponse } from "@remit/api-http-client/types.gen.ts";
10
- import { useCallback, useState } from "react";
11
- import type { ComposeMode } from "@/components/compose/ComposeProvider";
13
+ import { useCallback } from "react";
12
14
  import { useDeleteMessages } from "@/hooks/useDeleteMessages";
13
15
  import { useMailboxAccount } from "@/hooks/useMailboxAccount";
14
16
  import { useMoveMessages } from "@/hooks/useMoveMessages";
@@ -31,9 +33,6 @@ export interface ThreadActions {
31
33
  deleteThread: () => void;
32
34
  moveThread: (destinationMailboxId: string) => void;
33
35
  toggleStar: () => void;
34
- composeRequest: ComposeMode | null;
35
- requestCompose: (mode: ComposeMode) => void;
36
- clearComposeRequest: () => void;
37
36
  }
38
37
 
39
38
  export const useThreadActions = ({
@@ -91,11 +90,6 @@ export const useThreadActions = ({
91
90
  toggleStarFor(thread.messageId, thread.hasStars);
92
91
  }, [thread, toggleStarFor]);
93
92
 
94
- const [composeRequest, setComposeRequest] = useState<ComposeMode | null>(
95
- null,
96
- );
97
- const clearComposeRequest = useCallback(() => setComposeRequest(null), []);
98
-
99
93
  return {
100
94
  mailboxId: resolvedMailboxId,
101
95
  accountId: resolvedAccountId,
@@ -103,8 +97,5 @@ export const useThreadActions = ({
103
97
  deleteThread,
104
98
  moveThread,
105
99
  toggleStar,
106
- composeRequest,
107
- requestCompose: setComposeRequest,
108
- clearComposeRequest,
109
100
  };
110
101
  };
@@ -131,6 +131,34 @@ describe("mailViewKey", () => {
131
131
  }
132
132
  });
133
133
 
134
+ // Same trap one surface over: compose is a child of the list, and a key that
135
+ // moved when it opened would wipe the query the reader was mid-search on.
136
+ it("gives a list and its compose surface the same key", () => {
137
+ assert.equal(
138
+ mailViewKey(
139
+ matches([
140
+ MAIL_BRIEF_ROUTE_ID,
141
+ `${MAIL_BRIEF_ROUTE_ID}/compose/{-$outboxMessageId}`,
142
+ ]),
143
+ ),
144
+ mailViewKey(brief),
145
+ );
146
+ assert.equal(
147
+ mailViewKey(
148
+ matches(
149
+ [
150
+ MAIL_MAILBOX_ROUTE_ID,
151
+ `${MAIL_MAILBOX_ROUTE_ID}/compose/{-$outboxMessageId}`,
152
+ ],
153
+ {
154
+ mailboxId: "inbox-1",
155
+ },
156
+ ),
157
+ ),
158
+ mailViewKey(mailbox),
159
+ );
160
+ });
161
+
134
162
  it("gives a list and its reading-pane index child the same key", () => {
135
163
  assert.equal(
136
164
  mailViewKey(matches([MAIL_BRIEF_ROUTE_ID, `${MAIL_BRIEF_ROUTE_ID}/`])),
@@ -157,7 +185,7 @@ describe("locationIsOnList", () => {
157
185
  );
158
186
  assert.equal(locationIsOnList("/mail/brief/", "/mail/brief"), true);
159
187
  assert.equal(
160
- locationIsOnList("/mail/inbox-1/compose", "/mail/inbox-1"),
188
+ locationIsOnList("/mail/inbox-1/compose/ob-1", "/mail/inbox-1"),
161
189
  true,
162
190
  );
163
191
  });
@@ -191,6 +219,12 @@ describe("locationOpensDetail", () => {
191
219
  assert.equal(locationOpensDetail("/mail/inbox-1/thread-1"), true);
192
220
  });
193
221
 
222
+ it("is true for the compose surface, which the FAB must not sit over", () => {
223
+ assert.equal(locationOpensDetail("/mail/brief/compose"), true);
224
+ assert.equal(locationOpensDetail("/mail/inbox-1/compose"), true);
225
+ assert.equal(locationOpensDetail("/mail/inbox-1/compose/ob-1"), true);
226
+ });
227
+
194
228
  it("is false on a bare list, trailing slash and query included", () => {
195
229
  assert.equal(locationOpensDetail("/mail/brief"), false);
196
230
  assert.equal(locationOpensDetail("/mail/brief/"), false);
@@ -95,7 +95,8 @@ export function locationIsOnList(pathname: string, listPath: string): boolean {
95
95
 
96
96
  /**
97
97
  * Whether the address names something the list has open below it — a thread, a
98
- * message. A list on its own, and its bare reading pane, do not.
98
+ * message, the compose surface. A list on its own, and its bare reading pane,
99
+ * do not.
99
100
  *
100
101
  * Answers what an open thread used to be asked of the query. Anything sharing
101
102
  * the single pane with the conversation reads it from here rather than keeping a
@@ -36,11 +36,18 @@ import { Route as MailFlaggedThreadIdRouteImport } from './routes/mail/flagged/$
36
36
  import { Route as MailOutboxIndexRouteImport } from './routes/mail/outbox/index'
37
37
  import { Route as MailMailboxIdThreadIdIndexRouteImport } from './routes/mail/$mailboxId/$threadId/index'
38
38
  import { Route as MailMailboxIdThreadIdMessageIdRouteImport } from './routes/mail/$mailboxId/$threadId/$messageId'
39
+ import { Route as MailMailboxIdComposeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/$mailboxId/compose.{-$outboxMessageId}'
39
40
  import { Route as MailBriefThreadIdIndexRouteImport } from './routes/mail/brief/$threadId/index'
40
41
  import { Route as MailBriefThreadIdMessageIdRouteImport } from './routes/mail/brief/$threadId/$messageId'
42
+ import { Route as MailBriefComposeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/brief/compose.{-$outboxMessageId}'
41
43
  import { Route as MailFlaggedThreadIdIndexRouteImport } from './routes/mail/flagged/$threadId/index'
42
44
  import { Route as MailFlaggedThreadIdMessageIdRouteImport } from './routes/mail/flagged/$threadId/$messageId'
45
+ import { Route as MailFlaggedComposeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/flagged/compose.{-$outboxMessageId}'
46
+ import { Route as MailOutboxComposeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/outbox/compose.{-$outboxMessageId}'
43
47
  import { Route as MailOutboxDraftOutboxMessageIdRouteImport } from './routes/mail/outbox/draft/$outboxMessageId'
48
+ import { Route as MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/$mailboxId/$threadId/$messageId/$mode.{-$outboxMessageId}'
49
+ import { Route as MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/brief/$threadId/$messageId/$mode.{-$outboxMessageId}'
50
+ import { Route as MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport } from './routes/mail/flagged/$threadId/$messageId/$mode.{-$outboxMessageId}'
44
51
 
45
52
  const IndexRoute = IndexRouteImport.update({
46
53
  id: '/',
@@ -179,6 +186,12 @@ const MailMailboxIdThreadIdMessageIdRoute =
179
186
  path: '/$messageId',
180
187
  getParentRoute: () => MailMailboxIdThreadIdRoute,
181
188
  } as any)
189
+ const MailMailboxIdComposeChar123OutboxMessageIdChar125Route =
190
+ MailMailboxIdComposeChar123OutboxMessageIdChar125RouteImport.update({
191
+ id: '/compose/{-$outboxMessageId}',
192
+ path: '/compose/{-$outboxMessageId}',
193
+ getParentRoute: () => MailMailboxIdRoute,
194
+ } as any)
182
195
  const MailBriefThreadIdIndexRoute = MailBriefThreadIdIndexRouteImport.update({
183
196
  id: '/',
184
197
  path: '/',
@@ -190,6 +203,12 @@ const MailBriefThreadIdMessageIdRoute =
190
203
  path: '/$messageId',
191
204
  getParentRoute: () => MailBriefThreadIdRoute,
192
205
  } as any)
206
+ const MailBriefComposeChar123OutboxMessageIdChar125Route =
207
+ MailBriefComposeChar123OutboxMessageIdChar125RouteImport.update({
208
+ id: '/compose/{-$outboxMessageId}',
209
+ path: '/compose/{-$outboxMessageId}',
210
+ getParentRoute: () => MailBriefRoute,
211
+ } as any)
193
212
  const MailFlaggedThreadIdIndexRoute =
194
213
  MailFlaggedThreadIdIndexRouteImport.update({
195
214
  id: '/',
@@ -202,12 +221,48 @@ const MailFlaggedThreadIdMessageIdRoute =
202
221
  path: '/$messageId',
203
222
  getParentRoute: () => MailFlaggedThreadIdRoute,
204
223
  } as any)
224
+ const MailFlaggedComposeChar123OutboxMessageIdChar125Route =
225
+ MailFlaggedComposeChar123OutboxMessageIdChar125RouteImport.update({
226
+ id: '/compose/{-$outboxMessageId}',
227
+ path: '/compose/{-$outboxMessageId}',
228
+ getParentRoute: () => MailFlaggedRoute,
229
+ } as any)
230
+ const MailOutboxComposeChar123OutboxMessageIdChar125Route =
231
+ MailOutboxComposeChar123OutboxMessageIdChar125RouteImport.update({
232
+ id: '/compose/{-$outboxMessageId}',
233
+ path: '/compose/{-$outboxMessageId}',
234
+ getParentRoute: () => MailOutboxRoute,
235
+ } as any)
205
236
  const MailOutboxDraftOutboxMessageIdRoute =
206
237
  MailOutboxDraftOutboxMessageIdRouteImport.update({
207
238
  id: '/draft/$outboxMessageId',
208
239
  path: '/draft/$outboxMessageId',
209
240
  getParentRoute: () => MailOutboxRoute,
210
241
  } as any)
242
+ const MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route =
243
+ MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport.update(
244
+ {
245
+ id: '/$mode/{-$outboxMessageId}',
246
+ path: '/$mode/{-$outboxMessageId}',
247
+ getParentRoute: () => MailMailboxIdThreadIdMessageIdRoute,
248
+ } as any,
249
+ )
250
+ const MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route =
251
+ MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport.update(
252
+ {
253
+ id: '/$mode/{-$outboxMessageId}',
254
+ path: '/$mode/{-$outboxMessageId}',
255
+ getParentRoute: () => MailBriefThreadIdMessageIdRoute,
256
+ } as any,
257
+ )
258
+ const MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route =
259
+ MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport.update(
260
+ {
261
+ id: '/$mode/{-$outboxMessageId}',
262
+ path: '/$mode/{-$outboxMessageId}',
263
+ getParentRoute: () => MailFlaggedThreadIdMessageIdRoute,
264
+ } as any,
265
+ )
211
266
 
212
267
  export interface FileRoutesByFullPath {
213
268
  '/': typeof IndexRoute
@@ -235,13 +290,20 @@ export interface FileRoutesByFullPath {
235
290
  '/mail/brief/': typeof MailBriefIndexRoute
236
291
  '/mail/flagged/': typeof MailFlaggedIndexRoute
237
292
  '/mail/outbox/': typeof MailOutboxIndexRoute
238
- '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
239
- '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
240
- '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
293
+ '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRouteWithChildren
294
+ '/mail/$mailboxId/compose/{-$outboxMessageId}': typeof MailMailboxIdComposeChar123OutboxMessageIdChar125Route
295
+ '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRouteWithChildren
296
+ '/mail/brief/compose/{-$outboxMessageId}': typeof MailBriefComposeChar123OutboxMessageIdChar125Route
297
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRouteWithChildren
298
+ '/mail/flagged/compose/{-$outboxMessageId}': typeof MailFlaggedComposeChar123OutboxMessageIdChar125Route
299
+ '/mail/outbox/compose/{-$outboxMessageId}': typeof MailOutboxComposeChar123OutboxMessageIdChar125Route
241
300
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
242
301
  '/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
243
302
  '/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
244
303
  '/mail/flagged/$threadId/': typeof MailFlaggedThreadIdIndexRoute
304
+ '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
305
+ '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
306
+ '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
245
307
  }
246
308
  export interface FileRoutesByTo {
247
309
  '/': typeof IndexRoute
@@ -260,13 +322,20 @@ export interface FileRoutesByTo {
260
322
  '/mail/brief': typeof MailBriefIndexRoute
261
323
  '/mail/flagged': typeof MailFlaggedIndexRoute
262
324
  '/mail/outbox': typeof MailOutboxIndexRoute
263
- '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
264
- '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
265
- '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
325
+ '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRouteWithChildren
326
+ '/mail/$mailboxId/compose/{-$outboxMessageId}': typeof MailMailboxIdComposeChar123OutboxMessageIdChar125Route
327
+ '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRouteWithChildren
328
+ '/mail/brief/compose/{-$outboxMessageId}': typeof MailBriefComposeChar123OutboxMessageIdChar125Route
329
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRouteWithChildren
330
+ '/mail/flagged/compose/{-$outboxMessageId}': typeof MailFlaggedComposeChar123OutboxMessageIdChar125Route
331
+ '/mail/outbox/compose/{-$outboxMessageId}': typeof MailOutboxComposeChar123OutboxMessageIdChar125Route
266
332
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
267
333
  '/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdIndexRoute
268
334
  '/mail/brief/$threadId': typeof MailBriefThreadIdIndexRoute
269
335
  '/mail/flagged/$threadId': typeof MailFlaggedThreadIdIndexRoute
336
+ '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
337
+ '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
338
+ '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
270
339
  }
271
340
  export interface FileRoutesById {
272
341
  __root__: typeof rootRouteImport
@@ -295,13 +364,20 @@ export interface FileRoutesById {
295
364
  '/mail/brief/': typeof MailBriefIndexRoute
296
365
  '/mail/flagged/': typeof MailFlaggedIndexRoute
297
366
  '/mail/outbox/': typeof MailOutboxIndexRoute
298
- '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
299
- '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
300
- '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRoute
367
+ '/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRouteWithChildren
368
+ '/mail/$mailboxId/compose/{-$outboxMessageId}': typeof MailMailboxIdComposeChar123OutboxMessageIdChar125Route
369
+ '/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRouteWithChildren
370
+ '/mail/brief/compose/{-$outboxMessageId}': typeof MailBriefComposeChar123OutboxMessageIdChar125Route
371
+ '/mail/flagged/$threadId/$messageId': typeof MailFlaggedThreadIdMessageIdRouteWithChildren
372
+ '/mail/flagged/compose/{-$outboxMessageId}': typeof MailFlaggedComposeChar123OutboxMessageIdChar125Route
373
+ '/mail/outbox/compose/{-$outboxMessageId}': typeof MailOutboxComposeChar123OutboxMessageIdChar125Route
301
374
  '/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
302
375
  '/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
303
376
  '/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
304
377
  '/mail/flagged/$threadId/': typeof MailFlaggedThreadIdIndexRoute
378
+ '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
379
+ '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
380
+ '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}': typeof MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
305
381
  }
306
382
  export interface FileRouteTypes {
307
383
  fileRoutesByFullPath: FileRoutesByFullPath
@@ -332,12 +408,19 @@ export interface FileRouteTypes {
332
408
  | '/mail/flagged/'
333
409
  | '/mail/outbox/'
334
410
  | '/mail/$mailboxId/$threadId/$messageId'
411
+ | '/mail/$mailboxId/compose/{-$outboxMessageId}'
335
412
  | '/mail/brief/$threadId/$messageId'
413
+ | '/mail/brief/compose/{-$outboxMessageId}'
336
414
  | '/mail/flagged/$threadId/$messageId'
415
+ | '/mail/flagged/compose/{-$outboxMessageId}'
416
+ | '/mail/outbox/compose/{-$outboxMessageId}'
337
417
  | '/mail/outbox/draft/$outboxMessageId'
338
418
  | '/mail/$mailboxId/$threadId/'
339
419
  | '/mail/brief/$threadId/'
340
420
  | '/mail/flagged/$threadId/'
421
+ | '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}'
422
+ | '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}'
423
+ | '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}'
341
424
  fileRoutesByTo: FileRoutesByTo
342
425
  to:
343
426
  | '/'
@@ -357,12 +440,19 @@ export interface FileRouteTypes {
357
440
  | '/mail/flagged'
358
441
  | '/mail/outbox'
359
442
  | '/mail/$mailboxId/$threadId/$messageId'
443
+ | '/mail/$mailboxId/compose/{-$outboxMessageId}'
360
444
  | '/mail/brief/$threadId/$messageId'
445
+ | '/mail/brief/compose/{-$outboxMessageId}'
361
446
  | '/mail/flagged/$threadId/$messageId'
447
+ | '/mail/flagged/compose/{-$outboxMessageId}'
448
+ | '/mail/outbox/compose/{-$outboxMessageId}'
362
449
  | '/mail/outbox/draft/$outboxMessageId'
363
450
  | '/mail/$mailboxId/$threadId'
364
451
  | '/mail/brief/$threadId'
365
452
  | '/mail/flagged/$threadId'
453
+ | '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}'
454
+ | '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}'
455
+ | '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}'
366
456
  id:
367
457
  | '__root__'
368
458
  | '/'
@@ -391,12 +481,19 @@ export interface FileRouteTypes {
391
481
  | '/mail/flagged/'
392
482
  | '/mail/outbox/'
393
483
  | '/mail/$mailboxId/$threadId/$messageId'
484
+ | '/mail/$mailboxId/compose/{-$outboxMessageId}'
394
485
  | '/mail/brief/$threadId/$messageId'
486
+ | '/mail/brief/compose/{-$outboxMessageId}'
395
487
  | '/mail/flagged/$threadId/$messageId'
488
+ | '/mail/flagged/compose/{-$outboxMessageId}'
489
+ | '/mail/outbox/compose/{-$outboxMessageId}'
396
490
  | '/mail/outbox/draft/$outboxMessageId'
397
491
  | '/mail/$mailboxId/$threadId/'
398
492
  | '/mail/brief/$threadId/'
399
493
  | '/mail/flagged/$threadId/'
494
+ | '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}'
495
+ | '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}'
496
+ | '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}'
400
497
  fileRoutesById: FileRoutesById
401
498
  }
402
499
  export interface RootRouteChildren {
@@ -597,6 +694,13 @@ declare module '@tanstack/react-router' {
597
694
  preLoaderRoute: typeof MailMailboxIdThreadIdMessageIdRouteImport
598
695
  parentRoute: typeof MailMailboxIdThreadIdRoute
599
696
  }
697
+ '/mail/$mailboxId/compose/{-$outboxMessageId}': {
698
+ id: '/mail/$mailboxId/compose/{-$outboxMessageId}'
699
+ path: '/compose/{-$outboxMessageId}'
700
+ fullPath: '/mail/$mailboxId/compose/{-$outboxMessageId}'
701
+ preLoaderRoute: typeof MailMailboxIdComposeChar123OutboxMessageIdChar125RouteImport
702
+ parentRoute: typeof MailMailboxIdRoute
703
+ }
600
704
  '/mail/brief/$threadId/': {
601
705
  id: '/mail/brief/$threadId/'
602
706
  path: '/'
@@ -611,6 +715,13 @@ declare module '@tanstack/react-router' {
611
715
  preLoaderRoute: typeof MailBriefThreadIdMessageIdRouteImport
612
716
  parentRoute: typeof MailBriefThreadIdRoute
613
717
  }
718
+ '/mail/brief/compose/{-$outboxMessageId}': {
719
+ id: '/mail/brief/compose/{-$outboxMessageId}'
720
+ path: '/compose/{-$outboxMessageId}'
721
+ fullPath: '/mail/brief/compose/{-$outboxMessageId}'
722
+ preLoaderRoute: typeof MailBriefComposeChar123OutboxMessageIdChar125RouteImport
723
+ parentRoute: typeof MailBriefRoute
724
+ }
614
725
  '/mail/flagged/$threadId/': {
615
726
  id: '/mail/flagged/$threadId/'
616
727
  path: '/'
@@ -625,6 +736,20 @@ declare module '@tanstack/react-router' {
625
736
  preLoaderRoute: typeof MailFlaggedThreadIdMessageIdRouteImport
626
737
  parentRoute: typeof MailFlaggedThreadIdRoute
627
738
  }
739
+ '/mail/flagged/compose/{-$outboxMessageId}': {
740
+ id: '/mail/flagged/compose/{-$outboxMessageId}'
741
+ path: '/compose/{-$outboxMessageId}'
742
+ fullPath: '/mail/flagged/compose/{-$outboxMessageId}'
743
+ preLoaderRoute: typeof MailFlaggedComposeChar123OutboxMessageIdChar125RouteImport
744
+ parentRoute: typeof MailFlaggedRoute
745
+ }
746
+ '/mail/outbox/compose/{-$outboxMessageId}': {
747
+ id: '/mail/outbox/compose/{-$outboxMessageId}'
748
+ path: '/compose/{-$outboxMessageId}'
749
+ fullPath: '/mail/outbox/compose/{-$outboxMessageId}'
750
+ preLoaderRoute: typeof MailOutboxComposeChar123OutboxMessageIdChar125RouteImport
751
+ parentRoute: typeof MailOutboxRoute
752
+ }
628
753
  '/mail/outbox/draft/$outboxMessageId': {
629
754
  id: '/mail/outbox/draft/$outboxMessageId'
630
755
  path: '/draft/$outboxMessageId'
@@ -632,16 +757,53 @@ declare module '@tanstack/react-router' {
632
757
  preLoaderRoute: typeof MailOutboxDraftOutboxMessageIdRouteImport
633
758
  parentRoute: typeof MailOutboxRoute
634
759
  }
760
+ '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}': {
761
+ id: '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}'
762
+ path: '/$mode/{-$outboxMessageId}'
763
+ fullPath: '/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}'
764
+ preLoaderRoute: typeof MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport
765
+ parentRoute: typeof MailMailboxIdThreadIdMessageIdRoute
766
+ }
767
+ '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}': {
768
+ id: '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}'
769
+ path: '/$mode/{-$outboxMessageId}'
770
+ fullPath: '/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}'
771
+ preLoaderRoute: typeof MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport
772
+ parentRoute: typeof MailBriefThreadIdMessageIdRoute
773
+ }
774
+ '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}': {
775
+ id: '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}'
776
+ path: '/$mode/{-$outboxMessageId}'
777
+ fullPath: '/mail/flagged/$threadId/$messageId/$mode/{-$outboxMessageId}'
778
+ preLoaderRoute: typeof MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125RouteImport
779
+ parentRoute: typeof MailFlaggedThreadIdMessageIdRoute
780
+ }
635
781
  }
636
782
  }
637
783
 
784
+ interface MailMailboxIdThreadIdMessageIdRouteChildren {
785
+ MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route: typeof MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
786
+ }
787
+
788
+ const MailMailboxIdThreadIdMessageIdRouteChildren: MailMailboxIdThreadIdMessageIdRouteChildren =
789
+ {
790
+ MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route:
791
+ MailMailboxIdThreadIdMessageIdModeChar123OutboxMessageIdChar125Route,
792
+ }
793
+
794
+ const MailMailboxIdThreadIdMessageIdRouteWithChildren =
795
+ MailMailboxIdThreadIdMessageIdRoute._addFileChildren(
796
+ MailMailboxIdThreadIdMessageIdRouteChildren,
797
+ )
798
+
638
799
  interface MailMailboxIdThreadIdRouteChildren {
639
- MailMailboxIdThreadIdMessageIdRoute: typeof MailMailboxIdThreadIdMessageIdRoute
800
+ MailMailboxIdThreadIdMessageIdRoute: typeof MailMailboxIdThreadIdMessageIdRouteWithChildren
640
801
  MailMailboxIdThreadIdIndexRoute: typeof MailMailboxIdThreadIdIndexRoute
641
802
  }
642
803
 
643
804
  const MailMailboxIdThreadIdRouteChildren: MailMailboxIdThreadIdRouteChildren = {
644
- MailMailboxIdThreadIdMessageIdRoute: MailMailboxIdThreadIdMessageIdRoute,
805
+ MailMailboxIdThreadIdMessageIdRoute:
806
+ MailMailboxIdThreadIdMessageIdRouteWithChildren,
645
807
  MailMailboxIdThreadIdIndexRoute: MailMailboxIdThreadIdIndexRoute,
646
808
  }
647
809
 
@@ -653,24 +815,42 @@ const MailMailboxIdThreadIdRouteWithChildren =
653
815
  interface MailMailboxIdRouteChildren {
654
816
  MailMailboxIdThreadIdRoute: typeof MailMailboxIdThreadIdRouteWithChildren
655
817
  MailMailboxIdIndexRoute: typeof MailMailboxIdIndexRoute
818
+ MailMailboxIdComposeChar123OutboxMessageIdChar125Route: typeof MailMailboxIdComposeChar123OutboxMessageIdChar125Route
656
819
  }
657
820
 
658
821
  const MailMailboxIdRouteChildren: MailMailboxIdRouteChildren = {
659
822
  MailMailboxIdThreadIdRoute: MailMailboxIdThreadIdRouteWithChildren,
660
823
  MailMailboxIdIndexRoute: MailMailboxIdIndexRoute,
824
+ MailMailboxIdComposeChar123OutboxMessageIdChar125Route:
825
+ MailMailboxIdComposeChar123OutboxMessageIdChar125Route,
661
826
  }
662
827
 
663
828
  const MailMailboxIdRouteWithChildren = MailMailboxIdRoute._addFileChildren(
664
829
  MailMailboxIdRouteChildren,
665
830
  )
666
831
 
832
+ interface MailBriefThreadIdMessageIdRouteChildren {
833
+ MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route: typeof MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
834
+ }
835
+
836
+ const MailBriefThreadIdMessageIdRouteChildren: MailBriefThreadIdMessageIdRouteChildren =
837
+ {
838
+ MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route:
839
+ MailBriefThreadIdMessageIdModeChar123OutboxMessageIdChar125Route,
840
+ }
841
+
842
+ const MailBriefThreadIdMessageIdRouteWithChildren =
843
+ MailBriefThreadIdMessageIdRoute._addFileChildren(
844
+ MailBriefThreadIdMessageIdRouteChildren,
845
+ )
846
+
667
847
  interface MailBriefThreadIdRouteChildren {
668
- MailBriefThreadIdMessageIdRoute: typeof MailBriefThreadIdMessageIdRoute
848
+ MailBriefThreadIdMessageIdRoute: typeof MailBriefThreadIdMessageIdRouteWithChildren
669
849
  MailBriefThreadIdIndexRoute: typeof MailBriefThreadIdIndexRoute
670
850
  }
671
851
 
672
852
  const MailBriefThreadIdRouteChildren: MailBriefThreadIdRouteChildren = {
673
- MailBriefThreadIdMessageIdRoute: MailBriefThreadIdMessageIdRoute,
853
+ MailBriefThreadIdMessageIdRoute: MailBriefThreadIdMessageIdRouteWithChildren,
674
854
  MailBriefThreadIdIndexRoute: MailBriefThreadIdIndexRoute,
675
855
  }
676
856
 
@@ -680,24 +860,43 @@ const MailBriefThreadIdRouteWithChildren =
680
860
  interface MailBriefRouteChildren {
681
861
  MailBriefThreadIdRoute: typeof MailBriefThreadIdRouteWithChildren
682
862
  MailBriefIndexRoute: typeof MailBriefIndexRoute
863
+ MailBriefComposeChar123OutboxMessageIdChar125Route: typeof MailBriefComposeChar123OutboxMessageIdChar125Route
683
864
  }
684
865
 
685
866
  const MailBriefRouteChildren: MailBriefRouteChildren = {
686
867
  MailBriefThreadIdRoute: MailBriefThreadIdRouteWithChildren,
687
868
  MailBriefIndexRoute: MailBriefIndexRoute,
869
+ MailBriefComposeChar123OutboxMessageIdChar125Route:
870
+ MailBriefComposeChar123OutboxMessageIdChar125Route,
688
871
  }
689
872
 
690
873
  const MailBriefRouteWithChildren = MailBriefRoute._addFileChildren(
691
874
  MailBriefRouteChildren,
692
875
  )
693
876
 
877
+ interface MailFlaggedThreadIdMessageIdRouteChildren {
878
+ MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route: typeof MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route
879
+ }
880
+
881
+ const MailFlaggedThreadIdMessageIdRouteChildren: MailFlaggedThreadIdMessageIdRouteChildren =
882
+ {
883
+ MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route:
884
+ MailFlaggedThreadIdMessageIdModeChar123OutboxMessageIdChar125Route,
885
+ }
886
+
887
+ const MailFlaggedThreadIdMessageIdRouteWithChildren =
888
+ MailFlaggedThreadIdMessageIdRoute._addFileChildren(
889
+ MailFlaggedThreadIdMessageIdRouteChildren,
890
+ )
891
+
694
892
  interface MailFlaggedThreadIdRouteChildren {
695
- MailFlaggedThreadIdMessageIdRoute: typeof MailFlaggedThreadIdMessageIdRoute
893
+ MailFlaggedThreadIdMessageIdRoute: typeof MailFlaggedThreadIdMessageIdRouteWithChildren
696
894
  MailFlaggedThreadIdIndexRoute: typeof MailFlaggedThreadIdIndexRoute
697
895
  }
698
896
 
699
897
  const MailFlaggedThreadIdRouteChildren: MailFlaggedThreadIdRouteChildren = {
700
- MailFlaggedThreadIdMessageIdRoute: MailFlaggedThreadIdMessageIdRoute,
898
+ MailFlaggedThreadIdMessageIdRoute:
899
+ MailFlaggedThreadIdMessageIdRouteWithChildren,
701
900
  MailFlaggedThreadIdIndexRoute: MailFlaggedThreadIdIndexRoute,
702
901
  }
703
902
 
@@ -707,11 +906,14 @@ const MailFlaggedThreadIdRouteWithChildren =
707
906
  interface MailFlaggedRouteChildren {
708
907
  MailFlaggedThreadIdRoute: typeof MailFlaggedThreadIdRouteWithChildren
709
908
  MailFlaggedIndexRoute: typeof MailFlaggedIndexRoute
909
+ MailFlaggedComposeChar123OutboxMessageIdChar125Route: typeof MailFlaggedComposeChar123OutboxMessageIdChar125Route
710
910
  }
711
911
 
712
912
  const MailFlaggedRouteChildren: MailFlaggedRouteChildren = {
713
913
  MailFlaggedThreadIdRoute: MailFlaggedThreadIdRouteWithChildren,
714
914
  MailFlaggedIndexRoute: MailFlaggedIndexRoute,
915
+ MailFlaggedComposeChar123OutboxMessageIdChar125Route:
916
+ MailFlaggedComposeChar123OutboxMessageIdChar125Route,
715
917
  }
716
918
 
717
919
  const MailFlaggedRouteWithChildren = MailFlaggedRoute._addFileChildren(
@@ -720,11 +922,14 @@ const MailFlaggedRouteWithChildren = MailFlaggedRoute._addFileChildren(
720
922
 
721
923
  interface MailOutboxRouteChildren {
722
924
  MailOutboxIndexRoute: typeof MailOutboxIndexRoute
925
+ MailOutboxComposeChar123OutboxMessageIdChar125Route: typeof MailOutboxComposeChar123OutboxMessageIdChar125Route
723
926
  MailOutboxDraftOutboxMessageIdRoute: typeof MailOutboxDraftOutboxMessageIdRoute
724
927
  }
725
928
 
726
929
  const MailOutboxRouteChildren: MailOutboxRouteChildren = {
727
930
  MailOutboxIndexRoute: MailOutboxIndexRoute,
931
+ MailOutboxComposeChar123OutboxMessageIdChar125Route:
932
+ MailOutboxComposeChar123OutboxMessageIdChar125Route,
728
933
  MailOutboxDraftOutboxMessageIdRoute: MailOutboxDraftOutboxMessageIdRoute,
729
934
  }
730
935
 
@@ -0,0 +1,14 @@
1
+ // biome-ignore lint/style/useFilenamingConvention: TanStack Router convention
2
+ /**
3
+ * The reply, reply-all or forward under a conversation opened from a folder.
4
+ *
5
+ * The same shape as the brief's: a child of the message so the source is the
6
+ * address, the mode as one param rather than three literal routes, and the
7
+ * draft as an optional segment so recording it does not unmount the composer.
8
+ * The conversation reads it off the address and opens it at its head.
9
+ */
10
+ import { createFileRoute } from "@tanstack/react-router";
11
+
12
+ export const Route = createFileRoute(
13
+ "/mail/$mailboxId/$threadId/$messageId/$mode/{-$outboxMessageId}",
14
+ )({});
@@ -6,6 +6,10 @@
6
6
  * with no thread, so the pane has nothing to fetch by. The segment names which
7
7
  * message inside the thread the reader pointed at, and the surface it renders is
8
8
  * the thread's.
9
+ *
10
+ * It mounts the pane rather than delegating to an index route, because the
11
+ * reply is a segment under it: a conversation that came back on a child match
12
+ * would collapse and scroll to the top the moment someone pressed Reply.
9
13
  */
10
14
  import { createFileRoute } from "@tanstack/react-router";
11
15
  import { MailboxPane } from "@/components/mail/MailboxPane";
@@ -0,0 +1,14 @@
1
+ // biome-ignore lint/style/useFilenamingConvention: TanStack Router convention
2
+ /**
3
+ * /mail/$mailboxId/compose — the compose surface, open over a folder, and
4
+ * /mail/$mailboxId/compose/<outboxMessageId> for the draft it is editing. The
5
+ * Drafts folder's own rows open here.
6
+ */
7
+ import { createFileRoute } from "@tanstack/react-router";
8
+ import { FullCompose } from "@/components/compose/FullCompose";
9
+
10
+ export const Route = createFileRoute(
11
+ "/mail/$mailboxId/compose/{-$outboxMessageId}",
12
+ )({
13
+ component: FullCompose,
14
+ });
@@ -0,0 +1,26 @@
1
+ // biome-ignore lint/style/useFilenamingConvention: TanStack Router convention
2
+ /**
3
+ * /mail/brief/<thread>/<message>/reply — the answer to that message, written at
4
+ * the head of the conversation it answers, and
5
+ * /mail/brief/<thread>/<message>/reply/<outboxMessageId> once it has a draft.
6
+ *
7
+ * A child of the message, so a reply cannot exist without a source and the
8
+ * conversation stays matched behind it. `reply`, `reply-all` and `forward` are
9
+ * one param rather than three literal routes: they differ in what the composer
10
+ * opens on, not in what the address mounts.
11
+ *
12
+ * The draft is an optional segment of this one route rather than a child of it,
13
+ * because the first autosave adopts the id it just created while the reader is
14
+ * still typing. Two routes would unmount the composer mid-sentence; one route
15
+ * with a param that arrives is a rewritten address and nothing more.
16
+ *
17
+ * The route declares the segments and mounts nothing itself. The composer opens
18
+ * inside the conversation, above the turn it answers, and the conversation is
19
+ * the single pane on a phone — where there is no reading `Outlet` to fill — so
20
+ * the pane reads the reply off the address, the way the shell reads compose.
21
+ */
22
+ import { createFileRoute } from "@tanstack/react-router";
23
+
24
+ export const Route = createFileRoute(
25
+ "/mail/brief/$threadId/$messageId/$mode/{-$outboxMessageId}",
26
+ )({});
@@ -6,6 +6,10 @@
6
6
  * with no thread, so the pane has nothing to fetch by. The segment names which
7
7
  * message inside the thread the reader pointed at, and the surface it renders is
8
8
  * the thread's.
9
+ *
10
+ * It mounts the pane rather than delegating to an index route, because the
11
+ * reply is a segment under it: a conversation that came back on a child match
12
+ * would collapse and scroll to the top the moment someone pressed Reply.
9
13
  */
10
14
  import { createFileRoute } from "@tanstack/react-router";
11
15
  import { BriefPane } from "@/components/mail/BriefPane";