@mediansh/widget 0.5.0 → 0.7.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/README.md +52 -3
- package/dist/index.d.ts +354 -19
- package/dist/index.js +2994 -1461
- package/dist/index.js.map +1 -1
- package/dist/widget.css +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@ support panel. An AI agent answers first, your team picks up in the Median
|
|
|
5
5
|
inbox, and replies stream back to the visitor. There is no connection or
|
|
6
6
|
message list to manage.
|
|
7
7
|
|
|
8
|
+
The same panel as a modal, a contact form, and a feedback panel ship
|
|
9
|
+
alongside it. Same key, same inbox.
|
|
10
|
+
|
|
8
11
|
## Install
|
|
9
12
|
|
|
10
13
|
```sh
|
|
@@ -66,6 +69,51 @@ medianSupport.reset(); // on sign out
|
|
|
66
69
|
React components can use `useMedianSupport()` for the same control with a
|
|
67
70
|
live `isOpen`.
|
|
68
71
|
|
|
72
|
+
## Modal
|
|
73
|
+
|
|
74
|
+
The widget, opened in the middle of the screen over a scrim rather than out
|
|
75
|
+
of the corner. The button stays; every prop and every control is the same.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { MedianSupportModal } from "@mediansh/widget";
|
|
79
|
+
|
|
80
|
+
<MedianSupportModal apiKey="median_pk_..." />;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`mountMedianSupportModal` is the version for pages without React. Mount one
|
|
84
|
+
of the two, not both.
|
|
85
|
+
|
|
86
|
+
## Contact form
|
|
87
|
+
|
|
88
|
+
A form where you put it, for the page where a chat panel is the wrong shape.
|
|
89
|
+
An email and a message are always asked; add the rest:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { MedianContactForm } from "@mediansh/widget";
|
|
93
|
+
|
|
94
|
+
<MedianContactForm
|
|
95
|
+
apiKey="median_pk_..."
|
|
96
|
+
fields={[
|
|
97
|
+
{ name: "name", label: "Name", required: true },
|
|
98
|
+
{ name: "topic", type: "select", options: ["Billing", "A bug"] },
|
|
99
|
+
]}
|
|
100
|
+
/>;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Sending starts a conversation in your inbox, exactly as the widget does, and
|
|
104
|
+
the reply goes to the email given. `mountMedianContactForm({ target, ... })`
|
|
105
|
+
renders it into an element of yours on a page without React.
|
|
106
|
+
|
|
107
|
+
Or keep your own form and only borrow the send:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const contact = useMedianContact({ apiKey: "median_pk_..." });
|
|
111
|
+
|
|
112
|
+
await contact.send({ email, message, fields: { Topic: topic } });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`sendMedianContact(options, message)` is the same call as a plain function.
|
|
116
|
+
|
|
69
117
|
## Feedback
|
|
70
118
|
|
|
71
119
|
A second panel, for notes rather than questions. It wraps a trigger of your
|
|
@@ -88,11 +136,12 @@ and the page it was written on rides along.
|
|
|
88
136
|
|
|
89
137
|
## Theming
|
|
90
138
|
|
|
91
|
-
|
|
92
|
-
a plain light theme without them. They inherit your font.
|
|
139
|
+
Every piece reads shadcn-style CSS variables from your page and falls back
|
|
140
|
+
to a plain light theme without them. They inherit your font.
|
|
93
141
|
|
|
94
142
|
## Docs
|
|
95
143
|
|
|
96
|
-
Props, identity, diagnostics, feedback, and
|
|
144
|
+
Props, identity, diagnostics, the modal, the contact form, feedback, and
|
|
145
|
+
agent tools:
|
|
97
146
|
[docs.median.sh](https://docs.median.sh), or the source at
|
|
98
147
|
[github.com/mediansh/median](https://github.com/mediansh/median).
|
package/dist/index.d.ts
CHANGED
|
@@ -177,16 +177,20 @@ type MedianSupportOptions = {
|
|
|
177
177
|
/** Every open and close, whoever asked for it. */
|
|
178
178
|
onOpenChange?: (open: boolean) => void;
|
|
179
179
|
/**
|
|
180
|
-
* Take the visitor to a page, when one of your own tools
|
|
180
|
+
* Take the visitor to a page, when one of your own tools offers one.
|
|
181
181
|
*
|
|
182
182
|
* <MedianSupport apiKey="median_pk_..." onNavigate={router.push} />
|
|
183
183
|
*
|
|
184
|
+
* A tool that answers with a destination puts a button under the reply
|
|
185
|
+
* that explains it, and this is what the button does. Nothing moves on its
|
|
186
|
+
* own: the visitor presses it, or they stay where they are.
|
|
187
|
+
*
|
|
184
188
|
* Called with a path on your own site, already checked against this page's
|
|
185
189
|
* origin. Your router decides what happens: pass `router.push` and the
|
|
186
190
|
* conversation stays open across the move.
|
|
187
191
|
*
|
|
188
|
-
* Without it
|
|
189
|
-
* console while you are building. Nothing else needs it.
|
|
192
|
+
* Without it there is nowhere for a press to go, so no button is shown and
|
|
193
|
+
* the console says so while you are building. Nothing else needs it.
|
|
190
194
|
*/
|
|
191
195
|
onNavigate?: (path: string) => void;
|
|
192
196
|
};
|
|
@@ -256,19 +260,26 @@ type MedianSupportProps = MedianSupportOptions & ({
|
|
|
256
260
|
declare function MedianSupport(props: MedianSupportProps): react.JSX.Element;
|
|
257
261
|
|
|
258
262
|
/**
|
|
259
|
-
* What a mounted
|
|
260
|
-
* no re-render to do for them. Everything else goes through
|
|
261
|
-
* same as anywhere.
|
|
263
|
+
* What a mounted component hands back: the two things a host without React
|
|
264
|
+
* has no re-render to do for them. Everything else goes through the
|
|
265
|
+
* component's own control, same as anywhere.
|
|
262
266
|
*/
|
|
263
|
-
type
|
|
267
|
+
type MedianMountInstance<Options> = {
|
|
264
268
|
/**
|
|
265
269
|
* Change any option after mount. Only the keys passed change, so
|
|
266
270
|
* `update({ user })` when somebody signs in leaves the rest alone.
|
|
267
271
|
*/
|
|
268
|
-
update: (options: Partial<
|
|
269
|
-
/** Take
|
|
272
|
+
update: (options: Partial<Options>) => void;
|
|
273
|
+
/** Take it off the page: the component, its container, its connection. */
|
|
270
274
|
unmount: () => void;
|
|
271
275
|
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* What a mounted widget hands back: the two things a host without React has
|
|
279
|
+
* no re-render to do for them. Everything else goes through `medianSupport`,
|
|
280
|
+
* same as anywhere.
|
|
281
|
+
*/
|
|
282
|
+
type MedianSupportInstance = MedianMountInstance<MedianSupportProps>;
|
|
272
283
|
/**
|
|
273
284
|
* The same widget, for a page that has never heard of React: Vue, Svelte,
|
|
274
285
|
* Rails, a script on plain HTML. One call puts the launcher in the corner,
|
|
@@ -289,6 +300,43 @@ type MedianSupportInstance = {
|
|
|
289
300
|
*/
|
|
290
301
|
declare function mountMedianSupport(options: MedianSupportProps): MedianSupportInstance;
|
|
291
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Every prop the widget takes, unchanged. The modal is the widget in a
|
|
305
|
+
* different place, not a different widget.
|
|
306
|
+
*/
|
|
307
|
+
type MedianSupportModalProps = MedianSupportProps;
|
|
308
|
+
/**
|
|
309
|
+
* The support widget as a modal: the same button in the corner, and a panel
|
|
310
|
+
* that opens in the middle of the screen over a scrim rather than out of
|
|
311
|
+
* the corner.
|
|
312
|
+
*
|
|
313
|
+
* <MedianSupportModal apiKey="median_pk_..." />
|
|
314
|
+
*
|
|
315
|
+
* Everything else is the widget's. Same props, same thread, same inbox on
|
|
316
|
+
* the other side, same `useMedianSupport()` to open it from your own nav,
|
|
317
|
+
* same sheet on a phone. Pick this for a site where a conversation deserves
|
|
318
|
+
* the room, or where a panel floating over the page would be in the way of
|
|
319
|
+
* the page.
|
|
320
|
+
*
|
|
321
|
+
* One or the other. Mounting both shares one open state, and the console
|
|
322
|
+
* says so.
|
|
323
|
+
*/
|
|
324
|
+
declare function MedianSupportModal(props: MedianSupportModalProps): react.JSX.Element;
|
|
325
|
+
|
|
326
|
+
type MedianSupportModalInstance = MedianMountInstance<MedianSupportModalProps>;
|
|
327
|
+
/**
|
|
328
|
+
* The modal, for a page that has never heard of React: Vue, Svelte, Rails,
|
|
329
|
+
* a script on plain HTML.
|
|
330
|
+
*
|
|
331
|
+
* const support = mountMedianSupportModal({ apiKey: "median_pk_..." });
|
|
332
|
+
*
|
|
333
|
+
* It takes the component's props as one options object, and the rest of the
|
|
334
|
+
* SDK does not know the difference: `medianSupport.open()` opens this modal
|
|
335
|
+
* exactly as it opens the component. `update` and `unmount` are the same two
|
|
336
|
+
* `mountMedianSupport` hands back.
|
|
337
|
+
*/
|
|
338
|
+
declare function mountMedianSupportModal(options: MedianSupportModalProps): MedianSupportModalInstance;
|
|
339
|
+
|
|
292
340
|
/**
|
|
293
341
|
* Filing the crash the visitor is looking at.
|
|
294
342
|
*
|
|
@@ -533,15 +581,7 @@ declare function MedianFeedback(props: MedianFeedbackProps): react.JSX.Element;
|
|
|
533
581
|
* no re-render to do for them. Opening and closing go through
|
|
534
582
|
* `medianFeedback`, same as anywhere.
|
|
535
583
|
*/
|
|
536
|
-
type MedianFeedbackInstance =
|
|
537
|
-
/**
|
|
538
|
-
* Change any option after mount. Only the keys passed change, so
|
|
539
|
-
* `update({ user })` when somebody signs in leaves the rest alone.
|
|
540
|
-
*/
|
|
541
|
-
update: (options: Partial<MedianFeedbackProps>) => void;
|
|
542
|
-
/** Take the panel off the page. */
|
|
543
|
-
unmount: () => void;
|
|
544
|
-
};
|
|
584
|
+
type MedianFeedbackInstance = MedianMountInstance<MedianFeedbackProps>;
|
|
545
585
|
/**
|
|
546
586
|
* The same panel, for a page that has never heard of React: Vue, Svelte,
|
|
547
587
|
* Rails, a script on plain HTML.
|
|
@@ -583,4 +623,299 @@ type MedianFeedbackControl = {
|
|
|
583
623
|
*/
|
|
584
624
|
declare function useMedianFeedback(): MedianFeedbackControl;
|
|
585
625
|
|
|
586
|
-
|
|
626
|
+
/**
|
|
627
|
+
* What a host can put on the contact form, and what the form always has.
|
|
628
|
+
*
|
|
629
|
+
* Two fields are on every form whatever the host says: an email, because a
|
|
630
|
+
* message nobody can reply to is a message nobody sent, and the message
|
|
631
|
+
* itself. Everything else is the host's to add: a name, a topic, an order
|
|
632
|
+
* number, whatever their team wants to know before they read.
|
|
633
|
+
*/
|
|
634
|
+
/** A line, a paragraph, or one of a list. */
|
|
635
|
+
type MedianContactFieldKind = "text" | "textarea" | "select";
|
|
636
|
+
/**
|
|
637
|
+
* One field a host adds to the form.
|
|
638
|
+
*
|
|
639
|
+
* { name: "topic", label: "Topic", type: "select", options: ["Billing", "A bug"] }
|
|
640
|
+
*
|
|
641
|
+
* Three names are reserved. `email` and `message` are the two the form always
|
|
642
|
+
* has, and naming either here only changes its label and placeholder. `name`
|
|
643
|
+
* lands on the customer record beside the email rather than in the message,
|
|
644
|
+
* so the team sees who wrote rather than a line saying so.
|
|
645
|
+
*/
|
|
646
|
+
type MedianContactField = {
|
|
647
|
+
/** Its key. Rides the message as the label on its line. */
|
|
648
|
+
name: string;
|
|
649
|
+
/** Shown above the box. Made from `name` when left out. */
|
|
650
|
+
label?: string;
|
|
651
|
+
/** `text` by default. Ignored on `email` and `message`, which know their shape. */
|
|
652
|
+
type?: MedianContactFieldKind;
|
|
653
|
+
/** What a `select` offers. A select with none falls back to a text box. */
|
|
654
|
+
options?: string[];
|
|
655
|
+
placeholder?: string;
|
|
656
|
+
/** Off by default. `email` and `message` are required whatever this says. */
|
|
657
|
+
required?: boolean;
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
type MedianContactFormOptions = {
|
|
661
|
+
/**
|
|
662
|
+
* What the form asks, beyond an email and a message, which it always asks:
|
|
663
|
+
*
|
|
664
|
+
* <MedianContactForm
|
|
665
|
+
* apiKey="median_pk_..."
|
|
666
|
+
* fields={[
|
|
667
|
+
* { name: "name", label: "Name", required: true },
|
|
668
|
+
* { name: "topic", type: "select", options: ["Billing", "A bug", "Something else"] },
|
|
669
|
+
* ]}
|
|
670
|
+
* />
|
|
671
|
+
*
|
|
672
|
+
* Shown in this order. Email goes first, or right after a `name` field,
|
|
673
|
+
* and the message goes last, unless either is placed here by name. A
|
|
674
|
+
* `name` field lands on the customer record; every other answer goes
|
|
675
|
+
* above the message as a line of its own.
|
|
676
|
+
*/
|
|
677
|
+
fields?: MedianContactField[];
|
|
678
|
+
/**
|
|
679
|
+
* Who is signed in on your site, if anyone. Their email and name fill the
|
|
680
|
+
* boxes in, and the rest is shown to your team.
|
|
681
|
+
*/
|
|
682
|
+
user?: MedianUser;
|
|
683
|
+
/**
|
|
684
|
+
* Whether the message carries what the browser already knows: time zone,
|
|
685
|
+
* language, browser, OS, device, screen, and the page it was sent from.
|
|
686
|
+
* On by default. Pass `false` and none of it is read at all.
|
|
687
|
+
*/
|
|
688
|
+
telemetry?: boolean;
|
|
689
|
+
/**
|
|
690
|
+
* Watch what goes wrong on the page, so a bug report filed from the
|
|
691
|
+
* message arrives with the error already attached. The same switch
|
|
692
|
+
* `<MedianSupport>` takes, and the same buffer behind it.
|
|
693
|
+
*/
|
|
694
|
+
diagnostics?: MedianDiagnostics;
|
|
695
|
+
/**
|
|
696
|
+
* Anything about your own app worth having next to a bug report: a build,
|
|
697
|
+
* a route, a flag. Read when the form is sent.
|
|
698
|
+
*/
|
|
699
|
+
appState?: Record<string, string | number | boolean>;
|
|
700
|
+
/** The heading, for a page that calls this something else. */
|
|
701
|
+
title?: string;
|
|
702
|
+
/** The line under it. */
|
|
703
|
+
description?: string;
|
|
704
|
+
/**
|
|
705
|
+
* The message landed. For whatever your page does next: a thank-you of its
|
|
706
|
+
* own, a redirect, an event. The form has already said thank you itself.
|
|
707
|
+
*/
|
|
708
|
+
onSent?: () => void;
|
|
709
|
+
};
|
|
710
|
+
/**
|
|
711
|
+
* Two ways in, the same two `<MedianSupport>` takes.
|
|
712
|
+
*
|
|
713
|
+
* `apiKey` is the publishable id on its own. `identity` is the path to a
|
|
714
|
+
* route built with `medianIdentity`, which answers with the key and with
|
|
715
|
+
* whoever is signed in, already signed. Pass both and the form is up on the
|
|
716
|
+
* key from the prop while the route is still answering.
|
|
717
|
+
*/
|
|
718
|
+
type MedianContactFormProps = MedianContactFormOptions & ({
|
|
719
|
+
apiKey: string;
|
|
720
|
+
identity?: string;
|
|
721
|
+
} | {
|
|
722
|
+
apiKey?: string;
|
|
723
|
+
identity: string;
|
|
724
|
+
});
|
|
725
|
+
/**
|
|
726
|
+
* A contact form, for the page where a chat panel is the wrong shape.
|
|
727
|
+
*
|
|
728
|
+
* <MedianContactForm apiKey="median_pk_..." />
|
|
729
|
+
*
|
|
730
|
+
* An email and a message, on a card the shape of the support panel, with
|
|
731
|
+
* whatever else your team wants asked first. What is sent starts a
|
|
732
|
+
* conversation in your inbox exactly as the widget would: the agent answers
|
|
733
|
+
* first, your team picks it up, and the reply goes to the email given.
|
|
734
|
+
*
|
|
735
|
+
* Built on `useMedianContact`, which is also what a form of your own uses.
|
|
736
|
+
* It needs no connection of its own, and it is independent of
|
|
737
|
+
* `<MedianSupport>`: mount either, both, or neither. Mounted together they
|
|
738
|
+
* share a visitor, so somebody who writes here and later opens the widget
|
|
739
|
+
* finds the same thread.
|
|
740
|
+
*/
|
|
741
|
+
declare function MedianContactForm(props: MedianContactFormProps): react.JSX.Element;
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* The component's props, plus where on the page the form goes.
|
|
745
|
+
*
|
|
746
|
+
* Spelled out rather than `Omit`ped off the props, because omitting a key
|
|
747
|
+
* from a union of two shapes flattens it into one and loses the "either,
|
|
748
|
+
* but not neither" the union is there to say.
|
|
749
|
+
*/
|
|
750
|
+
type MedianContactFormMountOptions = MedianContactFormProps & {
|
|
751
|
+
/**
|
|
752
|
+
* The element the form is rendered into, or a selector for it:
|
|
753
|
+
*
|
|
754
|
+
* mountMedianContactForm({ target: "#contact", apiKey: "median_pk_..." });
|
|
755
|
+
*
|
|
756
|
+
* Unlike the widget, a form has no corner to go to, so this is required.
|
|
757
|
+
*/
|
|
758
|
+
target: string | Element;
|
|
759
|
+
};
|
|
760
|
+
type MedianContactFormInstance = MedianMountInstance<MedianContactFormProps>;
|
|
761
|
+
/**
|
|
762
|
+
* The same form, for a page that has never heard of React: Vue, Svelte,
|
|
763
|
+
* Rails, a script on plain HTML.
|
|
764
|
+
*
|
|
765
|
+
* const form = mountMedianContactForm({
|
|
766
|
+
* target: "#contact",
|
|
767
|
+
* apiKey: "median_pk_...",
|
|
768
|
+
* fields: [{ name: "name", label: "Name" }],
|
|
769
|
+
* });
|
|
770
|
+
*
|
|
771
|
+
* It takes the component's props as one options object, plus `target`, and
|
|
772
|
+
* renders into a container of its own inside that element with the
|
|
773
|
+
* package's copy of React. Whatever else the element holds is left alone.
|
|
774
|
+
*/
|
|
775
|
+
declare function mountMedianContactForm({ target, ...options }: MedianContactFormMountOptions): MedianContactFormInstance;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* What a contact form hands over, ours or a host's own.
|
|
779
|
+
*
|
|
780
|
+
* The shape is deliberately small: the two things a message needs to be
|
|
781
|
+
* answered, who wrote it if they said, and whatever else the form asked as
|
|
782
|
+
* label to answer. Building one from a `<form>` of your own is a few lines.
|
|
783
|
+
*/
|
|
784
|
+
type MedianContactMessage = {
|
|
785
|
+
/** Where the reply goes. */
|
|
786
|
+
email: string;
|
|
787
|
+
/** What they wrote. */
|
|
788
|
+
message: string;
|
|
789
|
+
/** Lands on the customer record beside the email. */
|
|
790
|
+
name?: string;
|
|
791
|
+
/**
|
|
792
|
+
* Everything else the form asked, as label to answer. Each goes above the
|
|
793
|
+
* message as a line of its own, so the team reads the form the way it was
|
|
794
|
+
* filled in:
|
|
795
|
+
*
|
|
796
|
+
* { Topic: "Billing", "Order number": "4821" }
|
|
797
|
+
*
|
|
798
|
+
* Empty answers are left off.
|
|
799
|
+
*/
|
|
800
|
+
fields?: Record<string, string>;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Sending a contact message, for a form of the host's own.
|
|
805
|
+
*
|
|
806
|
+
* The same door the widget's composer uses, on the same session token, so
|
|
807
|
+
* what a host's form sends starts the thread the corner would have started:
|
|
808
|
+
* the team answers it from the inbox, the agent goes first, and with no
|
|
809
|
+
* panel open to watch, the reply goes to the email given. Somebody who
|
|
810
|
+
* writes here and later opens the widget finds their own conversation.
|
|
811
|
+
*
|
|
812
|
+
* A stateless HTTP client rather than the subscribing one, because nothing
|
|
813
|
+
* is read back: a form is a thing you hand over, not a thread you watch.
|
|
814
|
+
*/
|
|
815
|
+
/** What a send is told about who is sending and from where. */
|
|
816
|
+
type MedianContactOptions = {
|
|
817
|
+
/** The browser-safe median_pk_ id. */
|
|
818
|
+
apiKey: string;
|
|
819
|
+
/**
|
|
820
|
+
* Who is signed in on your site, if anyone. What the form says about the
|
|
821
|
+
* email and the name wins over this; the rest is shown to your team.
|
|
822
|
+
*/
|
|
823
|
+
user?: MedianUser;
|
|
824
|
+
/**
|
|
825
|
+
* Whether the message carries what the browser already knows: time zone,
|
|
826
|
+
* language, browser, OS, device, screen, and the page it was sent from.
|
|
827
|
+
* On by default. Pass `false` and none of it is read at all.
|
|
828
|
+
*/
|
|
829
|
+
telemetry?: boolean;
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* Send a contact message from a form of your own, on any page in the
|
|
833
|
+
* browser:
|
|
834
|
+
*
|
|
835
|
+
* await sendMedianContact(
|
|
836
|
+
* { apiKey: "median_pk_..." },
|
|
837
|
+
* { email, message, name, fields: { Topic: topic } },
|
|
838
|
+
* );
|
|
839
|
+
*
|
|
840
|
+
* It resolves once the message is in your inbox and rejects with an `Error`
|
|
841
|
+
* whose `message` is worth showing above your button. Nothing about the
|
|
842
|
+
* form is ours: any `<form>`, any framework, any styling.
|
|
843
|
+
*
|
|
844
|
+
* In React, `useMedianContact()` is the same call with `isSending` and
|
|
845
|
+
* `error` kept for you, and an identity route resolved.
|
|
846
|
+
*/
|
|
847
|
+
declare function sendMedianContact(options: MedianContactOptions, message: MedianContactMessage): Promise<void>;
|
|
848
|
+
|
|
849
|
+
type MedianContactHookOptions = {
|
|
850
|
+
/** Who is signed in on your site, if anyone. */
|
|
851
|
+
user?: MedianUser;
|
|
852
|
+
/** Browser context on the message. On by default. */
|
|
853
|
+
telemetry?: boolean;
|
|
854
|
+
/**
|
|
855
|
+
* Watch what goes wrong on the page, so a bug report filed from the
|
|
856
|
+
* message arrives with the error already attached. The same switch
|
|
857
|
+
* `<MedianSupport>` takes, and the same buffer behind it.
|
|
858
|
+
*/
|
|
859
|
+
diagnostics?: MedianDiagnostics;
|
|
860
|
+
/** Anything about your own app worth having next to a bug report. */
|
|
861
|
+
appState?: Record<string, string | number | boolean>;
|
|
862
|
+
};
|
|
863
|
+
/**
|
|
864
|
+
* Two ways in, the same two every component takes: the publishable key, or
|
|
865
|
+
* the path of a route built with `medianIdentity`, which answers with the
|
|
866
|
+
* key and whoever is signed in.
|
|
867
|
+
*/
|
|
868
|
+
type UseMedianContactOptions = MedianContactHookOptions & ({
|
|
869
|
+
apiKey: string;
|
|
870
|
+
identity?: string;
|
|
871
|
+
} | {
|
|
872
|
+
apiKey?: string;
|
|
873
|
+
identity: string;
|
|
874
|
+
});
|
|
875
|
+
/** What the hook hands back: one call, and the state around it. */
|
|
876
|
+
type MedianContactControl = {
|
|
877
|
+
/**
|
|
878
|
+
* Send what the form holds. Resolves once the message is in the inbox.
|
|
879
|
+
* Rejects with an `Error` whose `message` is worth showing, and `error`
|
|
880
|
+
* holds the same words until the next send.
|
|
881
|
+
*/
|
|
882
|
+
send: (message: MedianContactMessage) => Promise<void>;
|
|
883
|
+
/** A send is in flight. Hold the button. */
|
|
884
|
+
isSending: boolean;
|
|
885
|
+
/** Why the last send failed, or null. Cleared by the next send. */
|
|
886
|
+
error: string | null;
|
|
887
|
+
/**
|
|
888
|
+
* Whether a send has somewhere to go. False on the server, and for the
|
|
889
|
+
* beat before an identity route has answered with the key.
|
|
890
|
+
*/
|
|
891
|
+
isReady: boolean;
|
|
892
|
+
/**
|
|
893
|
+
* Who is typing, as far as the page knows: what you passed, what the
|
|
894
|
+
* identity route said, and the email the widget's own question was
|
|
895
|
+
* answered with on this browser. For filling the boxes in.
|
|
896
|
+
*/
|
|
897
|
+
user: MedianUser | undefined;
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* A contact form of your own, on Median's inbox:
|
|
901
|
+
*
|
|
902
|
+
* const contact = useMedianContact({ apiKey: "median_pk_..." });
|
|
903
|
+
*
|
|
904
|
+
* <form
|
|
905
|
+
* onSubmit={async (event) => {
|
|
906
|
+
* event.preventDefault();
|
|
907
|
+
* await contact.send({ email, message });
|
|
908
|
+
* }}
|
|
909
|
+
* >
|
|
910
|
+
* ...
|
|
911
|
+
* {contact.error && <p>{contact.error}</p>}
|
|
912
|
+
* <button disabled={contact.isSending}>Send</button>
|
|
913
|
+
* </form>
|
|
914
|
+
*
|
|
915
|
+
* Everything about the form is yours. What is sent starts a conversation in
|
|
916
|
+
* your inbox exactly as `<MedianContactForm>` and the widget do, and the
|
|
917
|
+
* reply goes to the email given. No provider, no connection to manage.
|
|
918
|
+
*/
|
|
919
|
+
declare function useMedianContact(options: UseMedianContactOptions): MedianContactControl;
|
|
920
|
+
|
|
921
|
+
export { type MedianContactControl, type MedianContactField, type MedianContactFieldKind, MedianContactForm, type MedianContactFormInstance, type MedianContactFormMountOptions, type MedianContactFormProps, type MedianContactMessage, type MedianContactOptions, type MedianDiagnostics, type MedianErrorReportOptions, type MedianErrorReportOutcome, MedianFeedback, type MedianFeedbackControl, type MedianFeedbackInstance, type MedianFeedbackProps, MedianSupport, type MedianSupportControl, type MedianSupportInstance, MedianSupportModal, type MedianSupportModalInstance, type MedianSupportModalProps, type MedianSupportProps, type MedianUser, type MedianDiagnostics as PortalDiagnostics, type MedianErrorReportOptions as PortalErrorReportOptions, type MedianErrorReportOutcome as PortalErrorReportOutcome, MedianFeedback as PortalFeedback, type MedianFeedbackControl as PortalFeedbackControl, type MedianFeedbackInstance as PortalFeedbackInstance, type MedianFeedbackProps as PortalFeedbackProps, MedianSupport as PortalSupport, type MedianSupportControl as PortalSupportControl, type MedianSupportInstance as PortalSupportInstance, type MedianSupportProps as PortalSupportProps, type MedianUser as PortalUser, type SupportLauncherKind, type UseMedianContactOptions, medianFeedback, medianSupport, mountMedianContactForm, mountMedianFeedback, mountMedianSupport, mountMedianSupportModal, mountMedianFeedback as mountPortalFeedback, mountMedianSupport as mountPortalSupport, medianFeedback as portalFeedback, medianSupport as portalSupport, reportError, sendMedianContact, useCanReportError, useMedianContact, useMedianFeedback, useMedianSupport, useMedianFeedback as usePortalFeedback, useMedianSupport as usePortalSupport };
|