@remit/web-client 0.0.149 → 0.0.151
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/compose/ComposeProvider.tsx +27 -5
- package/src/components/layout/ComposeFab.tsx +3 -2
- package/src/components/mail/BriefPane.tsx +8 -8
- package/src/components/mail/DailyBrief.tsx +3 -7
- package/src/components/mail/DraftsView.tsx +7 -3
- package/src/components/mail/IntelligencePane.tsx +4 -3
- package/src/components/mail/MailListHeader.tsx +1 -5
- package/src/components/mail/MailSidebarAdapter.tsx +4 -3
- package/src/components/mail/MailboxPane.tsx +130 -150
- package/src/components/mail/MessageActionMenu.tsx +12 -8
- package/src/components/mail/MessageList.selection.test.ts +6 -7
- package/src/components/mail/MessageList.tsx +28 -32
- package/src/components/mail/MessageListItem.tsx +2 -1
- package/src/components/mail/MessageRow.tsx +13 -10
- package/src/components/mail/OutboxPane.tsx +31 -28
- package/src/components/mail/SwipeableMessageRow.modifier-select.test.ts +8 -3
- package/src/components/mail/SwipeableMessageRow.tsx +8 -12
- package/src/hooks/useIntelligenceData.ts +1 -0
- package/src/lib/conversation-target.ts +7 -49
- package/src/lib/mail-search.ts +13 -11
- package/src/routeTree.gen.ts +96 -0
- package/src/routes/mail/$mailboxId/$threadId/$messageId.tsx +19 -0
- package/src/routes/mail/$mailboxId/$threadId/index.tsx +14 -0
- package/src/routes/mail/$mailboxId/$threadId.tsx +12 -0
- package/src/routes/mail/$mailboxId.tsx +7 -3
- package/src/routes/mail/brief.tsx +2 -2
- package/src/routes/mail/outbox/draft/$outboxMessageId.tsx +19 -0
- package/src/routes/mail/outbox.tsx +6 -1
- package/src/routing/index.ts +6 -5
- package/src/routing/open-thread.ts +58 -0
- package/src/routing/outbox-draft.ts +17 -0
- package/src/lib/conversation-target.test.ts +0 -68
- package/src/lib/search-pending.test.ts +0 -99
- package/src/lib/search-pending.ts +0 -49
- package/src/routing/brief-thread.ts +0 -47
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* The mailbox list, the daily brief and Flagged used to carry three
|
|
5
5
|
* near-identical rows (#149). They differ in two axes only, both props here:
|
|
6
6
|
*
|
|
7
|
-
* - `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* - `link` — set for the mailbox list, whose rows are real links so a plain
|
|
8
|
+
* click routes and a middle click opens a tab. Omitted for the brief and
|
|
9
|
+
* Flagged, whose rows report the tap through `onClick`.
|
|
10
10
|
* - `selection` — set where multi-select is available. Absent renders the
|
|
11
11
|
* avatar alone with no checkbox, which is what "non-selectable mode" means.
|
|
12
12
|
*/
|
|
@@ -73,8 +73,11 @@ export interface MessageRowProps {
|
|
|
73
73
|
* orphan `option` is invalid ARIA and costs the row its button semantics.
|
|
74
74
|
*/
|
|
75
75
|
inListbox?: boolean;
|
|
76
|
-
/**
|
|
77
|
-
|
|
76
|
+
/**
|
|
77
|
+
* The address the row links to: the folder being browsed and the conversation
|
|
78
|
+
* this row opens. Omit for callback-driven rows.
|
|
79
|
+
*/
|
|
80
|
+
link?: { mailboxId: string; threadId: string };
|
|
78
81
|
/** Called when the row is opened by a plain click on a non-linking row. */
|
|
79
82
|
onClick?: () => void;
|
|
80
83
|
/** Called when the row takes DOM focus, so the roving cursor follows it. */
|
|
@@ -91,7 +94,7 @@ const MessageRowComponent = ({
|
|
|
91
94
|
badge,
|
|
92
95
|
selection: selectionProp,
|
|
93
96
|
inListbox = false,
|
|
94
|
-
|
|
97
|
+
link,
|
|
95
98
|
onClick,
|
|
96
99
|
onFocusRow: onFocusRowProp,
|
|
97
100
|
}: MessageRowProps) => {
|
|
@@ -240,7 +243,7 @@ const MessageRowComponent = ({
|
|
|
240
243
|
/>
|
|
241
244
|
);
|
|
242
245
|
|
|
243
|
-
if (
|
|
246
|
+
if (link === undefined) {
|
|
244
247
|
return (
|
|
245
248
|
<button type="button" {...interactionProps} className={className}>
|
|
246
249
|
{body}
|
|
@@ -250,9 +253,9 @@ const MessageRowComponent = ({
|
|
|
250
253
|
|
|
251
254
|
return (
|
|
252
255
|
<NavLink
|
|
253
|
-
to="/mail/$mailboxId"
|
|
254
|
-
params={{
|
|
255
|
-
search={(prev) =>
|
|
256
|
+
to="/mail/$mailboxId/$threadId/$messageId"
|
|
257
|
+
params={{ ...link, messageId }}
|
|
258
|
+
search={(prev) => prev}
|
|
256
259
|
variant="row"
|
|
257
260
|
{...interactionProps}
|
|
258
261
|
className={className}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OutboxPane — compound component for the outbox view (/mail/outbox route).
|
|
3
3
|
*
|
|
4
|
-
* Usage in
|
|
4
|
+
* Usage in the list layout route:
|
|
5
5
|
*
|
|
6
|
-
* <OutboxPane>
|
|
7
|
-
* <
|
|
6
|
+
* <OutboxPane outboxMessageId={useOutboxDraftId()}>
|
|
7
|
+
* <MailShell
|
|
8
8
|
* list={<OutboxPane.List />}
|
|
9
|
-
* reading={<
|
|
9
|
+
* reading={<Outlet />}
|
|
10
10
|
* />
|
|
11
11
|
* </OutboxPane>
|
|
12
12
|
*
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
useRovingFocus,
|
|
30
30
|
} from "@remit/ui";
|
|
31
31
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
32
|
-
import { useNavigate
|
|
32
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
33
33
|
import {
|
|
34
34
|
AlertCircle,
|
|
35
35
|
AlertTriangle,
|
|
@@ -143,9 +143,11 @@ interface OutboxPaneContextValue {
|
|
|
143
143
|
/** True when that query asks for a filter the outbox cannot apply. */
|
|
144
144
|
unsupportedQuery: boolean;
|
|
145
145
|
isLoading: boolean;
|
|
146
|
+
/** The message the address names, which is the one the list highlights. */
|
|
146
147
|
selectedMessageId: string | undefined;
|
|
147
148
|
selectedMessage: RemitImapOutboxMessageResponse | undefined;
|
|
148
|
-
|
|
149
|
+
onOpenMessage: (outboxMessageId: string) => void;
|
|
150
|
+
onCloseMessage: () => void;
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
const OutboxPaneCtx = createContext<OutboxPaneContextValue | null>(null);
|
|
@@ -161,16 +163,16 @@ function useOutboxPane(): OutboxPaneContextValue {
|
|
|
161
163
|
/* ------------------------------------------------------------------ */
|
|
162
164
|
|
|
163
165
|
interface OutboxPaneProps {
|
|
166
|
+
/** The open message, as the address states it. */
|
|
167
|
+
outboxMessageId: string | undefined;
|
|
164
168
|
children: ReactNode;
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
function OutboxPaneProvider({
|
|
171
|
+
function OutboxPaneProvider({
|
|
172
|
+
outboxMessageId: selectedMessageId,
|
|
173
|
+
children,
|
|
174
|
+
}: OutboxPaneProps) {
|
|
168
175
|
const navigate = useNavigate();
|
|
169
|
-
// Read selectedOutboxMessageId from the URL directly — this avoids threading
|
|
170
|
-
// a prop through the parent shell for a param that only outbox uses.
|
|
171
|
-
const { selectedOutboxMessageId: selectedMessageId } = useSearch({
|
|
172
|
-
strict: false,
|
|
173
|
-
}) as { selectedOutboxMessageId?: string };
|
|
174
176
|
|
|
175
177
|
const { data: outboxResponse, isLoading } = useQuery(
|
|
176
178
|
outboxOperationsListOutboxMessagesOptions(),
|
|
@@ -210,19 +212,21 @@ function OutboxPaneProvider({ children }: OutboxPaneProps) {
|
|
|
210
212
|
[messages, selectedMessageId],
|
|
211
213
|
);
|
|
212
214
|
|
|
213
|
-
const
|
|
214
|
-
(
|
|
215
|
+
const handleOpenMessage = useCallback(
|
|
216
|
+
(outboxMessageId: string) => {
|
|
215
217
|
navigate({
|
|
216
|
-
to: "/mail/outbox",
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
selectedOutboxMessageId: id,
|
|
220
|
-
}),
|
|
218
|
+
to: "/mail/outbox/draft/$outboxMessageId",
|
|
219
|
+
params: { outboxMessageId },
|
|
220
|
+
search: (prev) => prev,
|
|
221
221
|
});
|
|
222
222
|
},
|
|
223
223
|
[navigate],
|
|
224
224
|
);
|
|
225
225
|
|
|
226
|
+
const handleCloseMessage = useCallback(() => {
|
|
227
|
+
navigate({ to: "/mail/outbox", search: (prev) => prev });
|
|
228
|
+
}, [navigate]);
|
|
229
|
+
|
|
226
230
|
const ctx: OutboxPaneContextValue = {
|
|
227
231
|
messages,
|
|
228
232
|
hasQuery,
|
|
@@ -230,7 +234,8 @@ function OutboxPaneProvider({ children }: OutboxPaneProps) {
|
|
|
230
234
|
isLoading,
|
|
231
235
|
selectedMessageId,
|
|
232
236
|
selectedMessage,
|
|
233
|
-
|
|
237
|
+
onOpenMessage: handleOpenMessage,
|
|
238
|
+
onCloseMessage: handleCloseMessage,
|
|
234
239
|
};
|
|
235
240
|
|
|
236
241
|
return (
|
|
@@ -438,7 +443,7 @@ function OutboxList() {
|
|
|
438
443
|
unsupportedQuery,
|
|
439
444
|
isLoading,
|
|
440
445
|
selectedMessageId,
|
|
441
|
-
|
|
446
|
+
onOpenMessage,
|
|
442
447
|
} = useOutboxPane();
|
|
443
448
|
const listRef = useRef<HTMLDivElement>(null);
|
|
444
449
|
useRovingFocus({ containerRef: listRef, itemSelector: LIST_ROW_SELECTOR });
|
|
@@ -482,7 +487,7 @@ function OutboxList() {
|
|
|
482
487
|
key={message.outboxMessageId}
|
|
483
488
|
message={message}
|
|
484
489
|
isSelected={selectedMessageId === message.outboxMessageId}
|
|
485
|
-
onSelect={() =>
|
|
490
|
+
onSelect={() => onOpenMessage(message.outboxMessageId)}
|
|
486
491
|
/>
|
|
487
492
|
))}
|
|
488
493
|
</div>
|
|
@@ -529,7 +534,8 @@ function OutboxPhone() {
|
|
|
529
534
|
isLoading,
|
|
530
535
|
selectedMessageId,
|
|
531
536
|
selectedMessage,
|
|
532
|
-
|
|
537
|
+
onOpenMessage,
|
|
538
|
+
onCloseMessage,
|
|
533
539
|
} = useOutboxPane();
|
|
534
540
|
const listRef = useRef<HTMLDivElement>(null);
|
|
535
541
|
useRovingFocus({ containerRef: listRef, itemSelector: LIST_ROW_SELECTOR });
|
|
@@ -544,10 +550,7 @@ function OutboxPhone() {
|
|
|
544
550
|
|
|
545
551
|
if (selectedMessage) {
|
|
546
552
|
return (
|
|
547
|
-
<OutboxMessageDetail
|
|
548
|
-
message={selectedMessage}
|
|
549
|
-
onBack={() => onSelectMessage(undefined)}
|
|
550
|
-
/>
|
|
553
|
+
<OutboxMessageDetail message={selectedMessage} onBack={onCloseMessage} />
|
|
551
554
|
);
|
|
552
555
|
}
|
|
553
556
|
|
|
@@ -579,7 +582,7 @@ function OutboxPhone() {
|
|
|
579
582
|
key={message.outboxMessageId}
|
|
580
583
|
message={message}
|
|
581
584
|
isSelected={selectedMessageId === message.outboxMessageId}
|
|
582
|
-
onSelect={() =>
|
|
585
|
+
onSelect={() => onOpenMessage(message.outboxMessageId)}
|
|
583
586
|
/>
|
|
584
587
|
))}
|
|
585
588
|
</div>
|
|
@@ -64,6 +64,10 @@ const mailRoute = createRoute({
|
|
|
64
64
|
getParentRoute: () => rootRoute,
|
|
65
65
|
path: "/mail/$mailboxId",
|
|
66
66
|
});
|
|
67
|
+
const messageRoute = createRoute({
|
|
68
|
+
getParentRoute: () => mailRoute,
|
|
69
|
+
path: "$threadId/$messageId",
|
|
70
|
+
});
|
|
67
71
|
|
|
68
72
|
interface Mounted {
|
|
69
73
|
row: HTMLElement;
|
|
@@ -74,7 +78,7 @@ interface Mounted {
|
|
|
74
78
|
const mountRow = (selectionTakesIt = true): Mounted => {
|
|
75
79
|
const selects: SelectionModifiers[] = [];
|
|
76
80
|
const router = createRouter({
|
|
77
|
-
routeTree: rootRoute.addChildren([mailRoute]),
|
|
81
|
+
routeTree: rootRoute.addChildren([mailRoute.addChildren([messageRoute])]),
|
|
78
82
|
history: createMemoryHistory({ initialEntries: ["/mail/mbx-1"] }),
|
|
79
83
|
}) as unknown as AnyRouter;
|
|
80
84
|
const created = createDomHarness({ viewportWidth: HALF_SCREEN_WIDTH });
|
|
@@ -148,9 +152,10 @@ const click = (
|
|
|
148
152
|
}),
|
|
149
153
|
);
|
|
150
154
|
|
|
155
|
+
// The conversation the row opened is the address, so the message it named is the
|
|
156
|
+
// last segment of the path.
|
|
151
157
|
const openedMessageId = (router: AnyRouter): string | undefined =>
|
|
152
|
-
|
|
153
|
-
.selectedMessageId;
|
|
158
|
+
router.state.location.pathname.split("/")[4];
|
|
154
159
|
|
|
155
160
|
describe("SwipeableMessageRow — modifier selection below the desktop width", () => {
|
|
156
161
|
it("takes a shift-press for selection instead of opening the message", async () => {
|
|
@@ -13,11 +13,6 @@ import { formatEmailDate } from "@/lib/format";
|
|
|
13
13
|
import { MessageListItem } from "./MessageListItem";
|
|
14
14
|
import { useModifierSelect } from "./useModifierSelect";
|
|
15
15
|
|
|
16
|
-
interface MailboxLinkSearch {
|
|
17
|
-
selectedMessageId?: string;
|
|
18
|
-
q?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
16
|
interface SwipeableMessageRowProps {
|
|
22
17
|
thread: RemitImapThreadMessageResponse;
|
|
23
18
|
mailboxId: string;
|
|
@@ -103,14 +98,15 @@ export const SwipeableMessageRow = ({
|
|
|
103
98
|
|
|
104
99
|
const handleOpen = useCallback(() => {
|
|
105
100
|
navigate({
|
|
106
|
-
to: "/mail/$mailboxId",
|
|
107
|
-
params: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
101
|
+
to: "/mail/$mailboxId/$threadId/$messageId",
|
|
102
|
+
params: {
|
|
103
|
+
mailboxId,
|
|
104
|
+
threadId: thread.threadId,
|
|
105
|
+
messageId: thread.messageId,
|
|
106
|
+
},
|
|
107
|
+
search: (prev) => prev,
|
|
112
108
|
});
|
|
113
|
-
}, [navigate, mailboxId, thread.messageId]);
|
|
109
|
+
}, [navigate, mailboxId, thread.threadId, thread.messageId]);
|
|
114
110
|
|
|
115
111
|
const modifierSelect = useModifierSelect(thread.messageId, onRowSelect);
|
|
116
112
|
|
|
@@ -363,6 +363,7 @@ export function useIntelligenceData(
|
|
|
363
363
|
.filter((r) => r.messageId !== thread.messageId)
|
|
364
364
|
.map((r) => ({
|
|
365
365
|
id: r.messageId,
|
|
366
|
+
threadId: r.threadId,
|
|
366
367
|
// Prefer the mailbox the user is already viewing when the message also
|
|
367
368
|
// lives there, so opening a similar message stays in the same folder;
|
|
368
369
|
// otherwise fall back to the first mailbox it lives in.
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* What the reading pane needs to show a conversation.
|
|
3
|
+
*
|
|
4
|
+
* The thread is the whole address — `ConversationView` fetches by it — and the
|
|
5
|
+
* rest is what the list already knows about the row the reader pointed at, so
|
|
6
|
+
* the pane paints a subject before the thread's own messages arrive. A thread no
|
|
7
|
+
* list holds carries none of it and still opens.
|
|
7
8
|
*/
|
|
8
|
-
import type {
|
|
9
|
-
RemitImapMessageAuthenticity,
|
|
10
|
-
RemitImapThreadMessageResponse,
|
|
11
|
-
} from "@remit/api-http-client/types.gen.ts";
|
|
9
|
+
import type { RemitImapMessageAuthenticity } from "@remit/api-http-client/types.gen.ts";
|
|
12
10
|
|
|
13
11
|
export interface ConversationTarget {
|
|
14
12
|
threadId: string;
|
|
@@ -17,43 +15,3 @@ export interface ConversationTarget {
|
|
|
17
15
|
messageId?: string;
|
|
18
16
|
authenticity?: RemitImapMessageAuthenticity;
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
function threadToConversationTarget(
|
|
22
|
-
thread: RemitImapThreadMessageResponse,
|
|
23
|
-
): ConversationTarget {
|
|
24
|
-
return {
|
|
25
|
-
threadId: thread.threadId,
|
|
26
|
-
mailboxId: thread.mailboxId,
|
|
27
|
-
subject: thread.subject,
|
|
28
|
-
messageId: thread.messageId,
|
|
29
|
-
authenticity: thread.authenticity,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Resolve the conversation to open. Prefer the fully loaded thread (it carries
|
|
35
|
-
* the subject, authenticity and read state the reading pane wants); otherwise
|
|
36
|
-
* fall back to the `threadId` + `mailboxId` carried in the URL by a tapped
|
|
37
|
-
* "Related" hit so a message outside the loaded list still opens.
|
|
38
|
-
*/
|
|
39
|
-
export function buildConversationTarget(
|
|
40
|
-
selectedThread: RemitImapThreadMessageResponse | undefined,
|
|
41
|
-
fallback: {
|
|
42
|
-
messageId?: string;
|
|
43
|
-
threadId?: string;
|
|
44
|
-
mailboxId?: string;
|
|
45
|
-
},
|
|
46
|
-
): ConversationTarget | undefined {
|
|
47
|
-
if (selectedThread) return threadToConversationTarget(selectedThread);
|
|
48
|
-
// Require the messageId too: it's always set alongside the threadId when a
|
|
49
|
-
// result is tapped, and gating on it means clearing `selectedMessageId` (e.g.
|
|
50
|
-
// pressing Back) closes the conversation even if a stale threadId lingers.
|
|
51
|
-
if (fallback.messageId && fallback.threadId && fallback.mailboxId) {
|
|
52
|
-
return {
|
|
53
|
-
threadId: fallback.threadId,
|
|
54
|
-
mailboxId: fallback.mailboxId,
|
|
55
|
-
messageId: fallback.messageId,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
package/src/lib/mail-search.ts
CHANGED
|
@@ -16,7 +16,7 @@ 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
|
|
19
|
+
* "tolerated and ignored" means until the remaining lists follow.
|
|
20
20
|
*/
|
|
21
21
|
export const briefSearchSchema = listSearch.extend({});
|
|
22
22
|
|
|
@@ -24,17 +24,19 @@ export const flaggedSearchSchema = listSearch.extend({
|
|
|
24
24
|
selectedMessageId: z.string().optional(),
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The outbox's open message is a path segment
|
|
29
|
+
* (`/mail/outbox/draft/<outboxMessageId>`), so the query carries nothing but
|
|
30
|
+
* the search. An old link's selection param is dropped here.
|
|
31
|
+
*/
|
|
32
|
+
export const outboxSearchSchema = listSearch.extend({});
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
34
|
+
/**
|
|
35
|
+
* A folder's open thread and the message inside it are path segments
|
|
36
|
+
* (`/mail/<mailbox>/<thread>/<message>`), so the query carries nothing but the
|
|
37
|
+
* search.
|
|
38
|
+
*/
|
|
39
|
+
export const mailboxSearchSchema = listSearch.extend({});
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* `/mail` itself only redirects to the brief, so it accepts what the brief
|
package/src/routeTree.gen.ts
CHANGED
|
@@ -28,12 +28,16 @@ import { Route as SettingsLabelsRouteImport } from './routes/settings/labels'
|
|
|
28
28
|
import { Route as SettingsSendersRouteImport } from './routes/settings/senders'
|
|
29
29
|
import { Route as SettingsSuggestedVipsRouteImport } from './routes/settings/suggested-vips'
|
|
30
30
|
import { Route as MailMailboxIdIndexRouteImport } from './routes/mail/$mailboxId/index'
|
|
31
|
+
import { Route as MailMailboxIdThreadIdRouteImport } from './routes/mail/$mailboxId/$threadId'
|
|
31
32
|
import { Route as MailBriefIndexRouteImport } from './routes/mail/brief/index'
|
|
32
33
|
import { Route as MailBriefThreadIdRouteImport } from './routes/mail/brief/$threadId'
|
|
33
34
|
import { Route as MailFlaggedIndexRouteImport } from './routes/mail/flagged/index'
|
|
34
35
|
import { Route as MailOutboxIndexRouteImport } from './routes/mail/outbox/index'
|
|
36
|
+
import { Route as MailMailboxIdThreadIdIndexRouteImport } from './routes/mail/$mailboxId/$threadId/index'
|
|
37
|
+
import { Route as MailMailboxIdThreadIdMessageIdRouteImport } from './routes/mail/$mailboxId/$threadId/$messageId'
|
|
35
38
|
import { Route as MailBriefThreadIdIndexRouteImport } from './routes/mail/brief/$threadId/index'
|
|
36
39
|
import { Route as MailBriefThreadIdMessageIdRouteImport } from './routes/mail/brief/$threadId/$messageId'
|
|
40
|
+
import { Route as MailOutboxDraftOutboxMessageIdRouteImport } from './routes/mail/outbox/draft/$outboxMessageId'
|
|
37
41
|
|
|
38
42
|
const IndexRoute = IndexRouteImport.update({
|
|
39
43
|
id: '/',
|
|
@@ -130,6 +134,11 @@ const MailMailboxIdIndexRoute = MailMailboxIdIndexRouteImport.update({
|
|
|
130
134
|
path: '/',
|
|
131
135
|
getParentRoute: () => MailMailboxIdRoute,
|
|
132
136
|
} as any)
|
|
137
|
+
const MailMailboxIdThreadIdRoute = MailMailboxIdThreadIdRouteImport.update({
|
|
138
|
+
id: '/$threadId',
|
|
139
|
+
path: '/$threadId',
|
|
140
|
+
getParentRoute: () => MailMailboxIdRoute,
|
|
141
|
+
} as any)
|
|
133
142
|
const MailBriefIndexRoute = MailBriefIndexRouteImport.update({
|
|
134
143
|
id: '/',
|
|
135
144
|
path: '/',
|
|
@@ -150,6 +159,18 @@ const MailOutboxIndexRoute = MailOutboxIndexRouteImport.update({
|
|
|
150
159
|
path: '/',
|
|
151
160
|
getParentRoute: () => MailOutboxRoute,
|
|
152
161
|
} as any)
|
|
162
|
+
const MailMailboxIdThreadIdIndexRoute =
|
|
163
|
+
MailMailboxIdThreadIdIndexRouteImport.update({
|
|
164
|
+
id: '/',
|
|
165
|
+
path: '/',
|
|
166
|
+
getParentRoute: () => MailMailboxIdThreadIdRoute,
|
|
167
|
+
} as any)
|
|
168
|
+
const MailMailboxIdThreadIdMessageIdRoute =
|
|
169
|
+
MailMailboxIdThreadIdMessageIdRouteImport.update({
|
|
170
|
+
id: '/$messageId',
|
|
171
|
+
path: '/$messageId',
|
|
172
|
+
getParentRoute: () => MailMailboxIdThreadIdRoute,
|
|
173
|
+
} as any)
|
|
153
174
|
const MailBriefThreadIdIndexRoute = MailBriefThreadIdIndexRouteImport.update({
|
|
154
175
|
id: '/',
|
|
155
176
|
path: '/',
|
|
@@ -161,6 +182,12 @@ const MailBriefThreadIdMessageIdRoute =
|
|
|
161
182
|
path: '/$messageId',
|
|
162
183
|
getParentRoute: () => MailBriefThreadIdRoute,
|
|
163
184
|
} as any)
|
|
185
|
+
const MailOutboxDraftOutboxMessageIdRoute =
|
|
186
|
+
MailOutboxDraftOutboxMessageIdRouteImport.update({
|
|
187
|
+
id: '/draft/$outboxMessageId',
|
|
188
|
+
path: '/draft/$outboxMessageId',
|
|
189
|
+
getParentRoute: () => MailOutboxRoute,
|
|
190
|
+
} as any)
|
|
164
191
|
|
|
165
192
|
export interface FileRoutesByFullPath {
|
|
166
193
|
'/': typeof IndexRoute
|
|
@@ -181,12 +208,16 @@ export interface FileRoutesByFullPath {
|
|
|
181
208
|
'/settings/suggested-vips': typeof SettingsSuggestedVipsRoute
|
|
182
209
|
'/mail/': typeof MailIndexRoute
|
|
183
210
|
'/settings/': typeof SettingsIndexRoute
|
|
211
|
+
'/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdRouteWithChildren
|
|
184
212
|
'/mail/brief/$threadId': typeof MailBriefThreadIdRouteWithChildren
|
|
185
213
|
'/mail/$mailboxId/': typeof MailMailboxIdIndexRoute
|
|
186
214
|
'/mail/brief/': typeof MailBriefIndexRoute
|
|
187
215
|
'/mail/flagged/': typeof MailFlaggedIndexRoute
|
|
188
216
|
'/mail/outbox/': typeof MailOutboxIndexRoute
|
|
217
|
+
'/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
|
|
189
218
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
219
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
220
|
+
'/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
|
|
190
221
|
'/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
|
|
191
222
|
}
|
|
192
223
|
export interface FileRoutesByTo {
|
|
@@ -206,7 +237,10 @@ export interface FileRoutesByTo {
|
|
|
206
237
|
'/mail/brief': typeof MailBriefIndexRoute
|
|
207
238
|
'/mail/flagged': typeof MailFlaggedIndexRoute
|
|
208
239
|
'/mail/outbox': typeof MailOutboxIndexRoute
|
|
240
|
+
'/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
|
|
209
241
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
242
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
243
|
+
'/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdIndexRoute
|
|
210
244
|
'/mail/brief/$threadId': typeof MailBriefThreadIdIndexRoute
|
|
211
245
|
}
|
|
212
246
|
export interface FileRoutesById {
|
|
@@ -229,12 +263,16 @@ export interface FileRoutesById {
|
|
|
229
263
|
'/settings/suggested-vips': typeof SettingsSuggestedVipsRoute
|
|
230
264
|
'/mail/': typeof MailIndexRoute
|
|
231
265
|
'/settings/': typeof SettingsIndexRoute
|
|
266
|
+
'/mail/$mailboxId/$threadId': typeof MailMailboxIdThreadIdRouteWithChildren
|
|
232
267
|
'/mail/brief/$threadId': typeof MailBriefThreadIdRouteWithChildren
|
|
233
268
|
'/mail/$mailboxId/': typeof MailMailboxIdIndexRoute
|
|
234
269
|
'/mail/brief/': typeof MailBriefIndexRoute
|
|
235
270
|
'/mail/flagged/': typeof MailFlaggedIndexRoute
|
|
236
271
|
'/mail/outbox/': typeof MailOutboxIndexRoute
|
|
272
|
+
'/mail/$mailboxId/$threadId/$messageId': typeof MailMailboxIdThreadIdMessageIdRoute
|
|
237
273
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
274
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
275
|
+
'/mail/$mailboxId/$threadId/': typeof MailMailboxIdThreadIdIndexRoute
|
|
238
276
|
'/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
|
|
239
277
|
}
|
|
240
278
|
export interface FileRouteTypes {
|
|
@@ -258,12 +296,16 @@ export interface FileRouteTypes {
|
|
|
258
296
|
| '/settings/suggested-vips'
|
|
259
297
|
| '/mail/'
|
|
260
298
|
| '/settings/'
|
|
299
|
+
| '/mail/$mailboxId/$threadId'
|
|
261
300
|
| '/mail/brief/$threadId'
|
|
262
301
|
| '/mail/$mailboxId/'
|
|
263
302
|
| '/mail/brief/'
|
|
264
303
|
| '/mail/flagged/'
|
|
265
304
|
| '/mail/outbox/'
|
|
305
|
+
| '/mail/$mailboxId/$threadId/$messageId'
|
|
266
306
|
| '/mail/brief/$threadId/$messageId'
|
|
307
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
308
|
+
| '/mail/$mailboxId/$threadId/'
|
|
267
309
|
| '/mail/brief/$threadId/'
|
|
268
310
|
fileRoutesByTo: FileRoutesByTo
|
|
269
311
|
to:
|
|
@@ -283,7 +325,10 @@ export interface FileRouteTypes {
|
|
|
283
325
|
| '/mail/brief'
|
|
284
326
|
| '/mail/flagged'
|
|
285
327
|
| '/mail/outbox'
|
|
328
|
+
| '/mail/$mailboxId/$threadId/$messageId'
|
|
286
329
|
| '/mail/brief/$threadId/$messageId'
|
|
330
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
331
|
+
| '/mail/$mailboxId/$threadId'
|
|
287
332
|
| '/mail/brief/$threadId'
|
|
288
333
|
id:
|
|
289
334
|
| '__root__'
|
|
@@ -305,12 +350,16 @@ export interface FileRouteTypes {
|
|
|
305
350
|
| '/settings/suggested-vips'
|
|
306
351
|
| '/mail/'
|
|
307
352
|
| '/settings/'
|
|
353
|
+
| '/mail/$mailboxId/$threadId'
|
|
308
354
|
| '/mail/brief/$threadId'
|
|
309
355
|
| '/mail/$mailboxId/'
|
|
310
356
|
| '/mail/brief/'
|
|
311
357
|
| '/mail/flagged/'
|
|
312
358
|
| '/mail/outbox/'
|
|
359
|
+
| '/mail/$mailboxId/$threadId/$messageId'
|
|
313
360
|
| '/mail/brief/$threadId/$messageId'
|
|
361
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
362
|
+
| '/mail/$mailboxId/$threadId/'
|
|
314
363
|
| '/mail/brief/$threadId/'
|
|
315
364
|
fileRoutesById: FileRoutesById
|
|
316
365
|
}
|
|
@@ -456,6 +505,13 @@ declare module '@tanstack/react-router' {
|
|
|
456
505
|
preLoaderRoute: typeof MailMailboxIdIndexRouteImport
|
|
457
506
|
parentRoute: typeof MailMailboxIdRoute
|
|
458
507
|
}
|
|
508
|
+
'/mail/$mailboxId/$threadId': {
|
|
509
|
+
id: '/mail/$mailboxId/$threadId'
|
|
510
|
+
path: '/$threadId'
|
|
511
|
+
fullPath: '/mail/$mailboxId/$threadId'
|
|
512
|
+
preLoaderRoute: typeof MailMailboxIdThreadIdRouteImport
|
|
513
|
+
parentRoute: typeof MailMailboxIdRoute
|
|
514
|
+
}
|
|
459
515
|
'/mail/brief/': {
|
|
460
516
|
id: '/mail/brief/'
|
|
461
517
|
path: '/'
|
|
@@ -484,6 +540,20 @@ declare module '@tanstack/react-router' {
|
|
|
484
540
|
preLoaderRoute: typeof MailOutboxIndexRouteImport
|
|
485
541
|
parentRoute: typeof MailOutboxRoute
|
|
486
542
|
}
|
|
543
|
+
'/mail/$mailboxId/$threadId/': {
|
|
544
|
+
id: '/mail/$mailboxId/$threadId/'
|
|
545
|
+
path: '/'
|
|
546
|
+
fullPath: '/mail/$mailboxId/$threadId/'
|
|
547
|
+
preLoaderRoute: typeof MailMailboxIdThreadIdIndexRouteImport
|
|
548
|
+
parentRoute: typeof MailMailboxIdThreadIdRoute
|
|
549
|
+
}
|
|
550
|
+
'/mail/$mailboxId/$threadId/$messageId': {
|
|
551
|
+
id: '/mail/$mailboxId/$threadId/$messageId'
|
|
552
|
+
path: '/$messageId'
|
|
553
|
+
fullPath: '/mail/$mailboxId/$threadId/$messageId'
|
|
554
|
+
preLoaderRoute: typeof MailMailboxIdThreadIdMessageIdRouteImport
|
|
555
|
+
parentRoute: typeof MailMailboxIdThreadIdRoute
|
|
556
|
+
}
|
|
487
557
|
'/mail/brief/$threadId/': {
|
|
488
558
|
id: '/mail/brief/$threadId/'
|
|
489
559
|
path: '/'
|
|
@@ -498,14 +568,38 @@ declare module '@tanstack/react-router' {
|
|
|
498
568
|
preLoaderRoute: typeof MailBriefThreadIdMessageIdRouteImport
|
|
499
569
|
parentRoute: typeof MailBriefThreadIdRoute
|
|
500
570
|
}
|
|
571
|
+
'/mail/outbox/draft/$outboxMessageId': {
|
|
572
|
+
id: '/mail/outbox/draft/$outboxMessageId'
|
|
573
|
+
path: '/draft/$outboxMessageId'
|
|
574
|
+
fullPath: '/mail/outbox/draft/$outboxMessageId'
|
|
575
|
+
preLoaderRoute: typeof MailOutboxDraftOutboxMessageIdRouteImport
|
|
576
|
+
parentRoute: typeof MailOutboxRoute
|
|
577
|
+
}
|
|
501
578
|
}
|
|
502
579
|
}
|
|
503
580
|
|
|
581
|
+
interface MailMailboxIdThreadIdRouteChildren {
|
|
582
|
+
MailMailboxIdThreadIdMessageIdRoute: typeof MailMailboxIdThreadIdMessageIdRoute
|
|
583
|
+
MailMailboxIdThreadIdIndexRoute: typeof MailMailboxIdThreadIdIndexRoute
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const MailMailboxIdThreadIdRouteChildren: MailMailboxIdThreadIdRouteChildren = {
|
|
587
|
+
MailMailboxIdThreadIdMessageIdRoute: MailMailboxIdThreadIdMessageIdRoute,
|
|
588
|
+
MailMailboxIdThreadIdIndexRoute: MailMailboxIdThreadIdIndexRoute,
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const MailMailboxIdThreadIdRouteWithChildren =
|
|
592
|
+
MailMailboxIdThreadIdRoute._addFileChildren(
|
|
593
|
+
MailMailboxIdThreadIdRouteChildren,
|
|
594
|
+
)
|
|
595
|
+
|
|
504
596
|
interface MailMailboxIdRouteChildren {
|
|
597
|
+
MailMailboxIdThreadIdRoute: typeof MailMailboxIdThreadIdRouteWithChildren
|
|
505
598
|
MailMailboxIdIndexRoute: typeof MailMailboxIdIndexRoute
|
|
506
599
|
}
|
|
507
600
|
|
|
508
601
|
const MailMailboxIdRouteChildren: MailMailboxIdRouteChildren = {
|
|
602
|
+
MailMailboxIdThreadIdRoute: MailMailboxIdThreadIdRouteWithChildren,
|
|
509
603
|
MailMailboxIdIndexRoute: MailMailboxIdIndexRoute,
|
|
510
604
|
}
|
|
511
605
|
|
|
@@ -554,10 +648,12 @@ const MailFlaggedRouteWithChildren = MailFlaggedRoute._addFileChildren(
|
|
|
554
648
|
|
|
555
649
|
interface MailOutboxRouteChildren {
|
|
556
650
|
MailOutboxIndexRoute: typeof MailOutboxIndexRoute
|
|
651
|
+
MailOutboxDraftOutboxMessageIdRoute: typeof MailOutboxDraftOutboxMessageIdRoute
|
|
557
652
|
}
|
|
558
653
|
|
|
559
654
|
const MailOutboxRouteChildren: MailOutboxRouteChildren = {
|
|
560
655
|
MailOutboxIndexRoute: MailOutboxIndexRoute,
|
|
656
|
+
MailOutboxDraftOutboxMessageIdRoute: MailOutboxDraftOutboxMessageIdRoute,
|
|
561
657
|
}
|
|
562
658
|
|
|
563
659
|
const MailOutboxRouteWithChildren = MailOutboxRoute._addFileChildren(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /mail/$mailboxId/$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 { MailboxPane } from "@/components/mail/MailboxPane";
|
|
12
|
+
|
|
13
|
+
function MailboxMessagePane() {
|
|
14
|
+
return <MailboxPane.Reading />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Route = createFileRoute("/mail/$mailboxId/$threadId/$messageId")({
|
|
18
|
+
component: MailboxMessagePane,
|
|
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 { MailboxPane } from "@/components/mail/MailboxPane";
|
|
7
|
+
|
|
8
|
+
function MailboxThreadPane() {
|
|
9
|
+
return <MailboxPane.Reading />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Route = createFileRoute("/mail/$mailboxId/$threadId/")({
|
|
13
|
+
component: MailboxThreadPane,
|
|
14
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /mail/$mailboxId/$threadId — a conversation open in a folder's reading pane.
|
|
3
|
+
*
|
|
4
|
+
* The folder in the address is the list being browsed, not the thread's home:
|
|
5
|
+
* a cross-folder search hit opens here too, and every message it holds names
|
|
6
|
+
* its own mailbox. A layout, because the message segment nests under it.
|
|
7
|
+
*/
|
|
8
|
+
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
|
9
|
+
|
|
10
|
+
export const Route = createFileRoute("/mail/$mailboxId/$threadId")({
|
|
11
|
+
component: Outlet,
|
|
12
|
+
});
|