@graineai/inapp-react-native 0.30.0 → 0.31.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/dist/client.d.ts CHANGED
@@ -63,6 +63,9 @@ export declare class GraineInAppClient {
63
63
  configureFriction(override: FrictionConfig | null | undefined): void;
64
64
  get friction(): Required<FrictionConfig>;
65
65
  on(event: string, fn: Listener): () => void;
66
+ private lastWidget;
67
+ get pendingWidget(): any;
68
+ clearPendingWidget(): void;
66
69
  emit(event: string, payload?: any): void;
67
70
  private get fetch();
68
71
  session(): Promise<SessionConfig>;
package/dist/client.js CHANGED
@@ -28,6 +28,7 @@ export class GraineInAppClient {
28
28
  this.identity = {};
29
29
  this.appVersion = null;
30
30
  this.recentEvents = [];
31
+ this.lastWidget = null;
31
32
  if (!opts?.publishableKey)
32
33
  throw new Error("[Graine] publishableKey is required.");
33
34
  if (!opts?.baseUrl)
@@ -57,7 +58,15 @@ export class GraineInAppClient {
57
58
  this.listeners.get(event).add(fn);
58
59
  return () => this.listeners.get(event)?.delete(fn);
59
60
  }
61
+ get pendingWidget() {
62
+ return this.lastWidget;
63
+ }
64
+ clearPendingWidget() {
65
+ this.lastWidget = null;
66
+ }
60
67
  emit(event, payload) {
68
+ if (event === "widget")
69
+ this.lastWidget = payload ?? null;
61
70
  for (const fn of this.listeners.get(event) ?? []) {
62
71
  try {
63
72
  fn(payload);
@@ -389,24 +389,36 @@ export function useGraineHighlight(name) {
389
389
  }
390
390
  export function useGraineWidget() {
391
391
  const client = useGraineClient();
392
- const [widget, setWidget] = useState(null);
393
- useEffect(() => client.on("widget", (w) => {
394
- if (!w?.widget_id)
395
- return;
396
- setWidget({
392
+ const adopt = useCallback((w) => w?.widget_id
393
+ ? {
397
394
  id: String(w.widget_id),
398
395
  kind: String(w.kind || ""),
399
396
  data: resolveWidgetImages((w.data || {}), client.opts.baseUrl),
400
397
  fallbackText: String(w.fallback_text || ""),
398
+ }
399
+ : null, [client]);
400
+ const [widget, setWidget] = useState(() => adopt(client.pendingWidget));
401
+ useEffect(() => {
402
+ const pending = adopt(client.pendingWidget);
403
+ if (pending)
404
+ setWidget((cur) => (cur && cur.id === pending.id ? cur : pending));
405
+ return client.on("widget", (w) => {
406
+ const next = adopt(w);
407
+ if (next)
408
+ setWidget(next);
401
409
  });
402
- }), [client]);
410
+ }, [client, adopt]);
403
411
  const submit = useCallback((data, summary) => {
404
412
  if (!widget)
405
413
  return;
406
414
  client.submitWidget(widget.id, data, summary);
415
+ client.clearPendingWidget();
407
416
  setWidget(null);
408
417
  }, [client, widget]);
409
- const dismiss = useCallback(() => setWidget(null), []);
418
+ const dismiss = useCallback(() => {
419
+ client.clearPendingWidget();
420
+ setWidget(null);
421
+ }, [client]);
410
422
  return { widget, submit, dismiss };
411
423
  }
412
424
  export function useGraineAction(name, handler) {
@@ -138,6 +138,9 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
138
138
  startedRef.current = true;
139
139
  void beginCall();
140
140
  }
141
+ else if (!activeRef.current && !startedRef.current) {
142
+ post({ type: "graine:warm" });
143
+ }
141
144
  return;
142
145
  case "graine:call":
143
146
  setConnected(!!msg.connected);
@@ -200,6 +203,7 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
200
203
  start: useCallback(() => {
201
204
  setConnecting(true);
202
205
  setCaptions([]);
206
+ client.clearPendingWidget();
203
207
  if (!ready) {
204
208
  pendingStartRef.current = true;
205
209
  return;
@@ -243,6 +247,7 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
243
247
  setConnected(false);
244
248
  setConnecting(false);
245
249
  setCaptions([]);
250
+ client.clearPendingWidget();
246
251
  onCallState?.({ connected: false, connecting: false });
247
252
  }, [active]);
248
253
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graineai/inapp-react-native",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "description": "Graine in-app agent for React Native \u2014 an agent that sees the screen your customer is on and can act on it.",
6
6
  "license": "MIT",