@remit/ui 0.0.91 → 0.0.92
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
CHANGED
|
@@ -36,6 +36,19 @@ export const FilterMoveWithManageLink: Story = {
|
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* A user-reported spam message (issue #648). Independent of the classifier/
|
|
41
|
+
* filter-move shapes above: the badge follows the message wherever it now
|
|
42
|
+
* lives, including a report that never moved the message at all (it was
|
|
43
|
+
* already in Junk).
|
|
44
|
+
*/
|
|
45
|
+
export const ReportedAsSpam: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
label: "Reported as spam",
|
|
48
|
+
onUndo: () => alert("Undo"),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
39
52
|
export const SideBySide: Story = {
|
|
40
53
|
render: () => (
|
|
41
54
|
<div className="flex flex-col items-start gap-3">
|
|
@@ -198,31 +198,70 @@ export const WithSimilarMessages: Story = {
|
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
|
-
* The spam quick actions are
|
|
202
|
-
*
|
|
201
|
+
* The spam quick actions are a contextual pair, decided by whether the message
|
|
202
|
+
* carries a spam report — never by the mailbox it happens to sit in, since a
|
|
203
|
+
* report on a message already in Junk (the provider's own filter put it there)
|
|
204
|
+
* is a real, no-op-move case (issue #648). A reportable message offers
|
|
205
|
+
* "Report spam".
|
|
203
206
|
*/
|
|
204
|
-
export const
|
|
207
|
+
export const Reportable: Story = {
|
|
205
208
|
args: {
|
|
206
209
|
data: base,
|
|
207
|
-
actions: {
|
|
210
|
+
actions: { onReportSpam: () => {} },
|
|
208
211
|
},
|
|
209
212
|
};
|
|
210
213
|
|
|
211
|
-
/**
|
|
212
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Already reported: "Not spam" (the undo) is offered instead of "Report
|
|
216
|
+
* spam", and the panel names the message as reported. Driven by
|
|
217
|
+
* `actions.onNotSpam` being present, not by `flags.blocked` — a sender can be
|
|
218
|
+
* blocked manually, with no report on this particular message, and that must
|
|
219
|
+
* not read as "you reported this" (issue #648 review).
|
|
220
|
+
*/
|
|
221
|
+
export const Reported: Story = {
|
|
213
222
|
args: {
|
|
214
223
|
data: base,
|
|
215
|
-
actions: {
|
|
224
|
+
actions: { onNotSpam: () => {} },
|
|
216
225
|
},
|
|
217
226
|
};
|
|
218
227
|
|
|
219
228
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
229
|
+
* Neither action is offered. The panel hides the pair rather than disabling
|
|
230
|
+
* it — unlike VIP/Mute/Unsubscribe, which always render and go visibly
|
|
231
|
+
* unavailable with no handler (issue #51). The host's own wiring never
|
|
232
|
+
* actually reaches this: `resolveSpamAction` always returns one of the two,
|
|
233
|
+
* since every message either carries a spam report or doesn't. Kept as a
|
|
234
|
+
* defensive state for a host that doesn't wire the pair at all.
|
|
222
235
|
*/
|
|
223
|
-
export const
|
|
236
|
+
export const SpamActionUnavailable: Story = {
|
|
224
237
|
args: {
|
|
225
238
|
data: base,
|
|
226
239
|
actions: {},
|
|
227
240
|
},
|
|
228
241
|
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* A "Report spam" press in flight. There's no optimistic update for this
|
|
245
|
+
* action (a report against a message already in Junk is a real no-op-move,
|
|
246
|
+
* issue #648), so without a pending label the button gives no visible
|
|
247
|
+
* response at all until the request lands — the dead-button failure the
|
|
248
|
+
* coding standards call the worst outcome. The button stays clickable
|
|
249
|
+
* throughout, same as the undo direction's `isUndoing`: the operation is
|
|
250
|
+
* idempotent, so a second press is safe, never a queued duplicate.
|
|
251
|
+
*/
|
|
252
|
+
export const ReportSpamPending: Story = {
|
|
253
|
+
args: {
|
|
254
|
+
data: base,
|
|
255
|
+
actions: { onReportSpam: () => {} },
|
|
256
|
+
reportSpamPending: true,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/** The undo direction's equivalent — "Undoing…" while `notSpam` is in flight. */
|
|
261
|
+
export const NotSpamPending: Story = {
|
|
262
|
+
args: {
|
|
263
|
+
data: base,
|
|
264
|
+
actions: { onNotSpam: () => {} },
|
|
265
|
+
notSpamPending: true,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Ban,
|
|
3
2
|
BellOff,
|
|
4
3
|
MailCheck,
|
|
5
4
|
MailX,
|
|
@@ -112,6 +111,16 @@ export type SimilarMessageLinkComponent = (
|
|
|
112
111
|
export interface SenderFlagsIntel {
|
|
113
112
|
vip?: boolean;
|
|
114
113
|
muted?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* True once the sender is blocked — via a spam report on some message from
|
|
116
|
+
* them, or a manual block in Settings → Senders, which is the only durable
|
|
117
|
+
* manage-and-undo surface for it now that this panel has no `Block` action
|
|
118
|
+
* of its own. Not used to decide the quick-actions pair below: it's a
|
|
119
|
+
* sender-wide flag, and a manual block with no report on THIS message would
|
|
120
|
+
* make "You reported this sender" false (issue #648 review). The pair and
|
|
121
|
+
* that line key off `actions.onNotSpam`/`onReportSpam` instead, which the
|
|
122
|
+
* host derives from this specific message's `Message.spamReport`.
|
|
123
|
+
*/
|
|
115
124
|
blocked?: boolean;
|
|
116
125
|
unsubscribed?: boolean;
|
|
117
126
|
}
|
|
@@ -127,20 +136,21 @@ export interface IntelligenceData {
|
|
|
127
136
|
export interface IntelligenceQuickActions {
|
|
128
137
|
onToggleVip?: () => void;
|
|
129
138
|
onToggleMute?: () => void;
|
|
130
|
-
/** Block navigates through a confirm dialog — the callback fires post-confirm. */
|
|
131
|
-
onToggleBlock?: () => void;
|
|
132
139
|
onToggleUnsubscribe?: () => void;
|
|
133
140
|
onReclassify?: () => void;
|
|
134
141
|
/**
|
|
135
|
-
* "Not spam":
|
|
136
|
-
*
|
|
142
|
+
* "Not spam": undo a spam report — clears the sender block and moves the
|
|
143
|
+
* message back where it came from (issue #648). Wired only when the
|
|
144
|
+
* message currently carries a spam report.
|
|
137
145
|
*/
|
|
138
146
|
onNotSpam?: () => void;
|
|
139
147
|
/**
|
|
140
|
-
* "
|
|
141
|
-
*
|
|
148
|
+
* "Report spam": move the message into Junk and block the sender (the
|
|
149
|
+
* inverse of "Not spam", issue #648). Wired only when the message has not
|
|
150
|
+
* been reported yet. Replaces the former separate `Block` and `Mark spam`
|
|
151
|
+
* actions with one contextual control.
|
|
142
152
|
*/
|
|
143
|
-
|
|
153
|
+
onReportSpam?: () => void;
|
|
144
154
|
}
|
|
145
155
|
|
|
146
156
|
/**
|
|
@@ -173,6 +183,19 @@ export interface IntelligencePanelProps {
|
|
|
173
183
|
* Drawer header), so there is exactly one way back (#874).
|
|
174
184
|
*/
|
|
175
185
|
hideCloseButton?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* True while a "Report spam" press is in flight. There is no optimistic
|
|
188
|
+
* update for this action (issue #648: the client can't predict whether a
|
|
189
|
+
* report against a message already in Junk is a no-op), so without this
|
|
190
|
+
* the button gives no visible response at all until the request lands — a
|
|
191
|
+
* dead button is the worst outcome. Swaps the label to "Reporting…"; the
|
|
192
|
+
* handler stays wired throughout (same as `AutoMovedBadge`'s `isUndoing`)
|
|
193
|
+
* because the operation is idempotent, so a second press mid-flight is
|
|
194
|
+
* safe rather than a queued duplicate.
|
|
195
|
+
*/
|
|
196
|
+
reportSpamPending?: boolean;
|
|
197
|
+
/** True while a "Not spam" (undo) press is in flight. Same treatment as `reportSpamPending`. */
|
|
198
|
+
notSpamPending?: boolean;
|
|
176
199
|
}
|
|
177
200
|
|
|
178
201
|
const trustLabel: Record<
|
|
@@ -362,6 +385,8 @@ export function IntelligencePanel({
|
|
|
362
385
|
similarLinkComponent,
|
|
363
386
|
className,
|
|
364
387
|
hideCloseButton = false,
|
|
388
|
+
reportSpamPending = false,
|
|
389
|
+
notSpamPending = false,
|
|
365
390
|
}: IntelligencePanelProps) {
|
|
366
391
|
const { sender, authenticity, category, flags = {}, similar } = data;
|
|
367
392
|
const suspicious = authenticity.verdict === "mismatch";
|
|
@@ -434,35 +459,34 @@ export function IntelligencePanel({
|
|
|
434
459
|
active={flags.muted}
|
|
435
460
|
onClick={actions?.onToggleMute}
|
|
436
461
|
/>
|
|
437
|
-
<QuickAction
|
|
438
|
-
icon={<Ban className="size-3.5" />}
|
|
439
|
-
label="Block"
|
|
440
|
-
active={flags.blocked}
|
|
441
|
-
danger
|
|
442
|
-
onClick={actions?.onToggleBlock}
|
|
443
|
-
/>
|
|
444
|
-
<QuickAction
|
|
445
|
-
icon={<MailX className="size-3.5" />}
|
|
446
|
-
label="Unsubscribe"
|
|
447
|
-
active={flags.unsubscribed}
|
|
448
|
-
onClick={actions?.onToggleUnsubscribe}
|
|
449
|
-
/>
|
|
450
462
|
{actions?.onNotSpam && (
|
|
451
463
|
<QuickAction
|
|
452
464
|
icon={<MailCheck className="size-3.5" />}
|
|
453
|
-
label="Not spam"
|
|
465
|
+
label={notSpamPending ? "Undoing…" : "Not spam"}
|
|
466
|
+
active
|
|
454
467
|
onClick={actions.onNotSpam}
|
|
455
468
|
/>
|
|
456
469
|
)}
|
|
457
|
-
{actions?.
|
|
470
|
+
{actions?.onReportSpam && (
|
|
458
471
|
<QuickAction
|
|
459
472
|
icon={<ShieldX className="size-3.5" />}
|
|
460
|
-
label="
|
|
473
|
+
label={reportSpamPending ? "Reporting…" : "Report spam"}
|
|
461
474
|
danger
|
|
462
|
-
onClick={actions.
|
|
475
|
+
onClick={actions.onReportSpam}
|
|
463
476
|
/>
|
|
464
477
|
)}
|
|
478
|
+
<QuickAction
|
|
479
|
+
icon={<MailX className="size-3.5" />}
|
|
480
|
+
label="Unsubscribe"
|
|
481
|
+
active={flags.unsubscribed}
|
|
482
|
+
onClick={actions?.onToggleUnsubscribe}
|
|
483
|
+
/>
|
|
465
484
|
</div>
|
|
485
|
+
{actions?.onNotSpam && (
|
|
486
|
+
<p className="mt-1.5 text-2xs text-fg-subtle">
|
|
487
|
+
You reported this message as spam
|
|
488
|
+
</p>
|
|
489
|
+
)}
|
|
466
490
|
</Section>
|
|
467
491
|
|
|
468
492
|
{(similarState !== "ready" || similar.length > 0) && (
|