@chatwarden/player-react 0.1.2 → 0.1.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHL,CAAC;AAEX,sFAAsF;AACtF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7D,KAAK,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AACpE,KAAK,OAAO,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAE9E,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AAErE,oFAAoF;AACpF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC;AAIzE;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAO,GAAG,OAAO,CAiCtE"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoEb,+DAA+D;;IAE/D,+DAA+D;;;IAG/D,+DAA+D;;;;;;;;;;IAU/D,+DAA+D;;;IAG/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;IAE/D,+DAA+D;;;;;;;;;IAS/D,+DAA+D;;;;;;;;;;;;;;;;;;CAkBvD,CAAC;AAEX,sFAAsF;AACtF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7D,KAAK,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AACpE,KAAK,OAAO,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAE9E,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AAErE,oFAAoF;AACpF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC;AAIzE;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAO,GAAG,OAAO,CAiCtE"}
package/dist/src/i18n.js CHANGED
@@ -70,9 +70,12 @@ export const en = {
70
70
  'player.restricted.filter.noEnd': 'Your chat is off. It does not end on its own. The automatic filter caught a message.',
71
71
  'player.restricted.ended': 'Your chat is back on. The restriction ended {when}.',
72
72
  'player.restricted.ref': 'Reference {code}',
73
+ /** @deprecated unused since 0.1.3; removed in a later minor */
73
74
  'player.report.title': 'Report a player',
75
+ /** @deprecated unused since 0.1.3; removed in a later minor */
74
76
  'player.report.titleNamed': 'Report {name}',
75
- 'player.report.intro': 'Tell us what happened and a moderator will look at it.',
77
+ 'player.report.intro': 'Tell us why and a moderator will look at it.',
78
+ /** @deprecated unused since 0.1.3; removed in a later minor */
76
79
  'player.report.legend': 'What happened?',
77
80
  'player.report.titleOneTap': 'Report this message?',
78
81
  'player.report.titleOneTapNamed': 'Report {name}?',
@@ -82,24 +85,45 @@ export const en = {
82
85
  'player.report.oneTap.intro': 'We will pass it to the moderators. You do not need to say why.',
83
86
  'player.report.oneTap.send': 'Report',
84
87
  'player.report.quote': '“{line}”',
88
+ /** @deprecated unused since 0.1.3; removed in a later minor */
85
89
  'player.report.skip': 'Skip and report',
86
90
  'player.report.needReason': 'Choose a reason.',
91
+ /** @deprecated unused since 0.1.3; removed in a later minor */
87
92
  'player.report.needReasonOrSkip': 'Choose what happened, or skip.',
93
+ /** @deprecated unused since 0.1.3; removed in a later minor */
88
94
  'player.report.addReason': 'Add a reason',
95
+ /** @deprecated unused since 0.1.3; removed in a later minor */
89
96
  'player.report.addReasonHint': 'This helps, but the moderators decide what it really is. You can add this for a few minutes after you report.',
97
+ /** @deprecated unused since 0.1.3; removed in a later minor */
90
98
  'player.report.detail.legend': 'What was it about?',
99
+ /** @deprecated unused since 0.1.3; removed in a later minor */
91
100
  'player.report.detail.send': 'Add to my report',
101
+ /** @deprecated unused since 0.1.3; removed in a later minor */
92
102
  'player.report.detail.need': 'Choose a reason or write a note.',
103
+ /** @deprecated unused since 0.1.3; removed in a later minor */
93
104
  'player.report.detail.added': 'Added. Thanks.',
105
+ /** @deprecated unused since 0.1.3; removed in a later minor */
94
106
  'player.report.detail.failed': 'We could not add that.',
107
+ /** @deprecated unused since 0.1.3; removed in a later minor */
95
108
  'player.report.detail.refused.off': 'Adding to a report is not available right now.',
109
+ /** @deprecated unused since 0.1.3; removed in a later minor */
96
110
  'player.report.detail.refused.not_found': 'We cannot find that report any more.',
111
+ /** @deprecated unused since 0.1.3; removed in a later minor */
97
112
  'player.report.detail.refused.not_reporter': 'Only the player who sent a report can add to it.',
113
+ /** @deprecated unused since 0.1.3; removed in a later minor */
98
114
  'player.report.detail.refused.too_late': 'It is too late to add to this report. We still have the report.',
115
+ /** @deprecated unused since 0.1.3; removed in a later minor */
99
116
  'player.report.detail.refused.already_added': 'You already added to this report.',
117
+ /** @deprecated unused since 0.1.3; removed in a later minor */
100
118
  'player.report.detail.refused.reason_unknown': 'That reason is not available any more. Your report is still with us.',
101
- 'player.report.note': 'Add a note (optional)',
119
+ 'player.report.note': 'Note (optional)',
120
+ 'player.report.addNote': 'Add a note',
121
+ 'player.report.why': 'Why? (optional)',
122
+ 'player.report.whyRequired': 'Why?',
123
+ 'player.report.choose': 'Choose a reason (optional)',
124
+ 'player.report.chooseRequired': 'Choose a reason',
102
125
  'player.report.noteHint': 'A moderator will read this. Up to {max} characters. Do not include passwords or personal details.',
126
+ /** @deprecated unused since 0.1.3; removed in a later minor */
103
127
  'player.report.send': 'Send report',
104
128
  'player.report.cancel': 'Cancel',
105
129
  'player.report.close': 'Close',
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.1.2";
1
+ export declare const VERSION = "0.1.3";
2
2
  export { NoticeQueue } from './NoticeQueue.js';
3
3
  export type { NoticeQueueProps } from './NoticeQueue.js';
4
4
  export { MyRecord } from './MyRecord.js';
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = '0.1.2';
1
+ export const VERSION = '0.1.3';
2
2
  export { NoticeQueue } from './NoticeQueue.js';
3
3
  export { MyRecord } from './MyRecord.js';
4
4
  export { RulesPage } from './RulesPage.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatwarden/player-react",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Player-facing React components for Chatwarden: the notice dialog, My record, the rules page, a report dialog and a restricted-chat line, over the player HTTP routes.",
6
6
  "license": "Apache-2.0",
@@ -52,10 +52,10 @@
52
52
  "react-dom": "^18.3.0 || ^19.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@chatwarden/core": "0.1.2",
56
- "@chatwarden/http": "0.1.2",
57
- "@chatwarden/hub-react": "0.1.2",
58
- "@chatwarden/store-memory": "0.1.2",
55
+ "@chatwarden/core": "0.1.3",
56
+ "@chatwarden/http": "0.1.3",
57
+ "@chatwarden/hub-react": "0.1.3",
58
+ "@chatwarden/store-memory": "0.1.3",
59
59
  "@testing-library/dom": "^10.4.2",
60
60
  "@testing-library/jest-dom": "^7.0.1",
61
61
  "@testing-library/react": "^16.3.3",
package/src/Dialog.tsx CHANGED
@@ -11,18 +11,8 @@ import type { KeyboardEvent, ReactNode, RefObject } from 'react';
11
11
 
12
12
  const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
13
13
 
14
- /**
15
- * The controls Tab visits, in order. A radio group is one stop (the checked radio, or the first when none is checked), because the
16
- * browser moves between its radios with the arrow keys: counting every radio would make "the first control" the wrong one to wrap at.
17
- */
18
- function tabStops(box: HTMLElement): HTMLElement[] {
19
- const all = [...box.querySelectorAll<HTMLElement>(FOCUSABLE)];
20
- return all.filter((el) => {
21
- if (!(el instanceof HTMLInputElement) || el.type !== 'radio' || el.name === '') return true;
22
- const group = all.filter((o): o is HTMLInputElement => o instanceof HTMLInputElement && o.type === 'radio' && o.name === el.name);
23
- return el === (group.find((r) => r.checked) ?? group[0]);
24
- });
25
- }
14
+ /** The controls Tab visits, in order. */
15
+ const tabStops = (box: HTMLElement): HTMLElement[] => [...box.querySelectorAll<HTMLElement>(FOCUSABLE)];
26
16
 
27
17
  export function Dialog({
28
18
  labelledBy,
@@ -1,22 +1,18 @@
1
1
  import { useEffect, useId, useRef, useState } from 'react';
2
- import type { FormEvent } from 'react';
3
- import type { PlayerReporting, ReportDetailResult, ReportFlow, ReportRefusal, ReportResult } from '@chatwarden/core';
2
+ import type { FormEvent, ReactNode } from 'react';
3
+ import type { PlayerReporting, ReportDetailResult, ReportRefusal, ReportResult } from '@chatwarden/core';
4
4
  import { NOTE_MAX } from './constants.js';
5
5
  import { PlayerError } from './client.js';
6
6
  import { Dialog } from './Dialog.js';
7
- import { DETAIL_REFUSED_TEXT, FollowUp } from './FollowUp.js';
8
- import type { FollowUpOutcome, ReportFollowUp } from './FollowUp.js';
9
- import { Picker } from './Picker.js';
10
7
  import type { PlayerText } from './i18n.js';
11
8
  import { groupReasons, readReporting } from './reporting.js';
12
- import type { ReasonItem } from './reporting.js';
13
9
  import { errorText, Portal, Root, useUi } from './ui.js';
14
10
  import type { DialogHostProps, PlayerUiProps } from './ui.js';
15
11
 
16
12
  /**
17
13
  * What the player filled in. Your server adds the room and the seats and calls `cw.reports.submit({ ...draft, roomId, reporterSeat,
18
- * targetSeat })`. The dialog sends `reason` (an id from the studio's `reporting` list, or nothing, as in one tap). A reason is only a
19
- * hint: the moderators decide what a report really is.
14
+ * targetSeat })`. The dialog sends `reason` (an id from the studio's `reporting` list, or nothing) and `note` in the one report. A
15
+ * reason is only a hint: the moderators decide what a report really is.
20
16
  */
21
17
  export interface ReportDraft {
22
18
  /** The reason the player chose: an id from `reporting.reasons`. Left out when none was chosen. */
@@ -25,7 +21,14 @@ export interface ReportDraft {
25
21
  note?: string;
26
22
  }
27
23
 
28
- export type { ReportFollowUp } from './FollowUp.js';
24
+ /**
25
+ * A reason and a note added to a report after it was filed (`cw.reports.addDetail`). The dialog no longer has a follow-up step (the
26
+ * reason and the note go in the one report), so this is only the shape a follow-up of your own may take.
27
+ */
28
+ export interface ReportFollowUp {
29
+ reason?: string;
30
+ note?: string;
31
+ }
29
32
 
30
33
  export interface ReportDialogProps extends PlayerUiProps, DialogHostProps {
31
34
  /**
@@ -34,22 +37,30 @@ export interface ReportDialogProps extends PlayerUiProps, DialogHostProps {
34
37
  */
35
38
  onSubmit: (draft: ReportDraft) => Promise<ReportResult>;
36
39
  /**
37
- * The optional follow-up after a one-tap report: your server calls `cw.reports.addDetail(reportId, reporterSeat, detail)` (the seat
38
- * from its own session, never from the page) and returns its `ReportDetailResult`. Without it the "Add a reason" step is not shown.
40
+ * @deprecated The dialog is one step now: the reason and the note go in the single `onSubmit`, so this is accepted but never
41
+ * called. Remove it. (`cw.reports.addDetail` is still there for a follow-up of your own.)
39
42
  */
40
43
  onAddDetail?: (reportId: string, detail: ReportFollowUp) => Promise<ReportDetailResult>;
41
44
  /** Called on Escape, Cancel and Close. Unmount the dialog in answer. */
42
45
  onClose: () => void;
43
46
  /**
44
- * How reporting works here: `(await createPlayerClient.rules()).reporting`, the `reporting` part of `GET /rules`. It sets the flow
45
- * (one tap, pick a reason first, reason required), the reasons (grouped, with hints) and the thank-you. Without it the dialog is
46
- * one tap with the dialog's own words, the same as when `GET /rules` cannot be loaded.
47
+ * How reporting works here: `(await createPlayerClient.rules()).reporting`, the `reporting` part of `GET /rules`. It gives the
48
+ * reasons (grouped, with hints), whether a reason is required, and the thank-you. Without it the dialog has no reasons to offer:
49
+ * one press files the report, with the dialog's own words, the same as when `GET /rules` cannot be loaded.
47
50
  */
48
51
  reporting?: PlayerReporting;
49
52
  /** Who is being reported, as the player knows them (a handle). Shown as text in the heading. */
50
53
  targetName?: string;
51
54
  /** The message being reported. Shown as quoted text (cut if very long) so the player sees what they are reporting. */
52
55
  line?: string;
56
+ /**
57
+ * @experimental Your own controls, placed inside the form under the reason and the note and above Report and Cancel (for example
58
+ * an "Also mute this player" checkbox and a line about false reports). You own their state; the dialog only places them, adds no
59
+ * styling beyond the spacing, and keeps their focusable controls in its Tab order (reason, note, yours, Report, Cancel) and its
60
+ * focus trap. They are not shown once the report is sent. They sit inside the dialog's form, so give your buttons `type="button"`:
61
+ * a plain button, or Enter in a text field of yours, sends the report.
62
+ */
63
+ extra?: ReactNode;
53
64
  }
54
65
 
55
66
  // One plain message for each refusal the kit has. Anything else (a refusal a newer kit adds) gets the last one.
@@ -79,78 +90,66 @@ const quoted = (line: string | undefined): string => {
79
90
  return plain.length > LINE_MAX ? `${plain.slice(0, LINE_MAX).trimEnd()}…` : plain;
80
91
  };
81
92
 
82
- /** The screen to draw: the flow, the reasons as the dialog will list them, and the thank-you. */
83
- interface Screen {
84
- flow: ReportFlow;
85
- reasons: ReasonItem[];
86
- thanks: string;
87
- }
88
-
89
93
  /**
90
- * A dialog for reporting a message or a player. How it works is the studio's setting (`reporting`, from `GET /rules`):
91
- * - `one_tap` (the default): one step, "Report this message?" and a Report button. The report is filed at once and the thank-you
92
- * shown; an optional "Add a reason" step then attaches a reason and a note to the same report (`onAddDetail`).
93
- * - `reason_first`: the reasons in groups, then an optional note; a reason can be skipped.
94
- * - `reason_required`: the same without the skip.
95
- * The reason is only a hint for the moderators. Every refusal the kit can give has its own plain message. Render it when the player
96
- * chooses to report, and unmount it in `onClose`.
94
+ * A dialog for reporting a message or a player, in one step: "Report this message?", the quoted line, an optional "Why?" (one
95
+ * select, the reasons in groups) and a Report button. A note is behind "Add a note", or shown when the chosen reason asks for one.
96
+ * Report sends the reason and the note together (`onSubmit`); the dialog then says "Report sent" and the thank-you. The studio's
97
+ * `reporting` setting decides whether the reason is required (`reason_required`: the select is required, and Report without a
98
+ * reason asks for one and sends nothing), what the reasons are, and the thank-you. The reason is only a hint for the moderators.
99
+ * Every refusal the kit can give has its own plain message. Render it when the player chooses to report, and unmount it in `onClose`.
97
100
  */
98
101
  export function ReportDialog(props: ReportDialogProps) {
99
- const { onSubmit, onAddDetail, onClose, targetName, line, theme, className, container, returnFocusTo } = props;
102
+ const { onSubmit, onClose, targetName, line, theme, className, container, returnFocusTo, extra } = props;
100
103
  const ui = useUi(props);
101
104
  const { t } = ui;
102
105
  const heading = useRef<HTMLHeadingElement>(null);
103
- const picker = useRef<HTMLFieldSetElement>(null);
104
- // One thing on its way at a time, report or follow-up: a second click, or a second Enter, does nothing.
106
+ const select = useRef<HTMLSelectElement>(null);
107
+ const noteField = useRef<HTMLTextAreaElement>(null);
108
+ // One report on its way at a time: a second click, or a second Enter, does nothing.
105
109
  const sending = useRef(false);
106
110
  const titleId = useId();
107
- const groupName = useId();
111
+ const reasonId = useId();
108
112
  const noteId = useId();
109
- const hintId = useId();
113
+ const noteHintId = useId();
114
+ const reasonHintId = useId();
110
115
  const missingId = useId();
111
- const [reason, setReason] = useState<string | null>(null);
116
+ const [reason, setReason] = useState('');
112
117
  const [note, setNote] = useState('');
118
+ // The note is shown once it was asked for (the "Add a note" button, or a reason that asks for one) and then stays.
119
+ const [noteOpen, setNoteOpen] = useState(false);
120
+ const [focusNote, setFocusNote] = useState(false);
113
121
  const [missing, setMissing] = useState(false);
114
122
  const [busy, setBusy] = useState(false);
115
123
  const [failure, setFailure] = useState<PlayerError | null>(null);
116
124
  const [result, setResult] = useState<ReportResult | null>(null);
117
- // How the follow-up after a one-tap report ended, and whether its time is up (the kit takes one until `detailUntil`).
118
- const [dDone, setDDone] = useState<FollowUpOutcome | null>(null);
119
- const [expired, setExpired] = useState(false);
120
125
 
121
- const screen: Screen = (() => {
122
- // No `reporting` (or something that is not the payload): one tap with the dialog's own words, as when `GET /rules` cannot be loaded.
123
- const read = readReporting(props.reporting) ?? { flow: 'one_tap' as const, reasons: [], thanks: '' };
124
- // A flow that asks for a reason has nothing to ask with when the list is empty: it is one tap.
125
- return { flow: read.reasons.length === 0 ? 'one_tap' : read.flow, reasons: read.reasons, thanks: read.thanks.trim() };
126
- })();
127
- const { flow } = screen;
128
- const groups = groupReasons(screen.reasons);
129
- const chosen = screen.reasons.find((r) => r.id === reason);
130
- // The note: when the chosen reason asks for it.
131
- const showNote = chosen?.asksNote === true;
126
+ // No `reporting` (or something that is not the payload): no reasons to offer, as when `GET /rules` cannot be loaded.
127
+ const read = readReporting(props.reporting) ?? { flow: 'one_tap' as const, reasons: [], thanks: '' };
128
+ const thanks = read.thanks.trim();
129
+ const hasReasons = read.reasons.length > 0;
130
+ // A flow that asks for a reason has nothing to ask with when the list is empty.
131
+ const required = hasReasons && read.flow === 'reason_required';
132
+ const groups = groupReasons(read.reasons);
133
+ const named = groups.filter((g) => g.name !== '');
134
+ const bare = groups.filter((g) => g.name === '').flatMap((g) => g.items);
135
+ // The reason is read from the CURRENT list: if `reporting` changes while the dialog is open and the id the player chose is gone, the
136
+ // select shows the placeholder, and this is undefined, so nothing stale is sent (and a required reason is asked for again).
137
+ const chosen = read.reasons.find((r) => r.id === reason);
138
+ const reasonValue = chosen === undefined ? '' : chosen.id;
139
+ const hint = chosen !== undefined && chosen.hint !== '' ? chosen.hint : null;
140
+ const noteShown = hasReasons && noteOpen;
132
141
 
133
142
  // The form is gone once there is an answer: focus goes back to the heading, so the answer is read and focus is not lost.
134
143
  useEffect(() => {
135
144
  if (result !== null) heading.current?.focus();
136
145
  }, [result]);
146
+ // "Add a note" is replaced by the field: focus follows it.
137
147
  useEffect(() => {
138
- if (dDone !== null) heading.current?.focus();
139
- }, [dDone]);
140
- // The follow-up stops being offered when the kit would no longer take it.
141
- const until = result?.ok === true && typeof result.detailUntil === 'number' && Number.isFinite(result.detailUntil) ? result.detailUntil : null;
142
- useEffect(() => {
143
- if (until === null) return undefined;
144
- let timer: ReturnType<typeof setTimeout> | undefined;
145
- // Looks at the clock again when the time comes, so a clock that has not moved does not end the offer early.
146
- const check = (): void => {
147
- const left = until - ui.now();
148
- if (left < 0) setExpired(true);
149
- else timer = setTimeout(check, Math.min(Math.max(left + 1, 50), 2_000_000_000));
150
- };
151
- check();
152
- return () => clearTimeout(timer);
153
- }, [until]);
148
+ if (focusNote) {
149
+ noteField.current?.focus();
150
+ setFocusNote(false);
151
+ }
152
+ }, [focusNote]);
154
153
 
155
154
  const send = async (draft: ReportDraft): Promise<void> => {
156
155
  sending.current = true;
@@ -170,24 +169,19 @@ export function ReportDialog(props: ReportDialogProps) {
170
169
  const submit = (e: FormEvent): void => {
171
170
  e.preventDefault();
172
171
  if (sending.current) return;
173
- if (flow !== 'one_tap' && reason === null) {
172
+ if (required && reasonValue === '') {
174
173
  setMissing(true);
175
- picker.current?.querySelector<HTMLElement>('input')?.focus();
174
+ select.current?.focus();
176
175
  return;
177
176
  }
178
- const draft: ReportDraft = flow === 'one_tap' ? {} : { reason: reason as string };
179
- if (showNote && trimmed !== '') draft.note = trimmed;
177
+ const draft: ReportDraft = {};
178
+ if (reasonValue !== '') draft.reason = reasonValue;
179
+ if (noteShown && trimmed !== '') draft.note = trimmed;
180
180
  void send(draft);
181
181
  };
182
- const skip = (): void => {
183
- if (sending.current) return;
184
- void send(showNote && trimmed !== '' ? { note: trimmed } : {});
185
- };
186
182
 
187
- const reportId = result?.ok === true && typeof result.reportId === 'string' && result.reportId !== '' ? result.reportId : null;
188
183
  const quote = quoted(line);
189
- const oneTapTitle = quote !== '' ? t('player.report.titleOneTap') : targetName === undefined ? t('player.report.titleOneTapPlayer') : t('player.report.titleOneTapNamed', { name: targetName });
190
- const title = flow === 'one_tap' ? oneTapTitle : targetName === undefined ? t('player.report.title') : t('player.report.titleNamed', { name: targetName });
184
+ const title = quote !== '' ? t('player.report.titleOneTap') : targetName === undefined ? t('player.report.titleOneTapPlayer') : t('player.report.titleOneTapNamed', { name: targetName });
191
185
  const taken = result?.ok === true;
192
186
  const refusal = result !== null && !taken ? result : null;
193
187
  // After an answer the heading says what happened (a question would be stale), and focus is on it.
@@ -197,11 +191,12 @@ export function ReportDialog(props: ReportDialogProps) {
197
191
  const refused = (r as { refused?: unknown }).refused;
198
192
  return typeof refused === 'string' && Object.hasOwn(REFUSED_TEXT, refused) ? REFUSED_TEXT[refused as ReportRefusal] : 'player.report.refused.unknown';
199
193
  };
200
- const detailRefusedKey = (refused: string): PlayerText => (Object.hasOwn(DETAIL_REFUSED_TEXT, refused) ? (DETAIL_REFUSED_TEXT[refused] as PlayerText) : 'player.report.detail.failed');
201
- const detailUntil = taken && typeof result.detailUntil === 'number' && Number.isFinite(result.detailUntil) ? result.detailUntil : null;
202
- const timeUp = expired || (detailUntil !== null && ui.now() > detailUntil);
203
- const canFollow = taken && flow === 'one_tap' && reportId !== null && onAddDetail !== undefined && screen.reasons.length > 0 && !timeUp;
204
- const needText: PlayerText = flow === 'reason_first' ? 'player.report.needReasonOrSkip' : 'player.report.needReason';
194
+ const option = (r: { id: string; label: string }) => (
195
+ <option key={r.id} value={r.id}>
196
+ {r.label}
197
+ </option>
198
+ );
199
+ const describedBy = [hint === null ? '' : reasonHintId, missing ? missingId : ''].filter((id) => id !== '').join(' ');
205
200
 
206
201
  return (
207
202
  <Portal container={container}>
@@ -210,51 +205,84 @@ export function ReportDialog(props: ReportDialogProps) {
210
205
  <h2 id={titleId} ref={heading} className="cw-title" tabIndex={-1}>
211
206
  {shownTitle}
212
207
  </h2>
213
- {/* Always there, so what is written into it is announced: the thank-you, and "Added. Thanks.". */}
208
+ {/* Always there, so what is written into it is announced: the thank-you. */}
214
209
  <div role="status" className="cw-live">
215
210
  {taken && (
216
211
  <p className="cw-result">
217
- {screen.thanks === '' ? t('player.report.thanks') : screen.thanks}
212
+ {thanks === '' ? t('player.report.thanks') : thanks}
218
213
  {remaining !== null && ` ${t('player.report.remaining', { count: remaining })}`}
219
214
  </p>
220
215
  )}
221
- {dDone?.ok === true && <p className="cw-result">{t('player.report.detail.added')}</p>}
222
216
  </div>
223
217
  {result === null ? (
224
218
  <form onSubmit={submit} noValidate>
225
- <p className="cw-body">{flow === 'one_tap' ? t('player.report.oneTap.intro') : t('player.report.intro')}</p>
219
+ <p className="cw-body">{required ? t('player.report.intro') : t('player.report.oneTap.intro')}</p>
226
220
  {quote !== '' && <blockquote className="cw-quote">{t('player.report.quote', { line: quote })}</blockquote>}
227
- {flow !== 'one_tap' && (
228
- <Picker
229
- groups={groups}
230
- name={groupName}
231
- value={reason}
232
- legend={t('player.report.legend')}
233
- invalid={missing}
234
- describedBy={missingId}
235
- fieldset={picker}
236
- onChange={(id) => {
237
- setReason(id);
238
- setMissing(false);
239
- }}
240
- />
241
- )}
242
- {missing && (
243
- <p id={missingId} className="cw-field-error" role="alert">
244
- {t(needText)}
245
- </p>
221
+ {hasReasons && (
222
+ <div className="cw-field">
223
+ <label className="cw-label" htmlFor={reasonId}>
224
+ {required ? t('player.report.whyRequired') : t('player.report.why')}
225
+ </label>
226
+ <select
227
+ id={reasonId}
228
+ ref={select}
229
+ className="cw-select"
230
+ value={reasonValue}
231
+ required={required}
232
+ aria-invalid={missing || undefined}
233
+ aria-describedby={describedBy === '' ? undefined : describedBy}
234
+ onChange={(e) => {
235
+ const id = e.target.value;
236
+ setReason(id);
237
+ setMissing(false);
238
+ if (read.reasons.find((r) => r.id === id)?.asksNote === true) setNoteOpen(true);
239
+ }}
240
+ >
241
+ <option value="">{required ? t('player.report.chooseRequired') : t('player.report.choose')}</option>
242
+ {named.map((g) => (
243
+ <optgroup key={g.name} label={g.name}>
244
+ {g.items.map(option)}
245
+ </optgroup>
246
+ ))}
247
+ {bare.map(option)}
248
+ </select>
249
+ {hint !== null && (
250
+ <p id={reasonHintId} className="cw-hint">
251
+ {hint}
252
+ </p>
253
+ )}
254
+ {missing && (
255
+ <p id={missingId} className="cw-field-error" role="alert">
256
+ {t('player.report.needReason')}
257
+ </p>
258
+ )}
259
+ </div>
246
260
  )}
247
- {showNote && (
248
- <>
261
+ {noteShown && (
262
+ <div className="cw-field">
249
263
  <label className="cw-label" htmlFor={noteId}>
250
264
  {t('player.report.note')}
251
265
  </label>
252
- <textarea id={noteId} className="cw-textarea" rows={4} maxLength={NOTE_MAX} value={note} aria-describedby={hintId} onChange={(e) => setNote(e.target.value)} />
253
- <p id={hintId} className="cw-hint">
266
+ <textarea id={noteId} ref={noteField} className="cw-textarea" rows={3} maxLength={NOTE_MAX} value={note} aria-describedby={noteHintId} onChange={(e) => setNote(e.target.value)} />
267
+ <p id={noteHintId} className="cw-hint">
254
268
  {t('player.report.noteHint', { max: NOTE_MAX })}
255
269
  </p>
256
- </>
270
+ </div>
271
+ )}
272
+ {hasReasons && !noteShown && (
273
+ // No aria-expanded: this is a one-way door. It is removed when pressed (the note takes its place) and focus moves to the note.
274
+ <button
275
+ type="button"
276
+ className="cw-linkbtn"
277
+ onClick={() => {
278
+ setNoteOpen(true);
279
+ setFocusNote(true);
280
+ }}
281
+ >
282
+ {t('player.report.addNote')}
283
+ </button>
257
284
  )}
285
+ {extra !== undefined && extra !== null && extra !== false && <div className="cw-extra">{extra}</div>}
258
286
  {failure !== null && (
259
287
  <p className="cw-error" role="alert">
260
288
  {t('player.report.failed')} {errorText(t, failure)}
@@ -262,13 +290,8 @@ export function ReportDialog(props: ReportDialogProps) {
262
290
  )}
263
291
  <div className="cw-actions">
264
292
  <button type="submit" className="cw-btn cw-btn--primary" aria-disabled={busy || undefined} aria-busy={busy || undefined}>
265
- {flow === 'one_tap' ? t('player.report.oneTap.send') : t('player.report.send')}
293
+ {t('player.report.oneTap.send')}
266
294
  </button>
267
- {flow === 'reason_first' && (
268
- <button type="button" className="cw-btn" aria-disabled={busy || undefined} aria-busy={busy || undefined} onClick={skip}>
269
- {t('player.report.skip')}
270
- </button>
271
- )}
272
295
  {/* Closing while a report is on its way would lose the answer: busy, like Escape. */}
273
296
  <button type="button" className="cw-btn" aria-disabled={busy || undefined} onClick={() => !sending.current && onClose()}>
274
297
  {t('player.report.cancel')}
@@ -282,14 +305,6 @@ export function ReportDialog(props: ReportDialogProps) {
282
305
  {t(refusedKey(refusal))}
283
306
  </p>
284
307
  )}
285
- {canFollow && dDone === null && (
286
- <FollowUp t={t} groups={groups} reportId={reportId} onAddDetail={onAddDetail} sending={sending} busy={busy} onBusy={setBusy} onDone={setDDone} />
287
- )}
288
- {dDone !== null && !dDone.ok && (
289
- <p className="cw-error" role="alert">
290
- {t(detailRefusedKey(dDone.refused))}
291
- </p>
292
- )}
293
308
  <div className="cw-actions">
294
309
  <button type="button" className="cw-btn cw-btn--primary" aria-disabled={busy || undefined} onClick={() => !sending.current && onClose()}>
295
310
  {t('player.report.close')}
package/src/i18n.ts CHANGED
@@ -78,9 +78,12 @@ export const en = {
78
78
  'player.restricted.ended': 'Your chat is back on. The restriction ended {when}.',
79
79
  'player.restricted.ref': 'Reference {code}',
80
80
 
81
+ /** @deprecated unused since 0.1.3; removed in a later minor */
81
82
  'player.report.title': 'Report a player',
83
+ /** @deprecated unused since 0.1.3; removed in a later minor */
82
84
  'player.report.titleNamed': 'Report {name}',
83
- 'player.report.intro': 'Tell us what happened and a moderator will look at it.',
85
+ 'player.report.intro': 'Tell us why and a moderator will look at it.',
86
+ /** @deprecated unused since 0.1.3; removed in a later minor */
84
87
  'player.report.legend': 'What happened?',
85
88
  'player.report.titleOneTap': 'Report this message?',
86
89
  'player.report.titleOneTapNamed': 'Report {name}?',
@@ -90,24 +93,45 @@ export const en = {
90
93
  'player.report.oneTap.intro': 'We will pass it to the moderators. You do not need to say why.',
91
94
  'player.report.oneTap.send': 'Report',
92
95
  'player.report.quote': '“{line}”',
96
+ /** @deprecated unused since 0.1.3; removed in a later minor */
93
97
  'player.report.skip': 'Skip and report',
94
98
  'player.report.needReason': 'Choose a reason.',
99
+ /** @deprecated unused since 0.1.3; removed in a later minor */
95
100
  'player.report.needReasonOrSkip': 'Choose what happened, or skip.',
101
+ /** @deprecated unused since 0.1.3; removed in a later minor */
96
102
  'player.report.addReason': 'Add a reason',
103
+ /** @deprecated unused since 0.1.3; removed in a later minor */
97
104
  'player.report.addReasonHint': 'This helps, but the moderators decide what it really is. You can add this for a few minutes after you report.',
105
+ /** @deprecated unused since 0.1.3; removed in a later minor */
98
106
  'player.report.detail.legend': 'What was it about?',
107
+ /** @deprecated unused since 0.1.3; removed in a later minor */
99
108
  'player.report.detail.send': 'Add to my report',
109
+ /** @deprecated unused since 0.1.3; removed in a later minor */
100
110
  'player.report.detail.need': 'Choose a reason or write a note.',
111
+ /** @deprecated unused since 0.1.3; removed in a later minor */
101
112
  'player.report.detail.added': 'Added. Thanks.',
113
+ /** @deprecated unused since 0.1.3; removed in a later minor */
102
114
  'player.report.detail.failed': 'We could not add that.',
115
+ /** @deprecated unused since 0.1.3; removed in a later minor */
103
116
  'player.report.detail.refused.off': 'Adding to a report is not available right now.',
117
+ /** @deprecated unused since 0.1.3; removed in a later minor */
104
118
  'player.report.detail.refused.not_found': 'We cannot find that report any more.',
119
+ /** @deprecated unused since 0.1.3; removed in a later minor */
105
120
  'player.report.detail.refused.not_reporter': 'Only the player who sent a report can add to it.',
121
+ /** @deprecated unused since 0.1.3; removed in a later minor */
106
122
  'player.report.detail.refused.too_late': 'It is too late to add to this report. We still have the report.',
123
+ /** @deprecated unused since 0.1.3; removed in a later minor */
107
124
  'player.report.detail.refused.already_added': 'You already added to this report.',
125
+ /** @deprecated unused since 0.1.3; removed in a later minor */
108
126
  'player.report.detail.refused.reason_unknown': 'That reason is not available any more. Your report is still with us.',
109
- 'player.report.note': 'Add a note (optional)',
127
+ 'player.report.note': 'Note (optional)',
128
+ 'player.report.addNote': 'Add a note',
129
+ 'player.report.why': 'Why? (optional)',
130
+ 'player.report.whyRequired': 'Why?',
131
+ 'player.report.choose': 'Choose a reason (optional)',
132
+ 'player.report.chooseRequired': 'Choose a reason',
110
133
  'player.report.noteHint': 'A moderator will read this. Up to {max} characters. Do not include passwords or personal details.',
134
+ /** @deprecated unused since 0.1.3; removed in a later minor */
111
135
  'player.report.send': 'Send report',
112
136
  'player.report.cancel': 'Cancel',
113
137
  'player.report.close': 'Close',
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = '0.1.2';
1
+ export const VERSION = '0.1.3';
2
2
 
3
3
  export { NoticeQueue } from './NoticeQueue.js';
4
4
  export type { NoticeQueueProps } from './NoticeQueue.js';