@stapel/chat-react 0.1.0 → 0.3.0
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/CHANGELOG.md +95 -0
- package/LICENSE +21 -0
- package/MODULE.md +32 -0
- package/README.md +21 -0
- package/dist/default/SignInLink.d.ts +28 -0
- package/dist/default/SignInLink.d.ts.map +1 -0
- package/dist/default/SignInLink.js +17 -0
- package/dist/default/SignInLink.js.map +1 -0
- package/dist/default/StartChatButton.d.ts +7 -3
- package/dist/default/StartChatButton.d.ts.map +1 -1
- package/dist/default/StartChatButton.js +3 -2
- package/dist/default/StartChatButton.js.map +1 -1
- package/dist/default/index.d.ts +2 -0
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +1 -0
- package/dist/default/index.js.map +1 -1
- package/dist/flows/freshness.js +2 -2
- package/dist/headless/StartDirectChat.d.ts +5 -0
- package/dist/headless/StartDirectChat.d.ts.map +1 -1
- package/dist/headless/StartDirectChat.js +29 -2
- package/dist/headless/StartDirectChat.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +3 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/keys.d.ts +6 -0
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +9 -0
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +3 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/model/runtime.d.ts +1 -1
- package/llms.txt +1 -1
- package/manifest.json +4 -1
- package/nav-manifest.json +1 -1
- package/package.json +14 -14
- package/src/analytics/generated/events.json +1 -1
- package/src/default/SignInLink.tsx +54 -0
- package/src/default/StartChatButton.tsx +11 -4
- package/src/default/index.ts +2 -0
- package/src/headless/StartDirectChat.tsx +36 -1
- package/src/i18n/es.ts +3 -0
- package/src/i18n/keys.ts +9 -0
- package/src/i18n/ru.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1 +1,96 @@
|
|
|
1
1
|
# @stapel/chat-react
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3e2e2a3: A blocked control now carries the door, not just the reason: `signIn`
|
|
8
|
+
|
|
9
|
+
`actionBlocked` ended the grey-rectangle incident by making every switched-off
|
|
10
|
+
control state its reason. It did not end the next one. "Sign in to save this",
|
|
11
|
+
"sign in to leave a review", "sign in to message the seller" are reasons whose
|
|
12
|
+
next action is a LINK, and no pair took one — so the storefront had to put its
|
|
13
|
+
own notice a screen away from each of the three controls it was about, and
|
|
14
|
+
named it a gap rather than shipping it (Wave D, G-3).
|
|
15
|
+
|
|
16
|
+
All three now take the same prop, core's `SignInCta`:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<ListingCard listing={row} signIn={{ href: `/login?next=${here}` }} />
|
|
20
|
+
<ReviewsPanel target={target} signIn={{ href: `/login?next=${here}` }} />
|
|
21
|
+
<StartChatButton sellerId={sellerId} signIn={{ onSignIn: () => openModal() }} />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`{href}` **or** `{onSignIn}`, never both. Omit it and the reason renders alone,
|
|
25
|
+
with no trailing whitespace where the link is not — a host with no sign-in
|
|
26
|
+
route is a supported host.
|
|
27
|
+
|
|
28
|
+
Two more things each pair had to fix to make the door reachable:
|
|
29
|
+
|
|
30
|
+
- **listings**: the favourite's reason lived only in a `title` on a DISABLED
|
|
31
|
+
button, which receives no pointer events in any browser — core's own
|
|
32
|
+
`actionGate.ts` calls that "a reason nobody can read". It is now text beside
|
|
33
|
+
the heart (`listings-card-favorite-blocked`), with the link inside it. The
|
|
34
|
+
heart is still never hidden from a visitor.
|
|
35
|
+
- **chat**: `StartDirectChat` had no mandate gate at all, so a visitor could
|
|
36
|
+
press "message the seller" and collect a 401 — a refusal delivered at the one
|
|
37
|
+
moment it is useless. The axis is now the first arm of its `firstBlock`, read
|
|
38
|
+
through core's `MandateSource` seam. `member` may write; `guest`/`anonymous`
|
|
39
|
+
are told to sign in; `asking` says we are still asking. `unavailable` stays
|
|
40
|
+
AVAILABLE on purpose: that is what core answers outside a `<MandateProvider>`
|
|
41
|
+
too, and a host that never wired the axis must not lose its button — "we
|
|
42
|
+
could not ask" is not "you may not". This raises chat-react's `@stapel/core`
|
|
43
|
+
floor to `>=0.15.0`, where `useMandate`/`matchMandate` shipped.
|
|
44
|
+
|
|
45
|
+
The link's LABEL is each pair's own (`listings.card.sign_in`,
|
|
46
|
+
`reviews.form.sign_in`, `chat.start.sign_in`), in all three locales — core
|
|
47
|
+
floors `en` and `ru`, and these pairs also ship `es`.
|
|
48
|
+
|
|
49
|
+
## 0.2.0
|
|
50
|
+
|
|
51
|
+
### Minor Changes
|
|
52
|
+
|
|
53
|
+
- ca35e19: New pair: `@stapel/chat-react` — the React half of `stapel-chat`, wiring both
|
|
54
|
+
of its transports behind one seam.
|
|
55
|
+
|
|
56
|
+
The storefront spec ruled polling-only for chat v1, and it was right about the
|
|
57
|
+
fleet it surveyed: the resumable consumer existed but `stapel_chat.routing`
|
|
58
|
+
exported nothing, so no host could mount it. stapel-chat 0.2.2 ships that mount
|
|
59
|
+
(`ws/chat/<uuid:conversation_id>`). So this pair carries **two transports and
|
|
60
|
+
one seam** — `useChatFreshness(streamKey, mapToQueryKeys, { fallbackRefetchInterval })`,
|
|
61
|
+
deliberately the signature the realtime substrate reserves for
|
|
62
|
+
`useSignalInvalidate`:
|
|
63
|
+
|
|
64
|
+
- a typed client for the module's own protocol — `hello{last_seq}` → `welcome`
|
|
65
|
+
→ replay → `replay_done` → live frames, seq-deduped on both ends, resume from
|
|
66
|
+
the cursor the STORE holds (not the one the socket opened with), `error{resync}`
|
|
67
|
+
forwarded verbatim, close codes 4401/4403 treated as answers rather than
|
|
68
|
+
faults (no reconnect), everything else reconnected with jittered backoff;
|
|
69
|
+
- polling by `seq`, visibility-aware, with exponential backoff on consecutive
|
|
70
|
+
failures — used whenever the socket is not carrying the stream, and for the
|
|
71
|
+
inbox, which has no socket at all (the module fans out per thread).
|
|
72
|
+
|
|
73
|
+
Both ends do the same thing with what they learn: refetch the thread query,
|
|
74
|
+
whose query function advances the window BY SEQ
|
|
75
|
+
(`?direction=prev&anchor=<tip>`). The screens are written once and the tests
|
|
76
|
+
run against both transports. Writes never go over the socket: the `send` frame
|
|
77
|
+
is typed (a mirror must be complete) and never emitted, because its refusals
|
|
78
|
+
carry socket-local codes with no i18n key and no remediation, while
|
|
79
|
+
`POST …/messages` answers with the persisted row and a real error envelope.
|
|
80
|
+
|
|
81
|
+
Surface: `<ConversationList>` (server-computed unread counts, as a LoadState so
|
|
82
|
+
a badge cannot read "0" during an outage), `<ConversationThread>` (a contiguous
|
|
83
|
+
seq-ordered window — a hole is re-read, never stitched — with backfill and an
|
|
84
|
+
automatic, monotonic read marker), `<MessageComposer>` (code-point length
|
|
85
|
+
counting, so an emoji is one character on both sides of the wire), and
|
|
86
|
+
`<StartDirectChat>` — "message the seller", get-or-create over the module's own
|
|
87
|
+
participant-pair idempotency. An opt-in antd skin at `@stapel/chat-react/default`
|
|
88
|
+
and a member-surface nav entry.
|
|
89
|
+
|
|
90
|
+
Two contract facts recorded rather than papered over:
|
|
91
|
+
`CreateConversationRequest.scope_key` is ignored by the server, so a direct
|
|
92
|
+
thread cannot be scoped to a listing and the pair exposes no argument that
|
|
93
|
+
pretends otherwise; and stapel-chat ships no `translations/` directory, so the
|
|
94
|
+
pair authors ru/es for the twelve error keys the module owns (the
|
|
95
|
+
stapel-forms/stapel_attributes precedent) while the cross-cutting keys come from
|
|
96
|
+
stapel-core's catalogue.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stapel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MODULE.md
CHANGED
|
@@ -92,6 +92,38 @@ Unlike notifications-react, the `es` subpath here carries hand-written UI copy
|
|
|
92
92
|
too: a marketplace's buyer-to-seller chat is the surface where a half-translated
|
|
93
93
|
screen shows most.
|
|
94
94
|
|
|
95
|
+
## The gate before the click, and the door beside the reason
|
|
96
|
+
|
|
97
|
+
`POST /conversations/` is `IsAuthenticated`. Until 0.3.0 `StartDirectChat`
|
|
98
|
+
blocked only on the two seller-shaped reasons (no seller on the listing; the
|
|
99
|
+
listing is your own), so a visitor pressed the button and got a 401 — a
|
|
100
|
+
refusal delivered at the one moment it is useless.
|
|
101
|
+
|
|
102
|
+
The mandate axis is now the FIRST arm of that `firstBlock`, read through core's
|
|
103
|
+
`MandateSource` seam and never derived here (a storefront's derivation is "is
|
|
104
|
+
there a session?", a tenant app's is `@stapel/workspaces-react`'s). Four of the
|
|
105
|
+
five `matchMandate` arms behave as expected; the fifth is a deliberate
|
|
106
|
+
exception:
|
|
107
|
+
|
|
108
|
+
| arm | outcome |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `member` | available |
|
|
111
|
+
| `guest`, `anonymous` | blocked — `chat.start.blocked.sign_in` |
|
|
112
|
+
| `asking` | blocked — `chat.start.blocked.mandate_unknown` ("still asking", not "you may not") |
|
|
113
|
+
| `unavailable` | **available** |
|
|
114
|
+
|
|
115
|
+
`unavailable` is what core answers outside a `<MandateProvider>` too, so
|
|
116
|
+
refusing there would take the button away from every host that never wired the
|
|
117
|
+
axis — and "we could not ask" is not "you may not" (the storefront spec's own
|
|
118
|
+
§7.4 negative leg). If the guess is wrong, the module answers 401 exactly as it
|
|
119
|
+
did before.
|
|
120
|
+
|
|
121
|
+
`<StartChatButton signIn={…}>` renders the way out next to that sentence.
|
|
122
|
+
`SignInCta` is core's (`{href}` or `{onSignIn}`, never both) so the prop is
|
|
123
|
+
spelled identically in `@stapel/reviews-react` and `@stapel/listings-react`;
|
|
124
|
+
the LABEL is this pair's (`chat.start.sign_in`, in all three locales), because
|
|
125
|
+
core floors only `en` and `ru`.
|
|
126
|
+
|
|
95
127
|
## Extension seams (frontend-standard §7)
|
|
96
128
|
|
|
97
129
|
- The client is injected via `<ChatProvider>` / core's `StapelConfigProvider`
|
package/README.md
CHANGED
|
@@ -100,6 +100,27 @@ Get-or-create: a direct thread is keyed by the participant pair under a unique
|
|
|
100
100
|
constraint, so pressing twice — or writing to the same seller about a second
|
|
101
101
|
listing — lands in the same conversation.
|
|
102
102
|
|
|
103
|
+
### Who may press it, and where a visitor goes
|
|
104
|
+
|
|
105
|
+
`POST /conversations/` is `IsAuthenticated`, so the first gate is the mandate
|
|
106
|
+
axis, read through core's `MandateSource` seam. A visitor is told to sign in
|
|
107
|
+
BEFORE the click instead of collecting a 401 after it — and the sentence comes
|
|
108
|
+
with the door:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<StartChatButton sellerId={sellerId} signIn={{ href: `/login?next=${here}` }} />
|
|
112
|
+
<StartChatButton sellerId={sellerId} signIn={{ onSignIn: () => openModal() }} />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`signIn` is core's `SignInCta` — `{href}` **or** `{onSignIn}`, never both — and
|
|
116
|
+
it is the same prop `@stapel/reviews-react` and `@stapel/listings-react` take.
|
|
117
|
+
Omit it and the reason still renders, alone, which is what a host with no
|
|
118
|
+
sign-in route wants.
|
|
119
|
+
|
|
120
|
+
Outside a `<MandateProvider>` core answers `unresolved/unavailable`, and that
|
|
121
|
+
arm stays **available**: a host that never wired the axis keeps its button, and
|
|
122
|
+
"we could not ask" is not "you may not".
|
|
123
|
+
|
|
103
124
|
## The antd skin (opt-in)
|
|
104
125
|
|
|
105
126
|
```tsx
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door beside a blocked control.
|
|
3
|
+
*
|
|
4
|
+
* `actionBlocked` ended the grey-rectangle incident by making every
|
|
5
|
+
* switched-off control state its reason. It did not end the next problem:
|
|
6
|
+
* "sign in to message the seller" is a reason whose next action is a LINK, and
|
|
7
|
+
* this pair rendered the sentence and stopped there — leaving the visitor to
|
|
8
|
+
* find the header themselves (storefront Wave D, G-3).
|
|
9
|
+
*
|
|
10
|
+
* WHERE that link goes is the container's business, never the pair's: the
|
|
11
|
+
* storefront's is `/login?next=<current>`, a tenant app's may be a modal. So
|
|
12
|
+
* the shape is core's `SignInCta` — `{href}` or `{onSignIn}`, never both — and
|
|
13
|
+
* the copy is this pair's, because core floors `en` and `ru` while this pair
|
|
14
|
+
* also ships `es`.
|
|
15
|
+
*
|
|
16
|
+
* A host that routes internally passes `onSignIn`; the `href` arm renders a
|
|
17
|
+
* plain anchor on purpose, because arriving at a sign-in page is one of the
|
|
18
|
+
* few navigations a full load costs nothing.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReactElement } from "react";
|
|
21
|
+
import type { SignInCta } from "@stapel/core";
|
|
22
|
+
export interface SignInLinkProps {
|
|
23
|
+
/** Absent: no link — a host with no sign-in route shows the reason alone. */
|
|
24
|
+
readonly cta: SignInCta | undefined;
|
|
25
|
+
readonly testId: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function SignInLink(props: SignInLinkProps): ReactElement | null;
|
|
28
|
+
//# sourceMappingURL=SignInLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInLink.d.ts","sourceRoot":"","sources":["../../src/default/SignInLink.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAsBtE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Typography } from "antd";
|
|
3
|
+
import { useT } from "@stapel/core";
|
|
4
|
+
import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
5
|
+
export function SignInLink(props) {
|
|
6
|
+
const t = useT();
|
|
7
|
+
const { cta } = props;
|
|
8
|
+
if (cta === undefined)
|
|
9
|
+
return null;
|
|
10
|
+
// The separating space belongs HERE, not at the call site: a reason with no
|
|
11
|
+
// door must render as exactly its own sentence, and a `{" "}` left behind by
|
|
12
|
+
// an absent link is a trailing space in every caller's assertion.
|
|
13
|
+
return (_jsxs(_Fragment, { children: [" ", _jsx(Typography.Link, { "data-testid": props.testId, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", ...(cta.href !== undefined
|
|
14
|
+
? { href: cta.href }
|
|
15
|
+
: { onClick: cta.onSignIn }), children: t(CHAT_I18N_KEYS.startSignIn) })] }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=SignInLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInLink.js","sourceRoot":"","sources":["../../src/default/SignInLink.tsx"],"names":[],"mappings":";AAoBA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQjD,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnC,4EAA4E;IAC5E,6EAA6E;IAC7E,kEAAkE;IAClE,OAAO,CACL,8BACG,GAAG,EACJ,KAAC,UAAU,CAAC,IAAI,mBACD,KAAK,CAAC,MAAM,oBACV,MAAM,2BACC,8DAAyD,KAC3E,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS;oBACzB,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;oBACpB,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,YAE7B,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,GACd,IACjB,CACJ,CAAC;AACJ,CAAC"}
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
* `<StartChatButton/>` — "message the seller" on a listing card, skinned.
|
|
3
3
|
*
|
|
4
4
|
* The control is either pressable or switched off WITH a sentence beside it
|
|
5
|
-
* (no seller on the listing; the listing is your own
|
|
6
|
-
* grey rectangle with no explanation
|
|
5
|
+
* (no seller on the listing; the listing is your own; you are not signed in).
|
|
6
|
+
* It never renders as a grey rectangle with no explanation — and when the host
|
|
7
|
+
* supplies `signIn`, the sentence comes with the door: a stated reason whose
|
|
8
|
+
* next action is a link the visitor cannot find on their own is only half an
|
|
9
|
+
* answer (storefront Wave D, G-3).
|
|
7
10
|
*/
|
|
8
11
|
import type { ReactElement } from "react";
|
|
12
|
+
import type { SignInCtaProp } from "@stapel/core";
|
|
9
13
|
import type { Conversation } from "../api/types.js";
|
|
10
|
-
export interface StartChatButtonProps {
|
|
14
|
+
export interface StartChatButtonProps extends SignInCtaProp {
|
|
11
15
|
sellerId: string | null | undefined;
|
|
12
16
|
viewerId?: string | null;
|
|
13
17
|
onOpened?: (conversation: Conversation) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartChatButton.d.ts","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"StartChatButton.d.ts","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAiC,aAAa,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAMpD,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAwCD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAmBzE"}
|
|
@@ -4,15 +4,16 @@ import { useActionGate, useErrorDisplay, useT } from "@stapel/core";
|
|
|
4
4
|
import { StartDirectChat } from "../headless/StartDirectChat.js";
|
|
5
5
|
import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
6
6
|
import { ErrorAlert } from "./ErrorAlert.js";
|
|
7
|
+
import { SignInLink } from "./SignInLink.js";
|
|
7
8
|
function StartChatBody(props) {
|
|
8
9
|
const t = useT();
|
|
9
10
|
const gate = useActionGate(props.availability);
|
|
10
11
|
const errorDisplay = useErrorDisplay(CHAT_I18N_KEYS.unknownError);
|
|
11
12
|
return (_jsxs(Space, { direction: "vertical", style: { width: props.block ? "100%" : undefined }, children: [_jsx(Button, { type: "primary", block: props.block ?? false, disabled: gate.disabled, loading: props.isStarting, onClick: props.start, "data-testid": "chat-start-button", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: props.isStarting
|
|
12
13
|
? t(CHAT_I18N_KEYS.startStarting)
|
|
13
|
-
: t(CHAT_I18N_KEYS.startButton) }), gate.reason ? (
|
|
14
|
+
: t(CHAT_I18N_KEYS.startButton) }), gate.reason ? (_jsxs(Typography.Text, { type: "secondary", "data-testid": "chat-start-blocked", children: [gate.reason, _jsx(SignInLink, { cta: props.signIn, testId: "chat-start-sign-in" })] })) : null, _jsx(ErrorAlert, { error: errorDisplay(props.error) })] }));
|
|
14
15
|
}
|
|
15
16
|
export function StartChatButton(props) {
|
|
16
|
-
return (_jsx(StartDirectChat, { sellerId: props.sellerId, ...(props.viewerId !== undefined ? { viewerId: props.viewerId } : {}), ...(props.onOpened !== undefined ? { onOpened: props.onOpened } : {}), children: ({ availability, isStarting, error, start }) => (_jsx(StartChatBody, { availability: availability, isStarting: isStarting, error: error, start: start, block: props.block })) }));
|
|
17
|
+
return (_jsx(StartDirectChat, { sellerId: props.sellerId, ...(props.viewerId !== undefined ? { viewerId: props.viewerId } : {}), ...(props.onOpened !== undefined ? { onOpened: props.onOpened } : {}), children: ({ availability, isStarting, error, start }) => (_jsx(StartChatBody, { availability: availability, isStarting: isStarting, error: error, start: start, block: props.block, signIn: props.signIn })) }));
|
|
17
18
|
}
|
|
18
19
|
//# sourceMappingURL=StartChatButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartChatButton.js","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"StartChatButton.js","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAS7C,SAAS,aAAa,CAAC,KAOtB;IACC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,eAAe,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAClE,OAAO,CACL,MAAC,KAAK,IAAC,SAAS,EAAC,UAAU,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,aAC5E,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK,EAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,OAAO,EAAE,KAAK,CAAC,UAAU,EACzB,OAAO,EAAE,KAAK,CAAC,KAAK,iBACR,mBAAmB,oBAChB,MAAM,2BACC,8DAAyD,YAE9E,KAAK,CAAC,UAAU;oBACf,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC;oBACjC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,GAC1B,EACR,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CACb,MAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,oBAAoB,aAC/D,IAAI,CAAC,MAAM,EACZ,KAAC,UAAU,IAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAC,oBAAoB,GAAG,IAC7C,CACnB,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,UAAU,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,GAAI,IAC1C,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,OAAO,CACL,KAAC,eAAe,IACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,KACpB,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAClE,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAErE,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAC/C,KAAC,aAAa,IACZ,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,MAAM,EAAE,KAAK,CAAC,MAAM,GACpB,CACH,GACe,CACnB,CAAC;AACJ,CAAC"}
|
package/dist/default/index.d.ts
CHANGED
|
@@ -18,4 +18,6 @@ export { ConversationThreadPanel } from "./ConversationThreadPanel.js";
|
|
|
18
18
|
export type { ConversationThreadPanelProps } from "./ConversationThreadPanel.js";
|
|
19
19
|
export { StartChatButton } from "./StartChatButton.js";
|
|
20
20
|
export type { StartChatButtonProps } from "./StartChatButton.js";
|
|
21
|
+
export { SignInLink } from "./SignInLink.js";
|
|
22
|
+
export type { SignInLinkProps } from "./SignInLink.js";
|
|
21
23
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/default/index.js
CHANGED
|
@@ -15,4 +15,5 @@
|
|
|
15
15
|
export { ConversationListPanel } from "./ConversationListPanel.js";
|
|
16
16
|
export { ConversationThreadPanel } from "./ConversationThreadPanel.js";
|
|
17
17
|
export { StartChatButton } from "./StartChatButton.js";
|
|
18
|
+
export { SignInLink } from "./SignInLink.js";
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/flows/freshness.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ── What changed since the spec ─────────────────────────────────────────────
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* The storefront spec §3.6 ruled polling-by-seq for chat v1,
|
|
8
8
|
* and it was right about the fleet it surveyed: stapel-chat had the resumable
|
|
9
9
|
* consumer but `routing.py` exported nothing, so no host could mount it.
|
|
10
10
|
* stapel-chat 0.2.2 ships that mount (`ws/chat/<uuid:conversation_id>`) and
|
|
11
|
-
* the
|
|
11
|
+
* the client fleet runs it. So the pair carries BOTH transports and picks at
|
|
12
12
|
* runtime — a deployment without sockets (WSGI, no channel layer, a
|
|
13
13
|
* misconfigured proxy) is not a broken chat, it is a chat that refreshes on a
|
|
14
14
|
* timer.
|
|
@@ -21,6 +21,11 @@ export interface StartDirectChatBag {
|
|
|
21
21
|
* seller all land in the same conversation rather than fanning out into
|
|
22
22
|
* near-duplicate threads.
|
|
23
23
|
*
|
|
24
|
+
* WHO MAY PRESS IT. The write is `IsAuthenticated`, so the first gate is the
|
|
25
|
+
* mandate axis: a visitor is told to sign in BEFORE the click rather than
|
|
26
|
+
* being handed a 401 after it. `<StartChatButton signIn={…}>` puts the door
|
|
27
|
+
* next to that sentence.
|
|
28
|
+
*
|
|
24
29
|
* WHAT THIS BUTTON CANNOT DO, AND WHY IT SAYS SO HERE. It cannot open a
|
|
25
30
|
* thread ABOUT a listing. The thread is keyed by the pair of people, and
|
|
26
31
|
* `CreateConversationRequest.scope_key` — which looks like the place to put a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartDirectChat.d.ts","sourceRoot":"","sources":["../../src/headless/StartDirectChat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"StartDirectChat.d.ts","sourceRoot":"","sources":["../../src/headless/StartDirectChat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAQvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD,mDAAmD;AACnD,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,SAAS,CAAC;CAClD,GAAG,SAAS,CAsDZ"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
|
-
import { actionAvailable, actionBlocked, firstBlock } from "@stapel/core";
|
|
2
|
+
import { actionAvailable, actionBlocked, firstBlock, matchMandate, useMandate, } from "@stapel/core";
|
|
3
3
|
import { useStartDirectChat } from "../model/mutations.js";
|
|
4
4
|
import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
5
5
|
/**
|
|
@@ -12,6 +12,11 @@ import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
|
12
12
|
* seller all land in the same conversation rather than fanning out into
|
|
13
13
|
* near-duplicate threads.
|
|
14
14
|
*
|
|
15
|
+
* WHO MAY PRESS IT. The write is `IsAuthenticated`, so the first gate is the
|
|
16
|
+
* mandate axis: a visitor is told to sign in BEFORE the click rather than
|
|
17
|
+
* being handed a 401 after it. `<StartChatButton signIn={…}>` puts the door
|
|
18
|
+
* next to that sentence.
|
|
19
|
+
*
|
|
15
20
|
* WHAT THIS BUTTON CANNOT DO, AND WHY IT SAYS SO HERE. It cannot open a
|
|
16
21
|
* thread ABOUT a listing. The thread is keyed by the pair of people, and
|
|
17
22
|
* `CreateConversationRequest.scope_key` — which looks like the place to put a
|
|
@@ -22,7 +27,29 @@ import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
|
22
27
|
export function StartDirectChat(props) {
|
|
23
28
|
const start = useStartDirectChat();
|
|
24
29
|
const { sellerId, viewerId, onOpened } = props;
|
|
25
|
-
|
|
30
|
+
/**
|
|
31
|
+
* May this person write at all? `POST /conversations/` is
|
|
32
|
+
* `IsAuthenticated`, so a visitor pressing this bought a 401 — a refusal
|
|
33
|
+
* delivered after the click, which is the one moment it is useless. The
|
|
34
|
+
* axis is read through core's `MandateSource` seam, never derived here: a
|
|
35
|
+
* storefront's derivation is "is there a session?", a tenant app's is
|
|
36
|
+
* `@stapel/workspaces-react`'s.
|
|
37
|
+
*
|
|
38
|
+
* `unavailable` stays AVAILABLE, unlike the other four arms. Outside a
|
|
39
|
+
* `<MandateProvider>` core answers `unresolved/unavailable`, and a host that
|
|
40
|
+
* never wired the axis must not lose its button — "we could not ask" is not
|
|
41
|
+
* "you may not" (the storefront spec's own §7.4 negative leg), and if the
|
|
42
|
+
* guess is wrong the module answers 401 exactly as it did before.
|
|
43
|
+
*/
|
|
44
|
+
const mandate = useMandate();
|
|
45
|
+
const mandateGate = matchMandate(mandate, {
|
|
46
|
+
member: () => actionAvailable(),
|
|
47
|
+
guest: () => actionBlocked(CHAT_I18N_KEYS.startBlockedSignIn),
|
|
48
|
+
anonymous: () => actionBlocked(CHAT_I18N_KEYS.startBlockedSignIn),
|
|
49
|
+
asking: () => actionBlocked(CHAT_I18N_KEYS.startBlockedMandateUnknown),
|
|
50
|
+
unavailable: () => actionAvailable(),
|
|
51
|
+
});
|
|
52
|
+
const availability = firstBlock(mandateGate, sellerId === null || sellerId === undefined || sellerId.length === 0
|
|
26
53
|
? actionBlocked(CHAT_I18N_KEYS.startBlockedUnknownSeller)
|
|
27
54
|
: actionAvailable(), viewerId != null && viewerId === sellerId
|
|
28
55
|
? actionBlocked(CHAT_I18N_KEYS.startBlockedSelf)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartDirectChat.js","sourceRoot":"","sources":["../../src/headless/StartDirectChat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,
|
|
1
|
+
{"version":3,"file":"StartDirectChat.js","sourceRoot":"","sources":["../../src/headless/StartDirectChat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EACL,eAAe,EACf,aAAa,EACb,UAAU,EACV,YAAY,EACZ,UAAU,GACX,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAajD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,eAAe,CAAC,KAQ/B;IACC,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAG,YAAY,CAAqB,OAAO,EAAE;QAC5D,MAAM,EAAE,GAAG,EAAE,CAAC,eAAe,EAAE;QAC/B,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,kBAAkB,CAAC;QAC7D,SAAS,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,kBAAkB,CAAC;QACjE,MAAM,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,0BAA0B,CAAC;QACtE,WAAW,EAAE,GAAG,EAAE,CAAC,eAAe,EAAE;KACrC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAuB,UAAU,CACjD,WAAW,EACX,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAClE,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC,yBAAyB,CAAC;QACzD,CAAC,CAAC,eAAe,EAAE,EACrB,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,QAAQ;QACvC,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC,gBAAgB,CAAC;QAChD,CAAC,CAAC,eAAe,EAAE,CACtB,CAAC;IAEF,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACjD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAS,EAAE;QACrC,IAAI,CAAC,YAAY,CAAC,SAAS,IAAI,SAAS;YAAE,OAAO;QACjD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO;QACxD,MAAM,CACJ,EAAE,MAAM,EAAE,QAAQ,EAAE,EACpB,EAAE,SAAS,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,CAC1D,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEpE,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,YAAY;QACZ,UAAU,EAAE,SAAS;QACrB,YAAY,EAAE,IAAI;QAClB,KAAK;QACL,KAAK,EAAE,OAAO;KACf,CAAC,CAAC;AACL,CAAC"}
|
package/dist/i18n/es.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAoE9B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAG3D"}
|
package/dist/i18n/es.js
CHANGED
|
@@ -57,6 +57,9 @@ export const chatI18nBundleEs = {
|
|
|
57
57
|
"chat.start.starting": "Abriendo…",
|
|
58
58
|
"chat.start.blocked.self": "Este anuncio es tuyo.",
|
|
59
59
|
"chat.start.blocked.unknown_seller": "Este anuncio no tiene vendedor a quien escribir.",
|
|
60
|
+
"chat.start.blocked.sign_in": "Inicia sesión para escribir al vendedor.",
|
|
61
|
+
"chat.start.blocked.mandate_unknown": "Comprobando tu sesión…",
|
|
62
|
+
"chat.start.sign_in": "Iniciar sesión",
|
|
60
63
|
"chat.transport.live": "En directo",
|
|
61
64
|
"chat.transport.polling": "Actualizando cada pocos segundos",
|
|
62
65
|
"chat.transport.idle": "En pausa",
|
package/dist/i18n/es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,oEAAoE;IACpE,GAAG,iBAAiB;IAEpB,6EAA6E;IAC7E,qCAAqC,EACnC,gEAAgE;IAClE,8BAA8B,EAAE,gDAAgD;IAChF,8BAA8B,EAC5B,4DAA4D;IAC9D,+BAA+B,EAC7B,+DAA+D;IACjE,6BAA6B,EAAE,kCAAkC;IACjE,8BAA8B,EAC5B,8DAA8D;IAChE,8BAA8B,EAC5B,kEAAkE;IACpE,4BAA4B,EAC1B,wDAAwD;IAC1D,6BAA6B,EAC3B,wDAAwD;IAC1D,gCAAgC,EAAE,oCAAoC;IACtE,uCAAuC,EAAE,4BAA4B;IACrE,iCAAiC,EAAE,+CAA+C;IAElF,mEAAmE;IACnE,oBAAoB,EAAE,yCAAyC;IAE/D,iBAAiB,EAAE,UAAU;IAC7B,iBAAiB,EAAE,gCAAgC;IACnD,mBAAmB,EAAE,0BAA0B;IAC/C,qBAAqB,EAAE,YAAY;IACnC,eAAe,EAAE,eAAe;IAChC,iBAAiB,EAAE,YAAY;IAC/B,kBAAkB,EAAE,kBAAkB;IACtC,gBAAgB,EAAE,OAAO;IAEzB,kBAAkB,EAAE,iBAAiB;IACrC,iBAAiB,EAAE,OAAO;IAC1B,mBAAmB,EAAE,SAAS;IAE9B,qBAAqB,EAAE,oBAAoB;IAC3C,mBAAmB,EAAE,kCAAkC;IACvD,mBAAmB,EAAE,YAAY;IACjC,wBAAwB,EAAE,yBAAyB;IACnD,uBAAuB,EAAE,0CAA0C;IACnE,oBAAoB,EAAE,SAAS;IAE/B,2BAA2B,EAAE,qBAAqB;IAClD,oBAAoB,EAAE,QAAQ;IAC9B,uBAAuB,EAAE,WAAW;IACpC,6BAA6B,EAAE,uBAAuB;IACtD,gCAAgC,EAC9B,qDAAqD;IAEvD,mBAAmB,EAAE,sBAAsB;IAC3C,qBAAqB,EAAE,WAAW;IAClC,yBAAyB,EAAE,uBAAuB;IAClD,mCAAmC,EAAE,kDAAkD;
|
|
1
|
+
{"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,oEAAoE;IACpE,GAAG,iBAAiB;IAEpB,6EAA6E;IAC7E,qCAAqC,EACnC,gEAAgE;IAClE,8BAA8B,EAAE,gDAAgD;IAChF,8BAA8B,EAC5B,4DAA4D;IAC9D,+BAA+B,EAC7B,+DAA+D;IACjE,6BAA6B,EAAE,kCAAkC;IACjE,8BAA8B,EAC5B,8DAA8D;IAChE,8BAA8B,EAC5B,kEAAkE;IACpE,4BAA4B,EAC1B,wDAAwD;IAC1D,6BAA6B,EAC3B,wDAAwD;IAC1D,gCAAgC,EAAE,oCAAoC;IACtE,uCAAuC,EAAE,4BAA4B;IACrE,iCAAiC,EAAE,+CAA+C;IAElF,mEAAmE;IACnE,oBAAoB,EAAE,yCAAyC;IAE/D,iBAAiB,EAAE,UAAU;IAC7B,iBAAiB,EAAE,gCAAgC;IACnD,mBAAmB,EAAE,0BAA0B;IAC/C,qBAAqB,EAAE,YAAY;IACnC,eAAe,EAAE,eAAe;IAChC,iBAAiB,EAAE,YAAY;IAC/B,kBAAkB,EAAE,kBAAkB;IACtC,gBAAgB,EAAE,OAAO;IAEzB,kBAAkB,EAAE,iBAAiB;IACrC,iBAAiB,EAAE,OAAO;IAC1B,mBAAmB,EAAE,SAAS;IAE9B,qBAAqB,EAAE,oBAAoB;IAC3C,mBAAmB,EAAE,kCAAkC;IACvD,mBAAmB,EAAE,YAAY;IACjC,wBAAwB,EAAE,yBAAyB;IACnD,uBAAuB,EAAE,0CAA0C;IACnE,oBAAoB,EAAE,SAAS;IAE/B,2BAA2B,EAAE,qBAAqB;IAClD,oBAAoB,EAAE,QAAQ;IAC9B,uBAAuB,EAAE,WAAW;IACpC,6BAA6B,EAAE,uBAAuB;IACtD,gCAAgC,EAC9B,qDAAqD;IAEvD,mBAAmB,EAAE,sBAAsB;IAC3C,qBAAqB,EAAE,WAAW;IAClC,yBAAyB,EAAE,uBAAuB;IAClD,mCAAmC,EAAE,kDAAkD;IACvF,4BAA4B,EAAE,0CAA0C;IACxE,oCAAoC,EAAE,wBAAwB;IAC9D,oBAAoB,EAAE,gBAAgB;IAEtC,qBAAqB,EAAE,YAAY;IACnC,wBAAwB,EAAE,kCAAkC;IAC5D,qBAAqB,EAAE,UAAU;IAEjC,wBAAwB,EAAE,UAAU;CACrC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC9C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/i18n/keys.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export declare const CHAT_I18N_KEYS: {
|
|
|
35
35
|
readonly startStarting: "chat.start.starting";
|
|
36
36
|
readonly startBlockedSelf: "chat.start.blocked.self";
|
|
37
37
|
readonly startBlockedUnknownSeller: "chat.start.blocked.unknown_seller";
|
|
38
|
+
/** A visitor: the POST is `IsAuthenticated`, so say so before the click. */
|
|
39
|
+
readonly startBlockedSignIn: "chat.start.blocked.sign_in";
|
|
40
|
+
/** We have not finished asking who this is. Not "you may not". */
|
|
41
|
+
readonly startBlockedMandateUnknown: "chat.start.blocked.mandate_unknown";
|
|
42
|
+
/** The door beside a blocked "message the seller": the container says WHERE. */
|
|
43
|
+
readonly startSignIn: "chat.start.sign_in";
|
|
38
44
|
readonly transportLive: "chat.transport.live";
|
|
39
45
|
readonly transportPolling: "chat.transport.polling";
|
|
40
46
|
readonly transportIdle: "chat.transport.idle";
|
package/dist/i18n/keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuCzB,4EAA4E;;IAE5E,kEAAkE;;IAElE,gFAAgF;;;;;;;;;;;;;;;;;;CA+BxE,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,cA+C9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAExE"}
|
package/dist/i18n/keys.js
CHANGED
|
@@ -41,6 +41,12 @@ export const CHAT_I18N_KEYS = {
|
|
|
41
41
|
startStarting: "chat.start.starting",
|
|
42
42
|
startBlockedSelf: "chat.start.blocked.self",
|
|
43
43
|
startBlockedUnknownSeller: "chat.start.blocked.unknown_seller",
|
|
44
|
+
/** A visitor: the POST is `IsAuthenticated`, so say so before the click. */
|
|
45
|
+
startBlockedSignIn: "chat.start.blocked.sign_in",
|
|
46
|
+
/** We have not finished asking who this is. Not "you may not". */
|
|
47
|
+
startBlockedMandateUnknown: "chat.start.blocked.mandate_unknown",
|
|
48
|
+
/** The door beside a blocked "message the seller": the container says WHERE. */
|
|
49
|
+
startSignIn: "chat.start.sign_in",
|
|
44
50
|
// Transport (the seam is invisible to the UI's BEHAVIOUR, but a person may
|
|
45
51
|
// still be told whether the thread is live or on a timer)
|
|
46
52
|
transportLive: "chat.transport.live",
|
|
@@ -106,6 +112,9 @@ export const chatI18nBundleEn = {
|
|
|
106
112
|
"chat.start.starting": "Opening…",
|
|
107
113
|
"chat.start.blocked.self": "This is your own listing.",
|
|
108
114
|
"chat.start.blocked.unknown_seller": "This listing has no seller to write to.",
|
|
115
|
+
"chat.start.blocked.sign_in": "Sign in to message the seller.",
|
|
116
|
+
"chat.start.blocked.mandate_unknown": "Checking your session…",
|
|
117
|
+
"chat.start.sign_in": "Sign in",
|
|
109
118
|
"chat.transport.live": "Live",
|
|
110
119
|
"chat.transport.polling": "Refreshing every few seconds",
|
|
111
120
|
"chat.transport.idle": "Paused",
|
package/dist/i18n/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,oBAAoB;IAElC,oBAAoB;IACpB,SAAS,EAAE,iBAAiB;IAC5B,SAAS,EAAE,iBAAiB;IAC5B,WAAW,EAAE,mBAAmB;IAChC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,eAAe;IACxB,SAAS,EAAE,iBAAiB;IAC5B,UAAU,EAAE,kBAAkB;IAC9B,QAAQ,EAAE,gBAAgB;IAE1B,uEAAuE;IACvE,+CAA+C;IAC/C,UAAU,EAAE,kBAAkB;IAC9B,SAAS,EAAE,iBAAiB;IAC5B,WAAW,EAAE,mBAAmB;IAEhC,SAAS;IACT,aAAa,EAAE,qBAAqB;IACpC,WAAW,EAAE,mBAAmB;IAChC,WAAW,EAAE,mBAAmB;IAChC,eAAe,EAAE,wBAAwB;IACzC,eAAe,EAAE,uBAAuB;IACxC,YAAY,EAAE,oBAAoB;IAElC,WAAW;IACX,mBAAmB,EAAE,2BAA2B;IAChD,YAAY,EAAE,oBAAoB;IAClC,eAAe,EAAE,uBAAuB;IACxC,oBAAoB,EAAE,6BAA6B;IACnD,sBAAsB,EAAE,gCAAgC;IAExD,uBAAuB;IACvB,WAAW,EAAE,mBAAmB;IAChC,aAAa,EAAE,qBAAqB;IACpC,gBAAgB,EAAE,yBAAyB;IAC3C,yBAAyB,EAAE,mCAAmC;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,oBAAoB;IAElC,oBAAoB;IACpB,SAAS,EAAE,iBAAiB;IAC5B,SAAS,EAAE,iBAAiB;IAC5B,WAAW,EAAE,mBAAmB;IAChC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,eAAe;IACxB,SAAS,EAAE,iBAAiB;IAC5B,UAAU,EAAE,kBAAkB;IAC9B,QAAQ,EAAE,gBAAgB;IAE1B,uEAAuE;IACvE,+CAA+C;IAC/C,UAAU,EAAE,kBAAkB;IAC9B,SAAS,EAAE,iBAAiB;IAC5B,WAAW,EAAE,mBAAmB;IAEhC,SAAS;IACT,aAAa,EAAE,qBAAqB;IACpC,WAAW,EAAE,mBAAmB;IAChC,WAAW,EAAE,mBAAmB;IAChC,eAAe,EAAE,wBAAwB;IACzC,eAAe,EAAE,uBAAuB;IACxC,YAAY,EAAE,oBAAoB;IAElC,WAAW;IACX,mBAAmB,EAAE,2BAA2B;IAChD,YAAY,EAAE,oBAAoB;IAClC,eAAe,EAAE,uBAAuB;IACxC,oBAAoB,EAAE,6BAA6B;IACnD,sBAAsB,EAAE,gCAAgC;IAExD,uBAAuB;IACvB,WAAW,EAAE,mBAAmB;IAChC,aAAa,EAAE,qBAAqB;IACpC,gBAAgB,EAAE,yBAAyB;IAC3C,yBAAyB,EAAE,mCAAmC;IAC9D,4EAA4E;IAC5E,kBAAkB,EAAE,4BAA4B;IAChD,kEAAkE;IAClE,0BAA0B,EAAE,oCAAoC;IAChE,gFAAgF;IAChF,WAAW,EAAE,oBAAoB;IAEjC,2EAA2E;IAC3E,0DAA0D;IAC1D,aAAa,EAAE,qBAAqB;IACpC,gBAAgB,EAAE,wBAAwB;IAC1C,aAAa,EAAE,qBAAqB;IAEpC,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,sEAAsE;IACtE,wEAAwE;IACxE,4BAA4B;IAC5B,iBAAiB,EAAE,8BAA8B;IACjD,gBAAgB,EAAE,8BAA8B;IAChD,wBAAwB,EAAE,qCAAqC;IAC/D,kBAAkB,EAAE,+BAA+B;IACnD,gBAAgB,EAAE,6BAA6B;IAC/C,iBAAiB,EAAE,8BAA8B;IACjD,iBAAiB,EAAE,8BAA8B;IACjD,eAAe,EAAE,4BAA4B;IAC7C,gBAAgB,EAAE,6BAA6B;IAC/C,mBAAmB,EAAE,gCAAgC;IACrD,yBAAyB,EAAE,uCAAuC;IAClE,oBAAoB,EAAE,iCAAiC;IAEvD,kEAAkE;IAClE,uBAAuB;IACvB,gBAAgB,EAAE,wBAAwB;CAClC,CAAC;AAIX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,2EAA2E;IAC3E,GAAG,iBAAiB;IAEpB,gBAAgB;IAChB,oBAAoB,EAAE,yCAAyC;IAE/D,iBAAiB,EAAE,UAAU;IAC7B,iBAAiB,EAAE,uBAAuB;IAC1C,mBAAmB,EAAE,wBAAwB;IAC7C,qBAAqB,EAAE,WAAW;IAClC,eAAe,EAAE,oBAAoB;IACrC,iBAAiB,EAAE,WAAW;IAC9B,kBAAkB,EAAE,gBAAgB;IACpC,gBAAgB,EAAE,MAAM;IAExB,kBAAkB,EAAE,gBAAgB;IACpC,iBAAiB,EAAE,OAAO;IAC1B,mBAAmB,EAAE,SAAS;IAE9B,qBAAqB,EAAE,mBAAmB;IAC1C,mBAAmB,EAAE,6BAA6B;IAClD,mBAAmB,EAAE,WAAW;IAChC,wBAAwB,EAAE,uBAAuB;IACjD,uBAAuB,EAAE,4CAA4C;IACrE,oBAAoB,EAAE,QAAQ;IAE9B,2BAA2B,EAAE,kBAAkB;IAC/C,oBAAoB,EAAE,MAAM;IAC5B,uBAAuB,EAAE,UAAU;IACnC,6BAA6B,EAAE,wBAAwB;IACvD,gCAAgC,EAC9B,6DAA6D;IAE/D,mBAAmB,EAAE,oBAAoB;IACzC,qBAAqB,EAAE,UAAU;IACjC,yBAAyB,EAAE,2BAA2B;IACtD,mCAAmC,EAAE,yCAAyC;IAC9E,4BAA4B,EAAE,gCAAgC;IAC9D,oCAAoC,EAAE,wBAAwB;IAC9D,oBAAoB,EAAE,SAAS;IAE/B,qBAAqB,EAAE,MAAM;IAC7B,wBAAwB,EAAE,8BAA8B;IACxD,qBAAqB,EAAE,QAAQ;IAE/B,wBAAwB,EAAE,UAAU;CACrC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IAChE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/i18n/ru.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ru.d.ts","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"ru.d.ts","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAkE9B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAG3D"}
|
package/dist/i18n/ru.js
CHANGED
|
@@ -61,6 +61,9 @@ export const chatI18nBundleRu = {
|
|
|
61
61
|
"chat.start.starting": "Открываем…",
|
|
62
62
|
"chat.start.blocked.self": "Это ваше собственное объявление.",
|
|
63
63
|
"chat.start.blocked.unknown_seller": "У этого объявления не указан продавец.",
|
|
64
|
+
"chat.start.blocked.sign_in": "Войдите, чтобы написать продавцу.",
|
|
65
|
+
"chat.start.blocked.mandate_unknown": "Проверяем вашу сессию…",
|
|
66
|
+
"chat.start.sign_in": "Войти",
|
|
64
67
|
"chat.transport.live": "На связи",
|
|
65
68
|
"chat.transport.polling": "Обновляется каждые несколько секунд",
|
|
66
69
|
"chat.transport.idle": "Приостановлено",
|
package/dist/i18n/ru.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ru.js","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,oEAAoE;IACpE,GAAG,iBAAiB;IAEpB,6EAA6E;IAC7E,qCAAqC,EAAE,qCAAqC;IAC5E,8BAA8B,EAAE,uCAAuC;IACvE,8BAA8B,EAC5B,yDAAyD;IAC3D,+BAA+B,EAC7B,sDAAsD;IACxD,6BAA6B,EAAE,yBAAyB;IACxD,8BAA8B,EAC5B,yDAAyD;IAC3D,8BAA8B,EAAE,4CAA4C;IAC5E,4BAA4B,EAC1B,wDAAwD;IAC1D,6BAA6B,EAC3B,kDAAkD;IACpD,gCAAgC,EAAE,8BAA8B;IAChE,uCAAuC,EAAE,kBAAkB;IAC3D,iCAAiC,EAAE,kCAAkC;IAErE,mEAAmE;IACnE,oBAAoB,EAAE,0CAA0C;IAEhE,iBAAiB,EAAE,WAAW;IAC9B,iBAAiB,EAAE,oBAAoB;IACvC,mBAAmB,EAAE,oBAAoB;IACzC,qBAAqB,EAAE,cAAc;IACrC,eAAe,EAAE,kBAAkB;IACnC,iBAAiB,EAAE,WAAW;IAC9B,kBAAkB,EAAE,wBAAwB;IAC5C,gBAAgB,EAAE,SAAS;IAE3B,kBAAkB,EAAE,kBAAkB;IACtC,iBAAiB,EAAE,kBAAkB;IACrC,mBAAmB,EAAE,WAAW;IAEhC,qBAAqB,EAAE,qBAAqB;IAC5C,mBAAmB,EAAE,sCAAsC;IAC3D,mBAAmB,EAAE,WAAW;IAChC,wBAAwB,EAAE,uBAAuB;IACjD,uBAAuB,EAAE,uBAAuB;IAChD,oBAAoB,EAAE,qBAAqB;IAE3C,2BAA2B,EAAE,qBAAqB;IAClD,oBAAoB,EAAE,WAAW;IACjC,uBAAuB,EAAE,WAAW;IACpC,6BAA6B,EAAE,yBAAyB;IACxD,gCAAgC,EAC9B,iDAAiD;IAEnD,mBAAmB,EAAE,mBAAmB;IACxC,qBAAqB,EAAE,YAAY;IACnC,yBAAyB,EAAE,kCAAkC;IAC7D,mCAAmC,EAAE,wCAAwC;
|
|
1
|
+
{"version":3,"file":"ru.js","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,oEAAoE;IACpE,GAAG,iBAAiB;IAEpB,6EAA6E;IAC7E,qCAAqC,EAAE,qCAAqC;IAC5E,8BAA8B,EAAE,uCAAuC;IACvE,8BAA8B,EAC5B,yDAAyD;IAC3D,+BAA+B,EAC7B,sDAAsD;IACxD,6BAA6B,EAAE,yBAAyB;IACxD,8BAA8B,EAC5B,yDAAyD;IAC3D,8BAA8B,EAAE,4CAA4C;IAC5E,4BAA4B,EAC1B,wDAAwD;IAC1D,6BAA6B,EAC3B,kDAAkD;IACpD,gCAAgC,EAAE,8BAA8B;IAChE,uCAAuC,EAAE,kBAAkB;IAC3D,iCAAiC,EAAE,kCAAkC;IAErE,mEAAmE;IACnE,oBAAoB,EAAE,0CAA0C;IAEhE,iBAAiB,EAAE,WAAW;IAC9B,iBAAiB,EAAE,oBAAoB;IACvC,mBAAmB,EAAE,oBAAoB;IACzC,qBAAqB,EAAE,cAAc;IACrC,eAAe,EAAE,kBAAkB;IACnC,iBAAiB,EAAE,WAAW;IAC9B,kBAAkB,EAAE,wBAAwB;IAC5C,gBAAgB,EAAE,SAAS;IAE3B,kBAAkB,EAAE,kBAAkB;IACtC,iBAAiB,EAAE,kBAAkB;IACrC,mBAAmB,EAAE,WAAW;IAEhC,qBAAqB,EAAE,qBAAqB;IAC5C,mBAAmB,EAAE,sCAAsC;IAC3D,mBAAmB,EAAE,WAAW;IAChC,wBAAwB,EAAE,uBAAuB;IACjD,uBAAuB,EAAE,uBAAuB;IAChD,oBAAoB,EAAE,qBAAqB;IAE3C,2BAA2B,EAAE,qBAAqB;IAClD,oBAAoB,EAAE,WAAW;IACjC,uBAAuB,EAAE,WAAW;IACpC,6BAA6B,EAAE,yBAAyB;IACxD,gCAAgC,EAC9B,iDAAiD;IAEnD,mBAAmB,EAAE,mBAAmB;IACxC,qBAAqB,EAAE,YAAY;IACnC,yBAAyB,EAAE,kCAAkC;IAC7D,mCAAmC,EAAE,wCAAwC;IAC7E,4BAA4B,EAAE,mCAAmC;IACjE,oCAAoC,EAAE,wBAAwB;IAC9D,oBAAoB,EAAE,OAAO;IAE7B,qBAAqB,EAAE,UAAU;IACjC,wBAAwB,EAAE,qCAAqC;IAC/D,qBAAqB,EAAE,gBAAgB;IAEvC,wBAAwB,EAAE,WAAW;CACtC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC9C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/model/runtime.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { ChatReconnectOptions, ChatWebSocketFactory } from "../realtime/cha
|
|
|
10
10
|
export interface ChatRealtimeOptions {
|
|
11
11
|
/**
|
|
12
12
|
* Where this deployment mounts `stapel_chat.routing.websocket_urlpatterns`,
|
|
13
|
-
* e.g. `wss://
|
|
13
|
+
* e.g. `wss://shop.example/ws/chat/`. Omit to derive it from `baseUrl`
|
|
14
14
|
* (the canonical `/ws/chat/` at the API's own origin). Pass `null` to turn
|
|
15
15
|
* the socket transport OFF explicitly — a host that knows its backend runs
|
|
16
16
|
* under WSGI says so here instead of letting every tab fail a handshake
|
package/llms.txt
CHANGED
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
|
|
3
3
|
"package": "@stapel/chat-react",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"backend": {
|
|
6
6
|
"module": "stapel-chat",
|
|
7
7
|
"contract": ">=0.2 <0.3"
|
|
@@ -528,9 +528,12 @@
|
|
|
528
528
|
"chat.list.title",
|
|
529
529
|
"chat.list.unread",
|
|
530
530
|
"chat.nav.conversations",
|
|
531
|
+
"chat.start.blocked.mandate_unknown",
|
|
531
532
|
"chat.start.blocked.self",
|
|
533
|
+
"chat.start.blocked.sign_in",
|
|
532
534
|
"chat.start.blocked.unknown_seller",
|
|
533
535
|
"chat.start.button",
|
|
536
|
+
"chat.start.sign_in",
|
|
534
537
|
"chat.start.starting",
|
|
535
538
|
"chat.thread.beginning",
|
|
536
539
|
"chat.thread.empty",
|
package/nav-manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/chat-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Headless React pair for stapel-chat: typed API client, TanStack Query hooks, a seq-ordered thread store, and ONE transport seam behind which the module's own resumable WebSocket protocol (hello/welcome/replay/replay_done/message/error) and seq-anchored polling are interchangeable — the UI never knows which is live. Conversation list with unread badges, thread replay + live tail, REST send, direct-thread idempotency for \"message the seller\", monotonic read markers. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -47,13 +47,6 @@
|
|
|
47
47
|
"nav-manifest.json",
|
|
48
48
|
"llms.txt"
|
|
49
49
|
],
|
|
50
|
-
"scripts": {
|
|
51
|
-
"build": "tsc -p tsconfig.json",
|
|
52
|
-
"test": "tsc -p tsconfig.demo.json && vitest run --exclude test/prodBundlePurity.test.ts",
|
|
53
|
-
"test:pack": "vitest run test/prodBundlePurity.test.ts",
|
|
54
|
-
"lint": "eslint .",
|
|
55
|
-
"size": "size-limit"
|
|
56
|
-
},
|
|
57
50
|
"size-limit": [
|
|
58
51
|
{
|
|
59
52
|
"name": "index — headless pair (client + hooks + thread store + transport seam)",
|
|
@@ -77,7 +70,7 @@
|
|
|
77
70
|
}
|
|
78
71
|
],
|
|
79
72
|
"peerDependencies": {
|
|
80
|
-
"@stapel/core": ">=0.
|
|
73
|
+
"@stapel/core": ">=0.15.0 <1.0.0",
|
|
81
74
|
"@tanstack/react-query": "^5.0.0",
|
|
82
75
|
"antd": ">=5.20.0 <7",
|
|
83
76
|
"react": ">=19"
|
|
@@ -89,9 +82,6 @@
|
|
|
89
82
|
},
|
|
90
83
|
"devDependencies": {
|
|
91
84
|
"@size-limit/preset-small-lib": "^11.2.0",
|
|
92
|
-
"@stapel/core": "workspace:^",
|
|
93
|
-
"@stapel/showcase": "workspace:^",
|
|
94
|
-
"@stapel/tokens": "workspace:^",
|
|
95
85
|
"@tanstack/react-query": "^5.81.0",
|
|
96
86
|
"@testing-library/react": "^16.3.0",
|
|
97
87
|
"@types/react": "^19.1.0",
|
|
@@ -103,12 +93,22 @@
|
|
|
103
93
|
"react-dom": "^19.1.0",
|
|
104
94
|
"size-limit": "^11.2.0",
|
|
105
95
|
"typescript": "^5.8.3",
|
|
106
|
-
"vitest": "^3.2.4"
|
|
96
|
+
"vitest": "^3.2.4",
|
|
97
|
+
"@stapel/core": "^0.16.0",
|
|
98
|
+
"@stapel/showcase": "^0.2.0",
|
|
99
|
+
"@stapel/tokens": "^0.5.0"
|
|
107
100
|
},
|
|
108
101
|
"engines": {
|
|
109
102
|
"node": ">=22"
|
|
110
103
|
},
|
|
111
104
|
"publishConfig": {
|
|
112
105
|
"access": "public"
|
|
106
|
+
},
|
|
107
|
+
"scripts": {
|
|
108
|
+
"build": "tsc -p tsconfig.json",
|
|
109
|
+
"test": "tsc -p tsconfig.demo.json && vitest run --exclude test/prodBundlePurity.test.ts",
|
|
110
|
+
"test:pack": "vitest run test/prodBundlePurity.test.ts",
|
|
111
|
+
"lint": "eslint .",
|
|
112
|
+
"size": "size-limit"
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door beside a blocked control.
|
|
3
|
+
*
|
|
4
|
+
* `actionBlocked` ended the grey-rectangle incident by making every
|
|
5
|
+
* switched-off control state its reason. It did not end the next problem:
|
|
6
|
+
* "sign in to message the seller" is a reason whose next action is a LINK, and
|
|
7
|
+
* this pair rendered the sentence and stopped there — leaving the visitor to
|
|
8
|
+
* find the header themselves (storefront Wave D, G-3).
|
|
9
|
+
*
|
|
10
|
+
* WHERE that link goes is the container's business, never the pair's: the
|
|
11
|
+
* storefront's is `/login?next=<current>`, a tenant app's may be a modal. So
|
|
12
|
+
* the shape is core's `SignInCta` — `{href}` or `{onSignIn}`, never both — and
|
|
13
|
+
* the copy is this pair's, because core floors `en` and `ru` while this pair
|
|
14
|
+
* also ships `es`.
|
|
15
|
+
*
|
|
16
|
+
* A host that routes internally passes `onSignIn`; the `href` arm renders a
|
|
17
|
+
* plain anchor on purpose, because arriving at a sign-in page is one of the
|
|
18
|
+
* few navigations a full load costs nothing.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReactElement } from "react";
|
|
21
|
+
import { Typography } from "antd";
|
|
22
|
+
import { useT } from "@stapel/core";
|
|
23
|
+
import type { SignInCta } from "@stapel/core";
|
|
24
|
+
import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
25
|
+
|
|
26
|
+
export interface SignInLinkProps {
|
|
27
|
+
/** Absent: no link — a host with no sign-in route shows the reason alone. */
|
|
28
|
+
readonly cta: SignInCta | undefined;
|
|
29
|
+
readonly testId: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function SignInLink(props: SignInLinkProps): ReactElement | null {
|
|
33
|
+
const t = useT();
|
|
34
|
+
const { cta } = props;
|
|
35
|
+
if (cta === undefined) return null;
|
|
36
|
+
// The separating space belongs HERE, not at the call site: a reason with no
|
|
37
|
+
// door must render as exactly its own sentence, and a `{" "}` left behind by
|
|
38
|
+
// an absent link is a trailing space in every caller's assertion.
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{" "}
|
|
42
|
+
<Typography.Link
|
|
43
|
+
data-testid={props.testId}
|
|
44
|
+
data-analytics="none"
|
|
45
|
+
data-analytics-reason="business action — host app wraps with its own tracked()"
|
|
46
|
+
{...(cta.href !== undefined
|
|
47
|
+
? { href: cta.href }
|
|
48
|
+
: { onClick: cta.onSignIn })}
|
|
49
|
+
>
|
|
50
|
+
{t(CHAT_I18N_KEYS.startSignIn)}
|
|
51
|
+
</Typography.Link>
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
* `<StartChatButton/>` — "message the seller" on a listing card, skinned.
|
|
3
3
|
*
|
|
4
4
|
* The control is either pressable or switched off WITH a sentence beside it
|
|
5
|
-
* (no seller on the listing; the listing is your own
|
|
6
|
-
* grey rectangle with no explanation
|
|
5
|
+
* (no seller on the listing; the listing is your own; you are not signed in).
|
|
6
|
+
* It never renders as a grey rectangle with no explanation — and when the host
|
|
7
|
+
* supplies `signIn`, the sentence comes with the door: a stated reason whose
|
|
8
|
+
* next action is a link the visitor cannot find on their own is only half an
|
|
9
|
+
* answer (storefront Wave D, G-3).
|
|
7
10
|
*/
|
|
8
11
|
import type { ReactElement } from "react";
|
|
9
12
|
import { Button, Space, Typography } from "antd";
|
|
10
13
|
import { useActionGate, useErrorDisplay, useT } from "@stapel/core";
|
|
11
|
-
import type { ActionAvailability } from "@stapel/core";
|
|
14
|
+
import type { ActionAvailability, SignInCta, SignInCtaProp } from "@stapel/core";
|
|
12
15
|
import type { Conversation } from "../api/types.js";
|
|
13
16
|
import { StartDirectChat } from "../headless/StartDirectChat.js";
|
|
14
17
|
import { CHAT_I18N_KEYS } from "../i18n/keys.js";
|
|
15
18
|
import { ErrorAlert } from "./ErrorAlert.js";
|
|
19
|
+
import { SignInLink } from "./SignInLink.js";
|
|
16
20
|
|
|
17
|
-
export interface StartChatButtonProps {
|
|
21
|
+
export interface StartChatButtonProps extends SignInCtaProp {
|
|
18
22
|
sellerId: string | null | undefined;
|
|
19
23
|
viewerId?: string | null;
|
|
20
24
|
onOpened?: (conversation: Conversation) => void;
|
|
@@ -27,6 +31,7 @@ function StartChatBody(props: {
|
|
|
27
31
|
error: unknown;
|
|
28
32
|
start: () => void;
|
|
29
33
|
block: boolean | undefined;
|
|
34
|
+
signIn: SignInCta | undefined;
|
|
30
35
|
}): ReactElement {
|
|
31
36
|
const t = useT();
|
|
32
37
|
const gate = useActionGate(props.availability);
|
|
@@ -50,6 +55,7 @@ function StartChatBody(props: {
|
|
|
50
55
|
{gate.reason ? (
|
|
51
56
|
<Typography.Text type="secondary" data-testid="chat-start-blocked">
|
|
52
57
|
{gate.reason}
|
|
58
|
+
<SignInLink cta={props.signIn} testId="chat-start-sign-in" />
|
|
53
59
|
</Typography.Text>
|
|
54
60
|
) : null}
|
|
55
61
|
<ErrorAlert error={errorDisplay(props.error)} />
|
|
@@ -71,6 +77,7 @@ export function StartChatButton(props: StartChatButtonProps): ReactElement {
|
|
|
71
77
|
error={error}
|
|
72
78
|
start={start}
|
|
73
79
|
block={props.block}
|
|
80
|
+
signIn={props.signIn}
|
|
74
81
|
/>
|
|
75
82
|
)}
|
|
76
83
|
</StartDirectChat>
|
package/src/default/index.ts
CHANGED
|
@@ -18,3 +18,5 @@ export { ConversationThreadPanel } from "./ConversationThreadPanel.js";
|
|
|
18
18
|
export type { ConversationThreadPanelProps } from "./ConversationThreadPanel.js";
|
|
19
19
|
export { StartChatButton } from "./StartChatButton.js";
|
|
20
20
|
export type { StartChatButtonProps } from "./StartChatButton.js";
|
|
21
|
+
export { SignInLink } from "./SignInLink.js";
|
|
22
|
+
export type { SignInLinkProps } from "./SignInLink.js";
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
actionAvailable,
|
|
5
|
+
actionBlocked,
|
|
6
|
+
firstBlock,
|
|
7
|
+
matchMandate,
|
|
8
|
+
useMandate,
|
|
9
|
+
} from "@stapel/core";
|
|
4
10
|
import type { ActionAvailability } from "@stapel/core";
|
|
5
11
|
import type { Conversation } from "../api/types.js";
|
|
6
12
|
import { useStartDirectChat } from "../model/mutations.js";
|
|
@@ -27,6 +33,11 @@ export interface StartDirectChatBag {
|
|
|
27
33
|
* seller all land in the same conversation rather than fanning out into
|
|
28
34
|
* near-duplicate threads.
|
|
29
35
|
*
|
|
36
|
+
* WHO MAY PRESS IT. The write is `IsAuthenticated`, so the first gate is the
|
|
37
|
+
* mandate axis: a visitor is told to sign in BEFORE the click rather than
|
|
38
|
+
* being handed a 401 after it. `<StartChatButton signIn={…}>` puts the door
|
|
39
|
+
* next to that sentence.
|
|
40
|
+
*
|
|
30
41
|
* WHAT THIS BUTTON CANNOT DO, AND WHY IT SAYS SO HERE. It cannot open a
|
|
31
42
|
* thread ABOUT a listing. The thread is keyed by the pair of people, and
|
|
32
43
|
* `CreateConversationRequest.scope_key` — which looks like the place to put a
|
|
@@ -46,7 +57,31 @@ export function StartDirectChat(props: {
|
|
|
46
57
|
const start = useStartDirectChat();
|
|
47
58
|
const { sellerId, viewerId, onOpened } = props;
|
|
48
59
|
|
|
60
|
+
/**
|
|
61
|
+
* May this person write at all? `POST /conversations/` is
|
|
62
|
+
* `IsAuthenticated`, so a visitor pressing this bought a 401 — a refusal
|
|
63
|
+
* delivered after the click, which is the one moment it is useless. The
|
|
64
|
+
* axis is read through core's `MandateSource` seam, never derived here: a
|
|
65
|
+
* storefront's derivation is "is there a session?", a tenant app's is
|
|
66
|
+
* `@stapel/workspaces-react`'s.
|
|
67
|
+
*
|
|
68
|
+
* `unavailable` stays AVAILABLE, unlike the other four arms. Outside a
|
|
69
|
+
* `<MandateProvider>` core answers `unresolved/unavailable`, and a host that
|
|
70
|
+
* never wired the axis must not lose its button — "we could not ask" is not
|
|
71
|
+
* "you may not" (the storefront spec's own §7.4 negative leg), and if the
|
|
72
|
+
* guess is wrong the module answers 401 exactly as it did before.
|
|
73
|
+
*/
|
|
74
|
+
const mandate = useMandate();
|
|
75
|
+
const mandateGate = matchMandate<ActionAvailability>(mandate, {
|
|
76
|
+
member: () => actionAvailable(),
|
|
77
|
+
guest: () => actionBlocked(CHAT_I18N_KEYS.startBlockedSignIn),
|
|
78
|
+
anonymous: () => actionBlocked(CHAT_I18N_KEYS.startBlockedSignIn),
|
|
79
|
+
asking: () => actionBlocked(CHAT_I18N_KEYS.startBlockedMandateUnknown),
|
|
80
|
+
unavailable: () => actionAvailable(),
|
|
81
|
+
});
|
|
82
|
+
|
|
49
83
|
const availability: ActionAvailability = firstBlock(
|
|
84
|
+
mandateGate,
|
|
50
85
|
sellerId === null || sellerId === undefined || sellerId.length === 0
|
|
51
86
|
? actionBlocked(CHAT_I18N_KEYS.startBlockedUnknownSeller)
|
|
52
87
|
: actionAvailable(),
|
package/src/i18n/es.ts
CHANGED
|
@@ -75,6 +75,9 @@ export const chatI18nBundleEs: I18nDictionary = {
|
|
|
75
75
|
"chat.start.starting": "Abriendo…",
|
|
76
76
|
"chat.start.blocked.self": "Este anuncio es tuyo.",
|
|
77
77
|
"chat.start.blocked.unknown_seller": "Este anuncio no tiene vendedor a quien escribir.",
|
|
78
|
+
"chat.start.blocked.sign_in": "Inicia sesión para escribir al vendedor.",
|
|
79
|
+
"chat.start.blocked.mandate_unknown": "Comprobando tu sesión…",
|
|
80
|
+
"chat.start.sign_in": "Iniciar sesión",
|
|
78
81
|
|
|
79
82
|
"chat.transport.live": "En directo",
|
|
80
83
|
"chat.transport.polling": "Actualizando cada pocos segundos",
|
package/src/i18n/keys.ts
CHANGED
|
@@ -48,6 +48,12 @@ export const CHAT_I18N_KEYS = {
|
|
|
48
48
|
startStarting: "chat.start.starting",
|
|
49
49
|
startBlockedSelf: "chat.start.blocked.self",
|
|
50
50
|
startBlockedUnknownSeller: "chat.start.blocked.unknown_seller",
|
|
51
|
+
/** A visitor: the POST is `IsAuthenticated`, so say so before the click. */
|
|
52
|
+
startBlockedSignIn: "chat.start.blocked.sign_in",
|
|
53
|
+
/** We have not finished asking who this is. Not "you may not". */
|
|
54
|
+
startBlockedMandateUnknown: "chat.start.blocked.mandate_unknown",
|
|
55
|
+
/** The door beside a blocked "message the seller": the container says WHERE. */
|
|
56
|
+
startSignIn: "chat.start.sign_in",
|
|
51
57
|
|
|
52
58
|
// Transport (the seam is invisible to the UI's BEHAVIOUR, but a person may
|
|
53
59
|
// still be told whether the thread is live or on a timer)
|
|
@@ -126,6 +132,9 @@ export const chatI18nBundleEn: I18nDictionary = {
|
|
|
126
132
|
"chat.start.starting": "Opening…",
|
|
127
133
|
"chat.start.blocked.self": "This is your own listing.",
|
|
128
134
|
"chat.start.blocked.unknown_seller": "This listing has no seller to write to.",
|
|
135
|
+
"chat.start.blocked.sign_in": "Sign in to message the seller.",
|
|
136
|
+
"chat.start.blocked.mandate_unknown": "Checking your session…",
|
|
137
|
+
"chat.start.sign_in": "Sign in",
|
|
129
138
|
|
|
130
139
|
"chat.transport.live": "Live",
|
|
131
140
|
"chat.transport.polling": "Refreshing every few seconds",
|
package/src/i18n/ru.ts
CHANGED
|
@@ -77,6 +77,9 @@ export const chatI18nBundleRu: I18nDictionary = {
|
|
|
77
77
|
"chat.start.starting": "Открываем…",
|
|
78
78
|
"chat.start.blocked.self": "Это ваше собственное объявление.",
|
|
79
79
|
"chat.start.blocked.unknown_seller": "У этого объявления не указан продавец.",
|
|
80
|
+
"chat.start.blocked.sign_in": "Войдите, чтобы написать продавцу.",
|
|
81
|
+
"chat.start.blocked.mandate_unknown": "Проверяем вашу сессию…",
|
|
82
|
+
"chat.start.sign_in": "Войти",
|
|
80
83
|
|
|
81
84
|
"chat.transport.live": "На связи",
|
|
82
85
|
"chat.transport.polling": "Обновляется каждые несколько секунд",
|