@remit/web-client 0.0.149 → 0.0.150
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/MailSidebarAdapter.tsx +1 -1
- package/src/components/mail/OutboxPane.tsx +31 -28
- package/src/lib/mail-search.ts +6 -3
- package/src/routeTree.gen.ts +22 -0
- package/src/routes/mail/outbox/draft/$outboxMessageId.tsx +19 -0
- package/src/routes/mail/outbox.tsx +6 -1
- package/src/routing/index.ts +1 -0
- package/src/routing/outbox-draft.ts +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.150",
|
|
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": {
|
|
@@ -244,7 +244,7 @@ export function MailSidebarAdapter({
|
|
|
244
244
|
return (
|
|
245
245
|
<NavLink
|
|
246
246
|
to="/mail/outbox"
|
|
247
|
-
search={{ q: undefined
|
|
247
|
+
search={{ q: undefined }}
|
|
248
248
|
onClick={() => onClick?.()}
|
|
249
249
|
className={className}
|
|
250
250
|
aria-label={ariaLabel}
|
|
@@ -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>
|
package/src/lib/mail-search.ts
CHANGED
|
@@ -24,9 +24,12 @@ 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
34
|
export const mailboxSearchSchema = listSearch.extend({
|
|
32
35
|
selectedMessageId: z.string().optional(),
|
package/src/routeTree.gen.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { Route as MailFlaggedIndexRouteImport } from './routes/mail/flagged/inde
|
|
|
34
34
|
import { Route as MailOutboxIndexRouteImport } from './routes/mail/outbox/index'
|
|
35
35
|
import { Route as MailBriefThreadIdIndexRouteImport } from './routes/mail/brief/$threadId/index'
|
|
36
36
|
import { Route as MailBriefThreadIdMessageIdRouteImport } from './routes/mail/brief/$threadId/$messageId'
|
|
37
|
+
import { Route as MailOutboxDraftOutboxMessageIdRouteImport } from './routes/mail/outbox/draft/$outboxMessageId'
|
|
37
38
|
|
|
38
39
|
const IndexRoute = IndexRouteImport.update({
|
|
39
40
|
id: '/',
|
|
@@ -161,6 +162,12 @@ const MailBriefThreadIdMessageIdRoute =
|
|
|
161
162
|
path: '/$messageId',
|
|
162
163
|
getParentRoute: () => MailBriefThreadIdRoute,
|
|
163
164
|
} as any)
|
|
165
|
+
const MailOutboxDraftOutboxMessageIdRoute =
|
|
166
|
+
MailOutboxDraftOutboxMessageIdRouteImport.update({
|
|
167
|
+
id: '/draft/$outboxMessageId',
|
|
168
|
+
path: '/draft/$outboxMessageId',
|
|
169
|
+
getParentRoute: () => MailOutboxRoute,
|
|
170
|
+
} as any)
|
|
164
171
|
|
|
165
172
|
export interface FileRoutesByFullPath {
|
|
166
173
|
'/': typeof IndexRoute
|
|
@@ -187,6 +194,7 @@ export interface FileRoutesByFullPath {
|
|
|
187
194
|
'/mail/flagged/': typeof MailFlaggedIndexRoute
|
|
188
195
|
'/mail/outbox/': typeof MailOutboxIndexRoute
|
|
189
196
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
197
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
190
198
|
'/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
|
|
191
199
|
}
|
|
192
200
|
export interface FileRoutesByTo {
|
|
@@ -207,6 +215,7 @@ export interface FileRoutesByTo {
|
|
|
207
215
|
'/mail/flagged': typeof MailFlaggedIndexRoute
|
|
208
216
|
'/mail/outbox': typeof MailOutboxIndexRoute
|
|
209
217
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
218
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
210
219
|
'/mail/brief/$threadId': typeof MailBriefThreadIdIndexRoute
|
|
211
220
|
}
|
|
212
221
|
export interface FileRoutesById {
|
|
@@ -235,6 +244,7 @@ export interface FileRoutesById {
|
|
|
235
244
|
'/mail/flagged/': typeof MailFlaggedIndexRoute
|
|
236
245
|
'/mail/outbox/': typeof MailOutboxIndexRoute
|
|
237
246
|
'/mail/brief/$threadId/$messageId': typeof MailBriefThreadIdMessageIdRoute
|
|
247
|
+
'/mail/outbox/draft/$outboxMessageId': typeof MailOutboxDraftOutboxMessageIdRoute
|
|
238
248
|
'/mail/brief/$threadId/': typeof MailBriefThreadIdIndexRoute
|
|
239
249
|
}
|
|
240
250
|
export interface FileRouteTypes {
|
|
@@ -264,6 +274,7 @@ export interface FileRouteTypes {
|
|
|
264
274
|
| '/mail/flagged/'
|
|
265
275
|
| '/mail/outbox/'
|
|
266
276
|
| '/mail/brief/$threadId/$messageId'
|
|
277
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
267
278
|
| '/mail/brief/$threadId/'
|
|
268
279
|
fileRoutesByTo: FileRoutesByTo
|
|
269
280
|
to:
|
|
@@ -284,6 +295,7 @@ export interface FileRouteTypes {
|
|
|
284
295
|
| '/mail/flagged'
|
|
285
296
|
| '/mail/outbox'
|
|
286
297
|
| '/mail/brief/$threadId/$messageId'
|
|
298
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
287
299
|
| '/mail/brief/$threadId'
|
|
288
300
|
id:
|
|
289
301
|
| '__root__'
|
|
@@ -311,6 +323,7 @@ export interface FileRouteTypes {
|
|
|
311
323
|
| '/mail/flagged/'
|
|
312
324
|
| '/mail/outbox/'
|
|
313
325
|
| '/mail/brief/$threadId/$messageId'
|
|
326
|
+
| '/mail/outbox/draft/$outboxMessageId'
|
|
314
327
|
| '/mail/brief/$threadId/'
|
|
315
328
|
fileRoutesById: FileRoutesById
|
|
316
329
|
}
|
|
@@ -498,6 +511,13 @@ declare module '@tanstack/react-router' {
|
|
|
498
511
|
preLoaderRoute: typeof MailBriefThreadIdMessageIdRouteImport
|
|
499
512
|
parentRoute: typeof MailBriefThreadIdRoute
|
|
500
513
|
}
|
|
514
|
+
'/mail/outbox/draft/$outboxMessageId': {
|
|
515
|
+
id: '/mail/outbox/draft/$outboxMessageId'
|
|
516
|
+
path: '/draft/$outboxMessageId'
|
|
517
|
+
fullPath: '/mail/outbox/draft/$outboxMessageId'
|
|
518
|
+
preLoaderRoute: typeof MailOutboxDraftOutboxMessageIdRouteImport
|
|
519
|
+
parentRoute: typeof MailOutboxRoute
|
|
520
|
+
}
|
|
501
521
|
}
|
|
502
522
|
}
|
|
503
523
|
|
|
@@ -554,10 +574,12 @@ const MailFlaggedRouteWithChildren = MailFlaggedRoute._addFileChildren(
|
|
|
554
574
|
|
|
555
575
|
interface MailOutboxRouteChildren {
|
|
556
576
|
MailOutboxIndexRoute: typeof MailOutboxIndexRoute
|
|
577
|
+
MailOutboxDraftOutboxMessageIdRoute: typeof MailOutboxDraftOutboxMessageIdRoute
|
|
557
578
|
}
|
|
558
579
|
|
|
559
580
|
const MailOutboxRouteChildren: MailOutboxRouteChildren = {
|
|
560
581
|
MailOutboxIndexRoute: MailOutboxIndexRoute,
|
|
582
|
+
MailOutboxDraftOutboxMessageIdRoute: MailOutboxDraftOutboxMessageIdRoute,
|
|
561
583
|
}
|
|
562
584
|
|
|
563
585
|
const MailOutboxRouteWithChildren = MailOutboxRoute._addFileChildren(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /mail/outbox/draft/$outboxMessageId — one outbox message open in the
|
|
3
|
+
* reading pane.
|
|
4
|
+
*
|
|
5
|
+
* An outbox message is addressable on its own: it belongs to no thread and no
|
|
6
|
+
* folder, so the id is the whole address. The `draft/` prefix keeps the id in a
|
|
7
|
+
* named segment, so the literal siblings the outbox will grow stay
|
|
8
|
+
* distinguishable from it.
|
|
9
|
+
*/
|
|
10
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
11
|
+
import { OutboxPane } from "@/components/mail/OutboxPane";
|
|
12
|
+
|
|
13
|
+
function OutboxDraftPane() {
|
|
14
|
+
return <OutboxPane.Reading />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Route = createFileRoute("/mail/outbox/draft/$outboxMessageId")({
|
|
18
|
+
component: OutboxDraftPane,
|
|
19
|
+
});
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* /mail/outbox — one of the four list layouts. Two panes, list and reading;
|
|
3
3
|
* there is no intelligence rail for mail that has not been sent yet.
|
|
4
|
+
*
|
|
5
|
+
* The open message is the segment below this one, which is why nothing here
|
|
6
|
+
* reads a selection out of the query.
|
|
4
7
|
*/
|
|
5
8
|
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
|
6
9
|
import { MailShell } from "@/components/layout/MailShell";
|
|
7
10
|
import { OutboxPane } from "@/components/mail/OutboxPane";
|
|
8
11
|
import { useSearchMirror } from "@/hooks/useSearchMirror";
|
|
9
12
|
import { outboxSearchSchema } from "@/lib/mail-search";
|
|
13
|
+
import { useOutboxDraftId } from "@/routing";
|
|
10
14
|
|
|
11
15
|
function OutboxLayout() {
|
|
16
|
+
const outboxMessageId = useOutboxDraftId();
|
|
12
17
|
useSearchMirror({ to: "/mail/outbox" });
|
|
13
18
|
|
|
14
19
|
return (
|
|
15
|
-
<OutboxPane>
|
|
20
|
+
<OutboxPane outboxMessageId={outboxMessageId}>
|
|
16
21
|
<MailShell
|
|
17
22
|
phone={<OutboxPane.Phone />}
|
|
18
23
|
list={<OutboxPane.List />}
|
package/src/routing/index.ts
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useParams } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The outbox message the reader has open, read off the path.
|
|
5
|
+
*
|
|
6
|
+
* The message is a child route of the list, and the list layout mounts the
|
|
7
|
+
* panes above the `Outlet` — so it asks the router which of its children
|
|
8
|
+
* matched rather than reading a param it does not own. The `from` names a real
|
|
9
|
+
* route, so a segment that does not exist fails to compile.
|
|
10
|
+
*/
|
|
11
|
+
export function useOutboxDraftId(): string | undefined {
|
|
12
|
+
const draft = useParams({
|
|
13
|
+
from: "/mail/outbox/draft/$outboxMessageId",
|
|
14
|
+
shouldThrow: false,
|
|
15
|
+
});
|
|
16
|
+
return draft?.outboxMessageId;
|
|
17
|
+
}
|