@guidelytech/react-native 0.6.2 → 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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/index.cjs +688 -425
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -3
- package/dist/index.d.ts +83 -3
- package/dist/index.js +533 -282
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -3,8 +3,98 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
5
|
// src/index.tsx
|
|
6
|
-
import { useContext as
|
|
7
|
-
import { AppState, Dimensions, Linking, Platform as Platform6 } from "react-native";
|
|
6
|
+
import { useContext as useContext3, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef6, useState as useState8 } from "react";
|
|
7
|
+
import { AppState as AppState2, Dimensions, Linking, Platform as Platform6 } from "react-native";
|
|
8
|
+
import { createStaticFlags as createStaticFlags2, stableStringify as stableStringify2 } from "@guidelytech/flags";
|
|
9
|
+
|
|
10
|
+
// src/flags.tsx
|
|
11
|
+
import { createFlagsClient, createStaticFlags, jsonEquals, stableStringify } from "@guidelytech/flags";
|
|
12
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
13
|
+
import { AppState } from "react-native";
|
|
14
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
15
|
+
var FlagsContext = createContext(null);
|
|
16
|
+
var EMPTY = createStaticFlags();
|
|
17
|
+
function useFlagsValue(select) {
|
|
18
|
+
const src = useContext(FlagsContext);
|
|
19
|
+
const reader = src?.client ?? src?.bootstrap ?? EMPTY;
|
|
20
|
+
const cache = useRef(null);
|
|
21
|
+
const get = () => {
|
|
22
|
+
const value = select(reader);
|
|
23
|
+
const c = cache.current;
|
|
24
|
+
if (c && c.reader === reader && jsonEquals(c.value, value)) return c.value;
|
|
25
|
+
cache.current = { reader, value };
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
return useSyncExternalStore(reader.subscribe, get, get);
|
|
29
|
+
}
|
|
30
|
+
function useFlag(key, fallback = false) {
|
|
31
|
+
return useFlagsValue((r) => r.isEnabled(key, fallback));
|
|
32
|
+
}
|
|
33
|
+
function useFeatureValue(key, fallback) {
|
|
34
|
+
return useFlagsValue((r) => r.getValue(key, fallback));
|
|
35
|
+
}
|
|
36
|
+
function useFlagDetails(key, fallback) {
|
|
37
|
+
return useFlagsValue((r) => r.getDetails(key, fallback));
|
|
38
|
+
}
|
|
39
|
+
function useFlags() {
|
|
40
|
+
return useFlagsValue((r) => r.getAll());
|
|
41
|
+
}
|
|
42
|
+
function useFlagsReady() {
|
|
43
|
+
return useFlagsValue((r) => r.isReady());
|
|
44
|
+
}
|
|
45
|
+
function useFlagsClient() {
|
|
46
|
+
return useContext(FlagsContext)?.client ?? null;
|
|
47
|
+
}
|
|
48
|
+
function Feature({ flag, children, fallback = null }) {
|
|
49
|
+
return /* @__PURE__ */ jsx(Fragment, { children: useFlag(flag) ? children : fallback });
|
|
50
|
+
}
|
|
51
|
+
var FeatureFlag = Feature;
|
|
52
|
+
function FeatureValue({ flag, value, children, fallback = null }) {
|
|
53
|
+
const current = useFeatureValue(flag, null);
|
|
54
|
+
return /* @__PURE__ */ jsx(Fragment, { children: jsonEquals(current, value) ? children : fallback });
|
|
55
|
+
}
|
|
56
|
+
function useBootstrap(payload, attributes) {
|
|
57
|
+
const key = stableStringify(attributes);
|
|
58
|
+
return useMemo(() => payload ? createStaticFlags({ payload, attributes }) : EMPTY, [payload, key]);
|
|
59
|
+
}
|
|
60
|
+
function FlagsProvider({ children, client: external, payload, ...options }) {
|
|
61
|
+
const [created, setCreated] = useState(null);
|
|
62
|
+
const optionsRef = useRef(options);
|
|
63
|
+
optionsRef.current = options;
|
|
64
|
+
const payloadRef = useRef(payload);
|
|
65
|
+
payloadRef.current = payload;
|
|
66
|
+
const sdkKey = options.sdkKey ?? options.projectId;
|
|
67
|
+
const { apiUrl, environment } = options;
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (external || !sdkKey) return;
|
|
70
|
+
let c;
|
|
71
|
+
try {
|
|
72
|
+
c = createFlagsClient({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? void 0 });
|
|
73
|
+
} catch (err) {
|
|
74
|
+
optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setCreated(c);
|
|
78
|
+
const sub = AppState.addEventListener("change", (s) => {
|
|
79
|
+
if (s === "active") void c.refresh();
|
|
80
|
+
else if (s === "background") void c.flush();
|
|
81
|
+
});
|
|
82
|
+
return () => {
|
|
83
|
+
sub.remove();
|
|
84
|
+
c.destroy();
|
|
85
|
+
setCreated((cur) => cur === c ? null : cur);
|
|
86
|
+
};
|
|
87
|
+
}, [external, sdkKey, apiUrl, environment]);
|
|
88
|
+
const client = external ?? created;
|
|
89
|
+
const attributes = options.attributes ?? {};
|
|
90
|
+
const attrsKey = stableStringify(attributes);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (client && options.attributes) client.setAttributes(options.attributes);
|
|
93
|
+
}, [client, attrsKey]);
|
|
94
|
+
const bootstrap = useBootstrap(payload, attributes);
|
|
95
|
+
const value = useMemo(() => ({ client, bootstrap }), [client, bootstrap]);
|
|
96
|
+
return /* @__PURE__ */ jsx(FlagsContext.Provider, { value, children });
|
|
97
|
+
}
|
|
8
98
|
|
|
9
99
|
// ../shared/dist/chunk-OLWGMDRX.js
|
|
10
100
|
function fnv1a(input) {
|
|
@@ -834,6 +924,9 @@ function isWithinSchedule(guide, now = Date.now()) {
|
|
|
834
924
|
return true;
|
|
835
925
|
}
|
|
836
926
|
|
|
927
|
+
// src/core/client.ts
|
|
928
|
+
import { createFlagsClient as createFlagsClient2 } from "@guidelytech/flags";
|
|
929
|
+
|
|
837
930
|
// src/core/targets.ts
|
|
838
931
|
var TargetTimeoutError = class extends Error {
|
|
839
932
|
constructor(ids) {
|
|
@@ -1729,7 +1822,7 @@ var PersistentStore = class {
|
|
|
1729
1822
|
|
|
1730
1823
|
// src/core/client.ts
|
|
1731
1824
|
var SDK_NAME = "@guidelytech/react-native";
|
|
1732
|
-
var SDK_VERSION = "0.
|
|
1825
|
+
var SDK_VERSION = "0.7.0";
|
|
1733
1826
|
var DEFAULT_API_URL = "https://guidelytech.com";
|
|
1734
1827
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
1735
1828
|
var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1776,6 +1869,10 @@ var GuidelyClient = class {
|
|
|
1776
1869
|
__publicField(this, "messenger", null);
|
|
1777
1870
|
__publicField(this, "ui", { steps: [], checklists: [], survey: null, resourceCenterOpen: false, theme: {}, messenger: false });
|
|
1778
1871
|
__publicField(this, "uiListeners", /* @__PURE__ */ new Set());
|
|
1872
|
+
/** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
|
|
1873
|
+
__publicField(this, "flags");
|
|
1874
|
+
/** Experiments already reported this session (`experiment_viewed` is sent once per session). */
|
|
1875
|
+
__publicField(this, "exposures", /* @__PURE__ */ new Set());
|
|
1779
1876
|
__publicField(this, "customListeners", /* @__PURE__ */ new Map());
|
|
1780
1877
|
__publicField(this, "eventBus", {
|
|
1781
1878
|
on: (name, cb) => {
|
|
@@ -1804,6 +1901,40 @@ var GuidelyClient = class {
|
|
|
1804
1901
|
this.store = new PersistentStore(options.storage ?? null, (err) => this.log("storage error", err));
|
|
1805
1902
|
if (!options.storage) this.log("no `storage` passed: identity and guide history last only for this app session. Pass AsyncStorage to persist them.");
|
|
1806
1903
|
this.cleanups.push(this.targets.onTap((id) => this.handleTap(id)));
|
|
1904
|
+
if (options.user) this.user = { ...options.user };
|
|
1905
|
+
this.flags = this.createFlags(options.flags);
|
|
1906
|
+
}
|
|
1907
|
+
createFlags(opts) {
|
|
1908
|
+
const base = { sdkKey: this.options.projectId, apiUrl: this.options.apiUrl, fetch: this.options.fetch, sdk: { name: SDK_NAME, version: SDK_VERSION } };
|
|
1909
|
+
if (opts === false) return createFlagsClient2({ ...base, overrideBehaviour: "localOnly", offline: true, reportUsage: false, trackExposures: false, storage: false, logger: null });
|
|
1910
|
+
const o = opts ?? {};
|
|
1911
|
+
return createFlagsClient2({
|
|
1912
|
+
...base,
|
|
1913
|
+
storage: this.options.storage ?? false,
|
|
1914
|
+
reportUsage: this.options.analytics,
|
|
1915
|
+
// Failures surface through onError (and `debug` logs); development warnings (unknown flag keys) reach the console.
|
|
1916
|
+
logger: { warn: (...a) => console.warn("[Guidely]", ...a), error: (...a) => this.log(...a) },
|
|
1917
|
+
...o,
|
|
1918
|
+
contextAttributes: this.flagContext(),
|
|
1919
|
+
// Exposures travel through the event pipeline (with the session) as `experiment_viewed`.
|
|
1920
|
+
trackExposures: false,
|
|
1921
|
+
onFlagEvaluated: (result) => {
|
|
1922
|
+
this.trackExposure(result);
|
|
1923
|
+
o.onFlagEvaluated?.(result);
|
|
1924
|
+
},
|
|
1925
|
+
onReady: () => {
|
|
1926
|
+
this.emitter.emit("flags:ready");
|
|
1927
|
+
o.onReady?.();
|
|
1928
|
+
},
|
|
1929
|
+
onChange: (data) => {
|
|
1930
|
+
this.emitter.emit("flags:change");
|
|
1931
|
+
o.onChange?.(data);
|
|
1932
|
+
},
|
|
1933
|
+
onError: (err) => {
|
|
1934
|
+
this.reportError(err);
|
|
1935
|
+
o.onError?.(err);
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1807
1938
|
}
|
|
1808
1939
|
// ---------------------------------------------------------------------------
|
|
1809
1940
|
// Lifecycle
|
|
@@ -1834,6 +1965,7 @@ var GuidelyClient = class {
|
|
|
1834
1965
|
this.session = saved?.id && !this.sessionExpired(Date.parse(saved.lastActiveAt)) ? saved : newSession();
|
|
1835
1966
|
this.persistSession();
|
|
1836
1967
|
this.lastAudiences = new Set(this.state.audienceIds);
|
|
1968
|
+
this.syncFlagAttributes();
|
|
1837
1969
|
this.queue = new EventQueue({
|
|
1838
1970
|
store: this.store,
|
|
1839
1971
|
storageKey: KEYS.queue(key),
|
|
@@ -1923,6 +2055,7 @@ var GuidelyClient = class {
|
|
|
1923
2055
|
this.persistSession();
|
|
1924
2056
|
void this.queue?.flush();
|
|
1925
2057
|
void this.store.flush();
|
|
2058
|
+
void this.flags.flush();
|
|
1926
2059
|
this.messenger?.setForeground(false);
|
|
1927
2060
|
} else if (state === "active" && this.backgroundedAt !== null) {
|
|
1928
2061
|
this.messenger?.setForeground(true);
|
|
@@ -1933,6 +2066,7 @@ var GuidelyClient = class {
|
|
|
1933
2066
|
this.persistSession();
|
|
1934
2067
|
this.firedDelays.clear();
|
|
1935
2068
|
}
|
|
2069
|
+
void this.flags.refresh();
|
|
1936
2070
|
if (Date.now() - this.configFetchedAt > this.options.configRefreshMs) void this.refresh();
|
|
1937
2071
|
else if (this.isReady) this.evaluate("ready");
|
|
1938
2072
|
}
|
|
@@ -1959,6 +2093,7 @@ var GuidelyClient = class {
|
|
|
1959
2093
|
this.cleanups.splice(0).forEach((fn) => fn());
|
|
1960
2094
|
void this.queue?.flush();
|
|
1961
2095
|
this.queue?.destroy();
|
|
2096
|
+
this.flags.destroy();
|
|
1962
2097
|
this.emitter.clear();
|
|
1963
2098
|
this.uiListeners.clear();
|
|
1964
2099
|
}
|
|
@@ -1973,6 +2108,7 @@ var GuidelyClient = class {
|
|
|
1973
2108
|
const same = this.user?.id === id;
|
|
1974
2109
|
this.user = { id, traits: { ...same ? this.user?.traits : {}, ...traits || {} }, account: account ?? (same ? this.user?.account : void 0) };
|
|
1975
2110
|
writeJson(this.store, KEYS.user(this.options.projectId), this.user);
|
|
2111
|
+
this.syncFlagAttributes();
|
|
1976
2112
|
this.enqueue("identify", { properties: { traits: sanitizeProperties(this.user.traits) } });
|
|
1977
2113
|
void this.syncIdentify().then(() => {
|
|
1978
2114
|
this.emitter.emit("identify", { userId: id, anonymousId: this.anonymousId });
|
|
@@ -2016,6 +2152,7 @@ var GuidelyClient = class {
|
|
|
2016
2152
|
};
|
|
2017
2153
|
if (data.firstSeenAt && Date.now() - Date.parse(data.firstSeenAt) > 6e4) this.firstVisit = false;
|
|
2018
2154
|
this.persistState();
|
|
2155
|
+
this.syncFlagAttributes();
|
|
2019
2156
|
this.notify();
|
|
2020
2157
|
}
|
|
2021
2158
|
/** Sets or replaces the account (company / organisation) for the current user. */
|
|
@@ -2024,6 +2161,7 @@ var GuidelyClient = class {
|
|
|
2024
2161
|
if (!this.user?.id) {
|
|
2025
2162
|
this.user = { id: "", traits: this.user?.traits, account };
|
|
2026
2163
|
writeJson(this.store, KEYS.user(this.options.projectId), this.user);
|
|
2164
|
+
this.syncFlagAttributes();
|
|
2027
2165
|
return;
|
|
2028
2166
|
}
|
|
2029
2167
|
await this.identify(this.user.id, void 0, account);
|
|
@@ -2033,6 +2171,7 @@ var GuidelyClient = class {
|
|
|
2033
2171
|
await this.init();
|
|
2034
2172
|
if (!this.user?.id) {
|
|
2035
2173
|
this.user = { id: "", traits: { ...this.user?.traits || {}, ...traits }, account: this.user?.account };
|
|
2174
|
+
this.syncFlagAttributes();
|
|
2036
2175
|
return;
|
|
2037
2176
|
}
|
|
2038
2177
|
await this.identify(this.user.id, traits, this.user.account);
|
|
@@ -2060,6 +2199,8 @@ var GuidelyClient = class {
|
|
|
2060
2199
|
this.lastAudiences = /* @__PURE__ */ new Set();
|
|
2061
2200
|
this.firedDelays.clear();
|
|
2062
2201
|
this.firstVisit = true;
|
|
2202
|
+
this.exposures.clear();
|
|
2203
|
+
this.syncFlagAttributes();
|
|
2063
2204
|
this.emitter.emit("identify", { userId: null, anonymousId: this.anonymousId });
|
|
2064
2205
|
this.notify();
|
|
2065
2206
|
if (this.isReady) this.evaluate("identify");
|
|
@@ -2133,6 +2274,7 @@ var GuidelyClient = class {
|
|
|
2133
2274
|
const changed = name !== this.screenName;
|
|
2134
2275
|
this.screenName = name;
|
|
2135
2276
|
this.targets.setScreen(name);
|
|
2277
|
+
if (changed) this.syncFlagAttributes();
|
|
2136
2278
|
const url = this.currentUrl();
|
|
2137
2279
|
if (changed) this.enqueue("page_view", { properties: { ...properties, path: url, screen: name } });
|
|
2138
2280
|
this.completeChecklistTasksBy("url_visited", url);
|
|
@@ -2803,6 +2945,58 @@ var GuidelyClient = class {
|
|
|
2803
2945
|
showNewMessage(text) {
|
|
2804
2946
|
this.whenHydrated(() => this.messenger?.showNewMessage(text));
|
|
2805
2947
|
}
|
|
2948
|
+
// ---------------------------------------------------------------------------
|
|
2949
|
+
// Feature flags
|
|
2950
|
+
// ---------------------------------------------------------------------------
|
|
2951
|
+
/** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
|
|
2952
|
+
isFeatureEnabled(key, fallback = false) {
|
|
2953
|
+
return this.flags.isEnabled(key, fallback);
|
|
2954
|
+
}
|
|
2955
|
+
/** Flag value for the current user; `fallback` when missing or of a different type. */
|
|
2956
|
+
getFeatureValue(key, fallback) {
|
|
2957
|
+
return this.flags.getValue(key, fallback);
|
|
2958
|
+
}
|
|
2959
|
+
/** Flag value plus why it was served (reason, rule, variation, experiment). */
|
|
2960
|
+
getFeatureDetails(key, fallback) {
|
|
2961
|
+
return this.flags.getDetails(key, fallback);
|
|
2962
|
+
}
|
|
2963
|
+
/** Calls `cb` whenever flag values may have changed. Returns an unsubscribe function. */
|
|
2964
|
+
onFlagsChange(cb) {
|
|
2965
|
+
return this.flags.subscribe(cb);
|
|
2966
|
+
}
|
|
2967
|
+
/** Attributes flags are evaluated with: identity, traits, account, audiences, screen and device. */
|
|
2968
|
+
flagContext() {
|
|
2969
|
+
const d = this.bridge.device;
|
|
2970
|
+
const account = this.user?.account;
|
|
2971
|
+
return {
|
|
2972
|
+
platform: d.platform,
|
|
2973
|
+
deviceType: d.deviceType,
|
|
2974
|
+
locale: d.locale,
|
|
2975
|
+
appVersion: this.options.appVersion ?? d.appVersion,
|
|
2976
|
+
environment: this.options.environment,
|
|
2977
|
+
...this.screenName ? { screen: this.screenName, path: this.currentUrl() } : {},
|
|
2978
|
+
...this.user?.traits ?? {},
|
|
2979
|
+
id: this.user?.id || this.anonymousId || void 0,
|
|
2980
|
+
anonymousId: this.anonymousId || void 0,
|
|
2981
|
+
account: account ? { ...account.traits ?? {}, id: account.id } : void 0,
|
|
2982
|
+
$audiences: [...this.state.audienceIds]
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
syncFlagAttributes() {
|
|
2986
|
+
if (!this.destroyed) this.flags?.setContextAttributes(this.flagContext());
|
|
2987
|
+
}
|
|
2988
|
+
/** `experiment_viewed`, once per session and assignment, through the event queue (consent applies). */
|
|
2989
|
+
trackExposure(result) {
|
|
2990
|
+
const exp = result.inExperiment ? result.experiment : void 0;
|
|
2991
|
+
if (!exp || this.destroyed) return;
|
|
2992
|
+
this.whenHydrated(() => {
|
|
2993
|
+
const key = `${this.session.id}|${exp.key}|${exp.hashValue}|${exp.variationId}`;
|
|
2994
|
+
if (this.exposures.has(key)) return;
|
|
2995
|
+
if (this.exposures.size > 500) this.exposures.clear();
|
|
2996
|
+
this.exposures.add(key);
|
|
2997
|
+
this.enqueue("experiment_viewed", { properties: { experimentKey: exp.key, variationId: exp.variationId, flagKey: result.key, hashAttribute: exp.hashAttribute, ...exp.variationName ? { variationName: exp.variationName } : {} } });
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
2806
3000
|
on(event, cb) {
|
|
2807
3001
|
return this.emitter.on(event, cb);
|
|
2808
3002
|
}
|
|
@@ -2831,7 +3025,8 @@ var GuidelyClient = class {
|
|
|
2831
3025
|
audienceIds: this.state.audienceIds,
|
|
2832
3026
|
session: this.session,
|
|
2833
3027
|
pendingEvents: this.queue?.size ?? 0,
|
|
2834
|
-
targets: this.targets.ids()
|
|
3028
|
+
targets: this.targets.ids(),
|
|
3029
|
+
flagsReady: this.flags.isReady()
|
|
2835
3030
|
};
|
|
2836
3031
|
}
|
|
2837
3032
|
// ---------------------------------------------------------------------------
|
|
@@ -2862,7 +3057,7 @@ function forPlatform(config, platform) {
|
|
|
2862
3057
|
}
|
|
2863
3058
|
|
|
2864
3059
|
// src/ui/Overlay.tsx
|
|
2865
|
-
import { useCallback as useCallback2, useEffect as
|
|
3060
|
+
import { useCallback as useCallback2, useEffect as useEffect7, useRef as useRef4, useState as useState7, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
2866
3061
|
import { Platform as Platform5, Pressable as Pressable8, StatusBar, StyleSheet as StyleSheet9, Text as Text8, useColorScheme, useWindowDimensions, View as View9 } from "react-native";
|
|
2867
3062
|
|
|
2868
3063
|
// src/ui/Checklist.tsx
|
|
@@ -2870,11 +3065,11 @@ import { Pressable as Pressable3, ScrollView as ScrollView2, StyleSheet as Style
|
|
|
2870
3065
|
|
|
2871
3066
|
// src/ui/primitives.tsx
|
|
2872
3067
|
import { Pressable, StyleSheet, Text, View } from "react-native";
|
|
2873
|
-
import { jsx } from "react/jsx-runtime";
|
|
3068
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
2874
3069
|
function GButton({ label, variant = "primary", onPress, palette: p, style, accessibilityHint, disabled }) {
|
|
2875
3070
|
const filled = variant === "primary";
|
|
2876
3071
|
const outlined = variant === "secondary";
|
|
2877
|
-
return /* @__PURE__ */
|
|
3072
|
+
return /* @__PURE__ */ jsx2(
|
|
2878
3073
|
Pressable,
|
|
2879
3074
|
{
|
|
2880
3075
|
accessibilityRole: "button",
|
|
@@ -2892,15 +3087,15 @@ function GButton({ label, variant = "primary", onPress, palette: p, style, acces
|
|
|
2892
3087
|
variant === "link" && styles.link,
|
|
2893
3088
|
style
|
|
2894
3089
|
],
|
|
2895
|
-
children: /* @__PURE__ */
|
|
3090
|
+
children: /* @__PURE__ */ jsx2(Text, { style: [styles.btnText, { color: filled ? p.onPrimary : variant === "link" ? p.primary : variant === "ghost" ? p.muted : p.text, fontFamily: p.fontFamily }, variant === "link" && styles.underline], numberOfLines: 1, children: label })
|
|
2896
3091
|
}
|
|
2897
3092
|
);
|
|
2898
3093
|
}
|
|
2899
3094
|
function CloseButton({ onPress, palette: p, label = "Close" }) {
|
|
2900
|
-
return /* @__PURE__ */
|
|
3095
|
+
return /* @__PURE__ */ jsx2(Pressable, { accessibilityRole: "button", accessibilityLabel: label, onPress, hitSlop: 10, style: ({ pressed }) => [styles.close, { opacity: pressed ? 0.6 : 1 }], children: /* @__PURE__ */ jsx2(Text, { style: [styles.closeText, { color: p.muted }], children: "\xD7" }) });
|
|
2901
3096
|
}
|
|
2902
3097
|
function Surface({ palette: p, children, style }) {
|
|
2903
|
-
return /* @__PURE__ */
|
|
3098
|
+
return /* @__PURE__ */ jsx2(View, { style: [styles.surface, { backgroundColor: p.background, borderRadius: p.radius, borderColor: p.border }, style], children });
|
|
2904
3099
|
}
|
|
2905
3100
|
var styles = StyleSheet.create({
|
|
2906
3101
|
btn: { minHeight: 36, paddingHorizontal: 14, paddingVertical: 8, alignItems: "center", justifyContent: "center" },
|
|
@@ -2920,7 +3115,7 @@ var styles = StyleSheet.create({
|
|
|
2920
3115
|
});
|
|
2921
3116
|
|
|
2922
3117
|
// src/ui/RichText.tsx
|
|
2923
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3118
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
2924
3119
|
import { Image, Modal, Platform, Pressable as Pressable2, ScrollView, StyleSheet as StyleSheet2, Text as Text2, View as View2 } from "react-native";
|
|
2925
3120
|
|
|
2926
3121
|
// src/core/html.ts
|
|
@@ -3147,7 +3342,7 @@ function htmlToText(html) {
|
|
|
3147
3342
|
}
|
|
3148
3343
|
|
|
3149
3344
|
// src/ui/RichText.tsx
|
|
3150
|
-
import { jsx as
|
|
3345
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
3151
3346
|
var MONO = Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
|
|
3152
3347
|
var HEADING_SIZE = [0, 20, 17, 15];
|
|
3153
3348
|
function runStyle(run, p) {
|
|
@@ -3161,8 +3356,8 @@ function runStyle(run, p) {
|
|
|
3161
3356
|
};
|
|
3162
3357
|
}
|
|
3163
3358
|
function BodyImage({ block, palette: p }) {
|
|
3164
|
-
const [ratio, setRatio] =
|
|
3165
|
-
|
|
3359
|
+
const [ratio, setRatio] = useState2(block.width && block.height ? block.width / block.height : 16 / 9);
|
|
3360
|
+
useEffect2(() => {
|
|
3166
3361
|
if (block.width && block.height) return;
|
|
3167
3362
|
let live = true;
|
|
3168
3363
|
Image.getSize(block.src, (w, h) => live && w > 0 && h > 0 && setRatio(w / h), () => {
|
|
@@ -3171,77 +3366,77 @@ function BodyImage({ block, palette: p }) {
|
|
|
3171
3366
|
live = false;
|
|
3172
3367
|
};
|
|
3173
3368
|
}, [block.src, block.width, block.height]);
|
|
3174
|
-
return /* @__PURE__ */
|
|
3369
|
+
return /* @__PURE__ */ jsx3(Image, { source: { uri: block.src }, accessible: true, accessibilityRole: "image", accessibilityLabel: block.alt || void 0, resizeMode: "contain", style: [styles2.image, { aspectRatio: ratio, backgroundColor: p.surface }] });
|
|
3175
3370
|
}
|
|
3176
3371
|
function BodyVideo({ block, palette: p, onLink }) {
|
|
3177
3372
|
return /* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "link", accessibilityLabel: block.alt, onPress: () => onLink(block.href), style: ({ pressed }) => [styles2.video, { borderColor: p.border, backgroundColor: p.surface, opacity: pressed ? 0.7 : 1 }], children: [
|
|
3178
|
-
/* @__PURE__ */
|
|
3179
|
-
/* @__PURE__ */
|
|
3373
|
+
/* @__PURE__ */ jsx3(Text2, { style: [styles2.play, { color: p.primary }], children: "\u25B6" }),
|
|
3374
|
+
/* @__PURE__ */ jsx3(Text2, { style: { color: p.primary, fontFamily: p.fontFamily, fontWeight: "600" }, children: block.alt })
|
|
3180
3375
|
] });
|
|
3181
3376
|
}
|
|
3182
3377
|
var PAGE_RATIO = 1 / 1.414;
|
|
3183
3378
|
var pageRatio = (p) => p.width && p.height ? p.width / p.height : PAGE_RATIO;
|
|
3184
3379
|
function PdfBadge() {
|
|
3185
|
-
return /* @__PURE__ */
|
|
3380
|
+
return /* @__PURE__ */ jsx3(View2, { style: styles2.badge, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.badgeText, children: "PDF" }) });
|
|
3186
3381
|
}
|
|
3187
3382
|
function BodyFile({ block, palette: p, onLink }) {
|
|
3188
3383
|
return /* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "link", accessibilityLabel: `${block.alt}, PDF`, onPress: () => onLink(block.href), style: ({ pressed }) => [styles2.video, { borderColor: p.border, backgroundColor: p.surface, opacity: pressed ? 0.7 : 1 }], children: [
|
|
3189
|
-
/* @__PURE__ */
|
|
3190
|
-
/* @__PURE__ */
|
|
3384
|
+
/* @__PURE__ */ jsx3(PdfBadge, {}),
|
|
3385
|
+
/* @__PURE__ */ jsx3(Text2, { numberOfLines: 2, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt })
|
|
3191
3386
|
] });
|
|
3192
3387
|
}
|
|
3193
3388
|
function BodyPdf({ block, palette: p, onLink }) {
|
|
3194
3389
|
const pages = block.pages ?? [];
|
|
3195
|
-
const [width, setWidth] =
|
|
3196
|
-
const [index, setIndex] =
|
|
3197
|
-
const [reading, setReading] =
|
|
3390
|
+
const [width, setWidth] = useState2(0);
|
|
3391
|
+
const [index, setIndex] = useState2(0);
|
|
3392
|
+
const [reading, setReading] = useState2(false);
|
|
3198
3393
|
const height = width ? Math.max(...pages.map((pg) => width / pageRatio(pg))) : 0;
|
|
3199
3394
|
const onScroll = (e) => width && setIndex(Math.round(e.nativeEvent.contentOffset.x / width));
|
|
3200
3395
|
return /* @__PURE__ */ jsxs(View2, { style: [styles2.pdf, { borderColor: p.border, backgroundColor: p.surface }], children: [
|
|
3201
3396
|
/* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "button", accessibilityLabel: `Read ${block.alt} full screen`, onPress: () => setReading(true), style: ({ pressed }) => [styles2.pdfHeader, { opacity: pressed ? 0.7 : 1 }], children: [
|
|
3202
|
-
/* @__PURE__ */
|
|
3203
|
-
/* @__PURE__ */
|
|
3204
|
-
/* @__PURE__ */
|
|
3397
|
+
/* @__PURE__ */ jsx3(PdfBadge, {}),
|
|
3398
|
+
/* @__PURE__ */ jsx3(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt }),
|
|
3399
|
+
/* @__PURE__ */ jsx3(Text2, { style: { color: p.muted, fontSize: 16 }, children: "\u2922" })
|
|
3205
3400
|
] }),
|
|
3206
3401
|
/* @__PURE__ */ jsxs(View2, { style: styles2.pdfPages, onLayout: (e) => setWidth(e.nativeEvent.layout.width), children: [
|
|
3207
|
-
width > 0 && /* @__PURE__ */
|
|
3208
|
-
pages.length > 1 && /* @__PURE__ */
|
|
3402
|
+
width > 0 && /* @__PURE__ */ jsx3(ScrollView, { horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: false, onScroll, scrollEventThrottle: 32, accessibilityLabel: `${block.alt}, page ${index + 1} of ${pages.length}`, children: pages.map((pg, i) => /* @__PURE__ */ jsx3(Pressable2, { onPress: () => setReading(true), accessibilityRole: "imagebutton", accessibilityLabel: pg.alt || `Page ${i + 1}`, accessibilityHint: "Opens the document full screen", style: { width, height, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx3(Image, { source: { uri: pg.src }, resizeMode: "contain", style: [styles2.page, { width: Math.min(width, height * pageRatio(pg)), aspectRatio: pageRatio(pg) }] }) }, i)) }),
|
|
3403
|
+
pages.length > 1 && /* @__PURE__ */ jsx3(View2, { pointerEvents: "none", style: styles2.count, children: /* @__PURE__ */ jsxs(Text2, { style: styles2.countText, children: [
|
|
3209
3404
|
index + 1,
|
|
3210
3405
|
" / ",
|
|
3211
3406
|
pages.length
|
|
3212
3407
|
] }) })
|
|
3213
3408
|
] }),
|
|
3214
|
-
/* @__PURE__ */
|
|
3409
|
+
/* @__PURE__ */ jsx3(Modal, { visible: reading, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setReading(false), children: /* @__PURE__ */ jsxs(View2, { style: styles2.reader, children: [
|
|
3215
3410
|
/* @__PURE__ */ jsxs(View2, { style: styles2.readerBar, children: [
|
|
3216
|
-
/* @__PURE__ */
|
|
3217
|
-
/* @__PURE__ */
|
|
3218
|
-
/* @__PURE__ */
|
|
3411
|
+
/* @__PURE__ */ jsx3(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, styles2.readerTitle], accessibilityRole: "header", children: block.alt }),
|
|
3412
|
+
/* @__PURE__ */ jsx3(Pressable2, { accessibilityRole: "link", onPress: () => onLink(block.href), style: styles2.readerBtn, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.readerBtnText, children: "Open PDF \u2197" }) }),
|
|
3413
|
+
/* @__PURE__ */ jsx3(Pressable2, { accessibilityRole: "button", accessibilityLabel: "Close", onPress: () => setReading(false), style: styles2.readerBtn, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.readerBtnText, children: "Done" }) })
|
|
3219
3414
|
] }),
|
|
3220
|
-
/* @__PURE__ */
|
|
3415
|
+
/* @__PURE__ */ jsx3(ScrollView, { contentContainerStyle: styles2.readerPages, children: pages.map((pg, i) => /* @__PURE__ */ jsx3(Image, { source: { uri: pg.src }, accessible: true, accessibilityLabel: pg.alt || `Page ${i + 1}`, resizeMode: "contain", style: [styles2.readerPage, { aspectRatio: pageRatio(pg) }] }, i)) })
|
|
3221
3416
|
] }) })
|
|
3222
3417
|
] });
|
|
3223
3418
|
}
|
|
3224
3419
|
function RichText({ html, palette: p, onLink, size = 15 }) {
|
|
3225
|
-
const blocks =
|
|
3420
|
+
const blocks = useMemo2(() => parseHtml(html), [html]);
|
|
3226
3421
|
if (!blocks.length) return null;
|
|
3227
3422
|
const base = { color: p.text, fontSize: size, lineHeight: Math.round(size * 1.42), fontFamily: p.fontFamily };
|
|
3228
|
-
return /* @__PURE__ */
|
|
3229
|
-
if (b.kind === "image") return /* @__PURE__ */
|
|
3230
|
-
if (b.kind === "video") return /* @__PURE__ */
|
|
3231
|
-
if (b.kind === "pdf") return /* @__PURE__ */
|
|
3232
|
-
if (b.kind === "file") return /* @__PURE__ */
|
|
3233
|
-
const runs = b.runs.map((r, j) => /* @__PURE__ */
|
|
3423
|
+
return /* @__PURE__ */ jsx3(View2, { style: styles2.wrap, children: blocks.map((b, i) => {
|
|
3424
|
+
if (b.kind === "image") return /* @__PURE__ */ jsx3(BodyImage, { block: b, palette: p }, i);
|
|
3425
|
+
if (b.kind === "video") return /* @__PURE__ */ jsx3(BodyVideo, { block: b, palette: p, onLink }, i);
|
|
3426
|
+
if (b.kind === "pdf") return /* @__PURE__ */ jsx3(BodyPdf, { block: b, palette: p, onLink }, i);
|
|
3427
|
+
if (b.kind === "file") return /* @__PURE__ */ jsx3(BodyFile, { block: b, palette: p, onLink }, i);
|
|
3428
|
+
const runs = b.runs.map((r, j) => /* @__PURE__ */ jsx3(Text2, { style: runStyle(r, p), onPress: r.href ? () => onLink(r.href) : void 0, accessibilityRole: r.href ? "link" : void 0, children: r.text }, j));
|
|
3234
3429
|
if (b.kind === "list-item") {
|
|
3235
3430
|
return /* @__PURE__ */ jsxs(View2, { style: [styles2.li, { paddingLeft: (b.level - 1) * 14 }], children: [
|
|
3236
|
-
/* @__PURE__ */
|
|
3237
|
-
/* @__PURE__ */
|
|
3431
|
+
/* @__PURE__ */ jsx3(Text2, { style: [base, styles2.marker], children: b.marker }),
|
|
3432
|
+
/* @__PURE__ */ jsx3(Text2, { style: [base, styles2.flex], children: runs })
|
|
3238
3433
|
] }, i);
|
|
3239
3434
|
}
|
|
3240
3435
|
if (b.kind === "heading") {
|
|
3241
3436
|
const fs = HEADING_SIZE[b.level] ?? 15;
|
|
3242
|
-
return /* @__PURE__ */
|
|
3437
|
+
return /* @__PURE__ */ jsx3(Text2, { accessibilityRole: "header", style: [base, { fontSize: fs, lineHeight: Math.round(fs * 1.3), fontWeight: "700" }], children: runs }, i);
|
|
3243
3438
|
}
|
|
3244
|
-
return /* @__PURE__ */
|
|
3439
|
+
return /* @__PURE__ */ jsx3(Text2, { style: [base, b.kind === "quote" && { borderLeftWidth: 3, borderLeftColor: p.border, paddingLeft: 8, color: p.muted }], children: runs }, i);
|
|
3245
3440
|
}) });
|
|
3246
3441
|
}
|
|
3247
3442
|
var styles2 = StyleSheet2.create({
|
|
@@ -3270,7 +3465,7 @@ var styles2 = StyleSheet2.create({
|
|
|
3270
3465
|
});
|
|
3271
3466
|
|
|
3272
3467
|
// src/ui/Checklist.tsx
|
|
3273
|
-
import { jsx as
|
|
3468
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
3274
3469
|
function Checklist({ view, client, palette: p, geometry, showLauncher, launcherBottom }) {
|
|
3275
3470
|
const config = view.guide.config.checklist;
|
|
3276
3471
|
if (!config) return null;
|
|
@@ -3285,10 +3480,10 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
|
|
|
3285
3480
|
const nextOpen = config.ordered ? config.tasks.find((t) => !done.has(t.id))?.id : void 0;
|
|
3286
3481
|
const bottom = insets.bottom + launcherBottom;
|
|
3287
3482
|
return /* @__PURE__ */ jsxs2(View3, { pointerEvents: "box-none", style: StyleSheet3.absoluteFill, children: [
|
|
3288
|
-
view.open ? /* @__PURE__ */
|
|
3483
|
+
view.open ? /* @__PURE__ */ jsx4(Surface, { palette: p, style: [styles3.panel, side, { width, bottom: bottom + (showLauncher ? 56 : 0), maxHeight: viewport.height - insets.top - bottom - 80 }], children: /* @__PURE__ */ jsxs2(View3, { accessibilityViewIsModal: true, style: styles3.panelInner, children: [
|
|
3289
3484
|
/* @__PURE__ */ jsxs2(View3, { style: styles3.head, children: [
|
|
3290
3485
|
/* @__PURE__ */ jsxs2(View3, { style: styles3.flex, children: [
|
|
3291
|
-
/* @__PURE__ */
|
|
3486
|
+
/* @__PURE__ */ jsx4(Text3, { accessibilityRole: "header", style: [styles3.title, { color: p.text, fontFamily: p.fontFamily }], children: finished ? config.completionTitle : config.launcherLabel }),
|
|
3292
3487
|
/* @__PURE__ */ jsxs2(Text3, { style: [styles3.sub, { color: p.muted }], children: [
|
|
3293
3488
|
completed,
|
|
3294
3489
|
" of ",
|
|
@@ -3296,11 +3491,11 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
|
|
|
3296
3491
|
" complete"
|
|
3297
3492
|
] })
|
|
3298
3493
|
] }),
|
|
3299
|
-
/* @__PURE__ */
|
|
3494
|
+
/* @__PURE__ */ jsx4(CloseButton, { palette: p, onPress: () => client.setChecklistOpen(view.guide.id, false), label: "Close checklist" })
|
|
3300
3495
|
] }),
|
|
3301
|
-
/* @__PURE__ */
|
|
3302
|
-
finished && config.completionBody ? /* @__PURE__ */
|
|
3303
|
-
/* @__PURE__ */
|
|
3496
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.bar, { backgroundColor: p.surface }], accessibilityRole: "progressbar", accessibilityValue: { min: 0, max: total, now: completed }, children: /* @__PURE__ */ jsx4(View3, { style: [styles3.fill, { backgroundColor: p.primary, width: `${total ? completed / total * 100 : 0}%` }] }) }),
|
|
3497
|
+
finished && config.completionBody ? /* @__PURE__ */ jsx4(RichText, { html: config.completionBody, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null,
|
|
3498
|
+
/* @__PURE__ */ jsx4(ScrollView2, { style: styles3.tasks, contentContainerStyle: styles3.tasksContent, children: config.tasks.map((task) => {
|
|
3304
3499
|
const isDone = done.has(task.id);
|
|
3305
3500
|
const locked = !!nextOpen && !isDone && task.id !== nextOpen;
|
|
3306
3501
|
return /* @__PURE__ */ jsxs2(
|
|
@@ -3313,20 +3508,20 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
|
|
|
3313
3508
|
onPress: () => client.checklistTaskPressed(view.guide.id, task.id),
|
|
3314
3509
|
style: ({ pressed }) => [styles3.task, { borderColor: p.border, opacity: locked ? 0.5 : pressed ? 0.7 : 1 }],
|
|
3315
3510
|
children: [
|
|
3316
|
-
/* @__PURE__ */
|
|
3511
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.check, isDone ? { backgroundColor: p.primary, borderColor: p.primary } : { borderColor: p.border }], children: isDone ? /* @__PURE__ */ jsx4(Text3, { style: [styles3.tick, { color: p.onPrimary }], children: "\u2713" }) : null }),
|
|
3317
3512
|
/* @__PURE__ */ jsxs2(View3, { style: styles3.flex, children: [
|
|
3318
3513
|
/* @__PURE__ */ jsxs2(Text3, { style: [styles3.taskTitle, { color: p.text, fontFamily: p.fontFamily }, isDone && { textDecorationLine: "line-through", color: p.muted }], children: [
|
|
3319
3514
|
task.title,
|
|
3320
|
-
!task.required ? /* @__PURE__ */
|
|
3515
|
+
!task.required ? /* @__PURE__ */ jsx4(Text3, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
|
|
3321
3516
|
] }),
|
|
3322
|
-
task.description ? /* @__PURE__ */
|
|
3517
|
+
task.description ? /* @__PURE__ */ jsx4(Text3, { style: [styles3.taskDesc, { color: p.muted }], children: task.description }) : null
|
|
3323
3518
|
] })
|
|
3324
3519
|
]
|
|
3325
3520
|
},
|
|
3326
3521
|
task.id
|
|
3327
3522
|
);
|
|
3328
3523
|
}) }),
|
|
3329
|
-
config.dismissible ? /* @__PURE__ */
|
|
3524
|
+
config.dismissible ? /* @__PURE__ */ jsx4(GButton, { label: finished ? "Close for good" : "Dismiss checklist", variant: "ghost", palette: p, onPress: () => client.dismissChecklist(view.guide.id), style: styles3.dismiss }) : null
|
|
3330
3525
|
] }) }) : null,
|
|
3331
3526
|
showLauncher ? /* @__PURE__ */ jsxs2(
|
|
3332
3527
|
Pressable3,
|
|
@@ -3337,8 +3532,8 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
|
|
|
3337
3532
|
onPress: () => client.setChecklistOpen(view.guide.id, !view.open),
|
|
3338
3533
|
style: ({ pressed }) => [styles3.launcher, side, { bottom, backgroundColor: p.primary, opacity: pressed ? 0.85 : 1 }],
|
|
3339
3534
|
children: [
|
|
3340
|
-
/* @__PURE__ */
|
|
3341
|
-
/* @__PURE__ */
|
|
3535
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.count, { borderColor: p.onPrimary }], children: /* @__PURE__ */ jsx4(Text3, { style: [styles3.countText, { color: p.onPrimary }], children: finished ? "\u2713" : `${completed}/${total}` }) }),
|
|
3536
|
+
/* @__PURE__ */ jsx4(Text3, { style: [styles3.launcherText, { color: p.onPrimary, fontFamily: p.fontFamily }], numberOfLines: 1, children: config.launcherLabel })
|
|
3342
3537
|
]
|
|
3343
3538
|
}
|
|
3344
3539
|
) : null
|
|
@@ -3382,19 +3577,19 @@ var styles3 = StyleSheet3.create({
|
|
|
3382
3577
|
});
|
|
3383
3578
|
|
|
3384
3579
|
// src/ui/Messenger.tsx
|
|
3385
|
-
import { useCallback, useEffect as
|
|
3580
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
3386
3581
|
import { ActivityIndicator, Keyboard, Modal as Modal2, Platform as Platform2, Pressable as Pressable4, ScrollView as ScrollView3, StyleSheet as StyleSheet4, Text as Text4, TextInput, View as View4 } from "react-native";
|
|
3387
|
-
import { Fragment, jsx as
|
|
3582
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3388
3583
|
function useMessenger(store) {
|
|
3389
|
-
return
|
|
3584
|
+
return useSyncExternalStore2(
|
|
3390
3585
|
useCallback((cb) => store.subscribe(cb), [store]),
|
|
3391
3586
|
() => store.getSnapshot(),
|
|
3392
3587
|
() => store.getSnapshot()
|
|
3393
3588
|
);
|
|
3394
3589
|
}
|
|
3395
3590
|
function useKeyboardHeight() {
|
|
3396
|
-
const [h, setH] =
|
|
3397
|
-
|
|
3591
|
+
const [h, setH] = useState3(0);
|
|
3592
|
+
useEffect3(() => {
|
|
3398
3593
|
if (Platform2.OS !== "ios") return;
|
|
3399
3594
|
const show = Keyboard.addListener("keyboardWillShow", (e) => setH(e.endCoordinates.height));
|
|
3400
3595
|
const hide = Keyboard.addListener("keyboardWillHide", () => setH(0));
|
|
@@ -3417,8 +3612,8 @@ function MessengerLauncher({ store, palette: p, style }) {
|
|
|
3417
3612
|
if (snap.open) return null;
|
|
3418
3613
|
const label = snap.unread ? `Open ${snap.config.name}, ${snap.unread} unread` : `Open ${snap.config.name}`;
|
|
3419
3614
|
return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: label, onPress: () => store.open(), style: ({ pressed }) => [styles4.launcher, style, { backgroundColor: p.primary, opacity: pressed ? 0.85 : 1 }], children: [
|
|
3420
|
-
/* @__PURE__ */
|
|
3421
|
-
snap.unread ? /* @__PURE__ */
|
|
3615
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.launcherIcon, { color: p.onPrimary }], children: "\u{1F4AC}" }),
|
|
3616
|
+
snap.unread ? /* @__PURE__ */ jsx5(View4, { style: styles4.badge, children: /* @__PURE__ */ jsx5(Text4, { style: styles4.badgeText, children: snap.unread }) }) : null
|
|
3422
3617
|
] });
|
|
3423
3618
|
}
|
|
3424
3619
|
function MessengerSheet({ store, client, palette: p, topInset, bottomInset }) {
|
|
@@ -3426,14 +3621,14 @@ function MessengerSheet({ store, client, palette: p, topInset, bottomInset }) {
|
|
|
3426
3621
|
const keyboard = useKeyboardHeight();
|
|
3427
3622
|
const v = snap.view;
|
|
3428
3623
|
const title = v.kind === "thread" ? snap.thread?.conversation?.subject || "New conversation" : v.kind === "article" ? "" : v.tab === "home" ? snap.config.name : v.tab === "messages" ? "Messages" : "Help";
|
|
3429
|
-
return /* @__PURE__ */
|
|
3624
|
+
return /* @__PURE__ */ jsx5(Modal2, { visible: snap.open, animationType: "slide", presentationStyle: Platform2.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => v.kind === "tab" ? store.close() : store.back(), children: /* @__PURE__ */ jsxs3(View4, { style: [styles4.sheet, { backgroundColor: p.background, paddingTop: Platform2.OS === "ios" ? 8 : topInset, paddingBottom: keyboard }], children: [
|
|
3430
3625
|
/* @__PURE__ */ jsxs3(View4, { style: [styles4.header, { borderColor: p.border }], children: [
|
|
3431
|
-
v.kind !== "tab" ? /* @__PURE__ */
|
|
3432
|
-
/* @__PURE__ */
|
|
3433
|
-
/* @__PURE__ */
|
|
3626
|
+
v.kind !== "tab" ? /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => store.back(), hitSlop: 10, children: /* @__PURE__ */ jsx5(Text4, { style: [styles4.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
|
|
3627
|
+
/* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", numberOfLines: 1, style: [styles4.title, { color: p.text, fontFamily: p.fontFamily }], children: title }),
|
|
3628
|
+
/* @__PURE__ */ jsx5(CloseButton, { palette: p, onPress: () => store.close(), label: `Close ${snap.config.name}` })
|
|
3434
3629
|
] }),
|
|
3435
|
-
/* @__PURE__ */
|
|
3436
|
-
v.kind === "tab" && !keyboard ? /* @__PURE__ */
|
|
3630
|
+
/* @__PURE__ */ jsx5(View4, { style: styles4.flex, children: v.kind === "thread" ? /* @__PURE__ */ jsx5(Thread, { store, palette: p, bottomInset: keyboard ? 0 : bottomInset }) : v.kind === "article" ? /* @__PURE__ */ jsx5(Article, { article: snap.article, palette: p, client }) : v.tab === "home" ? /* @__PURE__ */ jsx5(Home, { store, palette: p }) : v.tab === "messages" ? /* @__PURE__ */ jsx5(Conversations, { store, palette: p }) : /* @__PURE__ */ jsx5(Help, { store, client, palette: p }) }),
|
|
3631
|
+
v.kind === "tab" && !keyboard ? /* @__PURE__ */ jsx5(Tabs, { store, active: v.tab, palette: p, bottomInset }) : null
|
|
3437
3632
|
] }) });
|
|
3438
3633
|
}
|
|
3439
3634
|
function Tabs({ store, active, palette: p, bottomInset }) {
|
|
@@ -3443,32 +3638,32 @@ function Tabs({ store, active, palette: p, bottomInset }) {
|
|
|
3443
3638
|
["messages", "\u2709\uFE0E", "Messages"],
|
|
3444
3639
|
["help", "?", "Help"]
|
|
3445
3640
|
];
|
|
3446
|
-
return /* @__PURE__ */
|
|
3641
|
+
return /* @__PURE__ */ jsx5(View4, { accessibilityRole: "tablist", style: [styles4.tabs, { borderColor: p.border, paddingBottom: Math.max(8, bottomInset) }], children: tabs.map(([tab, icon, label]) => {
|
|
3447
3642
|
const on = tab === active;
|
|
3448
3643
|
return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "tab", accessibilityState: { selected: on }, accessibilityLabel: tab === "messages" && unread ? `${label}, ${unread} unread` : label, onPress: () => store.setTab(tab), style: styles4.tab, children: [
|
|
3449
|
-
/* @__PURE__ */
|
|
3450
|
-
/* @__PURE__ */
|
|
3451
|
-
tab === "messages" && unread ? /* @__PURE__ */
|
|
3644
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.tabIcon, { color: on ? p.primary : p.muted }], children: icon }),
|
|
3645
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.tabLabel, { color: on ? p.primary : p.muted }], children: label }),
|
|
3646
|
+
tab === "messages" && unread ? /* @__PURE__ */ jsx5(View4, { style: [styles4.dot, styles4.tabDot] }) : null
|
|
3452
3647
|
] }, tab);
|
|
3453
3648
|
}) });
|
|
3454
3649
|
}
|
|
3455
3650
|
function Home({ store, palette: p }) {
|
|
3456
3651
|
const snap = useMessenger(store);
|
|
3457
3652
|
const recent = snap.conversations[0];
|
|
3458
|
-
const [q, setQ] =
|
|
3653
|
+
const [q, setQ] = useState3("");
|
|
3459
3654
|
return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, keyboardShouldPersistTaps: "handled", children: [
|
|
3460
3655
|
/* @__PURE__ */ jsxs3(View4, { style: [styles4.hero, { backgroundColor: p.primary, borderRadius: p.radius }], children: [
|
|
3461
|
-
/* @__PURE__ */
|
|
3462
|
-
/* @__PURE__ */
|
|
3656
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.heroTitle, { color: p.onPrimary }], children: snap.config.greeting || `Hi! I'm ${snap.config.name}.` }),
|
|
3657
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.heroSub, { color: p.onPrimary }], children: snap.config.aiAvailable ? `${snap.config.name} answers right away${snap.config.handoffEnabled ? ", and the team is here when you need a person" : ""}.` : "The team usually replies within a few hours." })
|
|
3463
3658
|
] }),
|
|
3464
|
-
/* @__PURE__ */
|
|
3659
|
+
/* @__PURE__ */ jsx5(GButton, { label: "Send us a message", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth }),
|
|
3465
3660
|
recent ? /* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
|
|
3466
|
-
/* @__PURE__ */
|
|
3467
|
-
/* @__PURE__ */
|
|
3661
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "RECENT" }),
|
|
3662
|
+
/* @__PURE__ */ jsx5(ConversationRow, { c: recent, palette: p, onPress: () => void store.openConversation(recent.id) })
|
|
3468
3663
|
] }) : null,
|
|
3469
3664
|
/* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
|
|
3470
|
-
/* @__PURE__ */
|
|
3471
|
-
/* @__PURE__ */
|
|
3665
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "SEARCH FOR HELP" }),
|
|
3666
|
+
/* @__PURE__ */ jsx5(
|
|
3472
3667
|
TextInput,
|
|
3473
3668
|
{
|
|
3474
3669
|
value: q,
|
|
@@ -3491,35 +3686,35 @@ var pendingSearch = { current: "" };
|
|
|
3491
3686
|
function ConversationRow({ c, palette: p, onPress }) {
|
|
3492
3687
|
return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: `${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`, onPress, style: ({ pressed }) => [styles4.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
|
|
3493
3688
|
/* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
|
|
3494
|
-
/* @__PURE__ */
|
|
3495
|
-
/* @__PURE__ */
|
|
3689
|
+
/* @__PURE__ */ jsx5(Text4, { numberOfLines: 1, style: [styles4.rowTitle, { color: p.text }], children: c.subject || "Conversation" }),
|
|
3690
|
+
/* @__PURE__ */ jsx5(Text4, { numberOfLines: 1, style: [styles4.rowSub, { color: p.muted }], children: c.lastMessagePreview || (c.status === "closed" ? "Closed" : " ") })
|
|
3496
3691
|
] }),
|
|
3497
3692
|
/* @__PURE__ */ jsxs3(View4, { style: styles4.rowEnd, children: [
|
|
3498
|
-
/* @__PURE__ */
|
|
3499
|
-
c.unread ? /* @__PURE__ */
|
|
3693
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.time, { color: p.muted }], children: ago(c.lastMessageAt) }),
|
|
3694
|
+
c.unread ? /* @__PURE__ */ jsx5(View4, { style: [styles4.dot, { backgroundColor: p.primary }] }) : null
|
|
3500
3695
|
] })
|
|
3501
3696
|
] });
|
|
3502
3697
|
}
|
|
3503
3698
|
function Conversations({ store, palette: p }) {
|
|
3504
3699
|
const snap = useMessenger(store);
|
|
3505
3700
|
return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, children: [
|
|
3506
|
-
snap.conversations.length ? snap.conversations.map((c) => /* @__PURE__ */
|
|
3507
|
-
/* @__PURE__ */
|
|
3701
|
+
snap.conversations.length ? snap.conversations.map((c) => /* @__PURE__ */ jsx5(ConversationRow, { c, palette: p, onPress: () => void store.openConversation(c.id) }, c.id)) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.empty, { color: p.muted }], children: "No conversations yet." }),
|
|
3702
|
+
/* @__PURE__ */ jsx5(GButton, { label: "New conversation", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth })
|
|
3508
3703
|
] });
|
|
3509
3704
|
}
|
|
3510
3705
|
function Bubble({ m, text, name, palette: p, onArticle, onGuide }) {
|
|
3511
3706
|
if (m.author === "system")
|
|
3512
|
-
return /* @__PURE__ */
|
|
3707
|
+
return /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "text", style: [styles4.system, { color: p.muted }], children: text });
|
|
3513
3708
|
const mine = m.author === "user";
|
|
3514
3709
|
return /* @__PURE__ */ jsxs3(View4, { style: [styles4.bubbleWrap, mine ? styles4.mine : styles4.theirs], children: [
|
|
3515
|
-
!mine ? /* @__PURE__ */
|
|
3516
|
-
/* @__PURE__ */
|
|
3710
|
+
!mine ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.author, { color: p.muted }], children: m.author === "agent" ? m.authorName || "Team" : name }) : null,
|
|
3711
|
+
/* @__PURE__ */ jsx5(View4, { style: [styles4.bubble, { borderRadius: Math.max(12, p.radius) }, mine ? { backgroundColor: p.primary } : { backgroundColor: p.surface }], children: m.status === "pending" && !text ? /* @__PURE__ */ jsx5(TypingDots, { color: p.muted }) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.bubbleText, { color: mine ? p.onPrimary : p.text }], children: text }) }),
|
|
3517
3712
|
m.citations.length || m.suggestedGuides.length ? /* @__PURE__ */ jsxs3(View4, { style: styles4.chips, children: [
|
|
3518
|
-
m.citations.map((c) => /* @__PURE__ */
|
|
3713
|
+
m.citations.map((c) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "link", accessibilityLabel: `Article: ${c.title}`, onPress: () => onArticle(c.articleId), style: [styles4.chip, { borderColor: p.border }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.text }], children: [
|
|
3519
3714
|
"\u{1F4C4} ",
|
|
3520
3715
|
c.title
|
|
3521
3716
|
] }) }, c.articleId)),
|
|
3522
|
-
m.suggestedGuides.map((g) => /* @__PURE__ */
|
|
3717
|
+
m.suggestedGuides.map((g) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "button", accessibilityLabel: `Show me: ${g.name}`, onPress: () => onGuide(g.key), style: [styles4.chip, { borderColor: p.primary }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.primary }], children: [
|
|
3523
3718
|
"\u25B6 Show me: ",
|
|
3524
3719
|
g.name
|
|
3525
3720
|
] }) }, g.key))
|
|
@@ -3527,18 +3722,18 @@ function Bubble({ m, text, name, palette: p, onArticle, onGuide }) {
|
|
|
3527
3722
|
] });
|
|
3528
3723
|
}
|
|
3529
3724
|
function TypingDots({ color }) {
|
|
3530
|
-
return /* @__PURE__ */
|
|
3725
|
+
return /* @__PURE__ */ jsx5(ActivityIndicator, { size: "small", color, accessibilityLabel: "Typing" });
|
|
3531
3726
|
}
|
|
3532
3727
|
function Thread({ store, palette: p, bottomInset }) {
|
|
3533
3728
|
const snap = useMessenger(store);
|
|
3534
3729
|
const t = snap.thread;
|
|
3535
|
-
const [text, setText] =
|
|
3536
|
-
const scroll =
|
|
3730
|
+
const [text, setText] = useState3(t?.draft ?? "");
|
|
3731
|
+
const scroll = useRef2(null);
|
|
3537
3732
|
const convo = t?.conversation ?? null;
|
|
3538
|
-
|
|
3733
|
+
useEffect3(() => {
|
|
3539
3734
|
if (t?.draft) setText(t.draft);
|
|
3540
3735
|
}, [t?.draft]);
|
|
3541
|
-
const streamingIds =
|
|
3736
|
+
const streamingIds = useMemo3(() => Object.keys(t?.streaming ?? {}).filter((id) => !t?.messages.some((m) => m.id === id)), [t]);
|
|
3542
3737
|
if (!t) return null;
|
|
3543
3738
|
const send = () => {
|
|
3544
3739
|
const value = text;
|
|
@@ -3548,19 +3743,19 @@ function Thread({ store, palette: p, bottomInset }) {
|
|
|
3548
3743
|
const canHandoff = snap.config.handoffEnabled && snap.config.aiAvailable && convo?.status === "bot" && !t.sending;
|
|
3549
3744
|
return /* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
|
|
3550
3745
|
/* @__PURE__ */ jsxs3(ScrollView3, { ref: scroll, contentContainerStyle: styles4.thread, onContentSizeChange: () => scroll.current?.scrollToEnd({ animated: true }), keyboardShouldPersistTaps: "handled", children: [
|
|
3551
|
-
!t.messages.length && !t.sending ? /* @__PURE__ */
|
|
3552
|
-
t.messages.map((m) => /* @__PURE__ */
|
|
3553
|
-
streamingIds.map((id) => /* @__PURE__ */
|
|
3746
|
+
!t.messages.length && !t.sending ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.empty, { color: p.muted }], children: snap.config.greeting || "Ask us anything." }) : null,
|
|
3747
|
+
t.messages.map((m) => /* @__PURE__ */ jsx5(Bubble, { m, text: t.streaming[m.id] ?? m.body, name: snap.config.name, palette: p, onArticle: (id) => void store.openArticle(id), onGuide: (k) => store.startGuide(k) }, m.id)),
|
|
3748
|
+
streamingIds.map((id) => /* @__PURE__ */ jsx5(Bubble, { m: { id, author: "scout", body: "", citations: [], suggestedGuides: [], status: "pending", createdAt: (/* @__PURE__ */ new Date()).toISOString() }, text: t.streaming[id] ?? "", name: snap.config.name, palette: p, onArticle: () => {
|
|
3554
3749
|
}, onGuide: () => {
|
|
3555
3750
|
} }, id)),
|
|
3556
|
-
t.sending && !streamingIds.length && snap.config.aiAvailable && t.messages.length ? /* @__PURE__ */
|
|
3557
|
-
t.error ? /* @__PURE__ */
|
|
3558
|
-
convo?.status === "closed" && convo.rating == null ? /* @__PURE__ */
|
|
3559
|
-
convo?.status === "closed" && convo.rating != null ? /* @__PURE__ */
|
|
3751
|
+
t.sending && !streamingIds.length && snap.config.aiAvailable && t.messages.length ? /* @__PURE__ */ jsx5(TypingDots, { color: p.muted }) : null,
|
|
3752
|
+
t.error ? /* @__PURE__ */ jsx5(Text4, { accessibilityLiveRegion: "polite", style: styles4.error, children: t.error }) : null,
|
|
3753
|
+
convo?.status === "closed" && convo.rating == null ? /* @__PURE__ */ jsx5(Rating, { palette: p, onRate: (n) => void store.rate(n) }) : null,
|
|
3754
|
+
convo?.status === "closed" && convo.rating != null ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.system, { color: p.muted }], children: "Thanks for rating this conversation." }) : null
|
|
3560
3755
|
] }),
|
|
3561
|
-
canHandoff ? /* @__PURE__ */
|
|
3756
|
+
canHandoff ? /* @__PURE__ */ jsx5(GButton, { label: "Talk to a person", variant: "secondary", palette: p, onPress: () => void store.handoff(), style: styles4.handoff }) : null,
|
|
3562
3757
|
/* @__PURE__ */ jsxs3(View4, { style: [styles4.composer, { borderColor: p.border, paddingBottom: Math.max(10, bottomInset) }], children: [
|
|
3563
|
-
/* @__PURE__ */
|
|
3758
|
+
/* @__PURE__ */ jsx5(
|
|
3564
3759
|
TextInput,
|
|
3565
3760
|
{
|
|
3566
3761
|
value: text,
|
|
@@ -3572,26 +3767,26 @@ function Thread({ store, palette: p, bottomInset }) {
|
|
|
3572
3767
|
style: [styles4.input, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(18, p.radius + 6) }]
|
|
3573
3768
|
}
|
|
3574
3769
|
),
|
|
3575
|
-
/* @__PURE__ */
|
|
3770
|
+
/* @__PURE__ */ jsx5(GButton, { label: "Send", palette: p, onPress: send, disabled: !text.trim() || t.sending })
|
|
3576
3771
|
] })
|
|
3577
3772
|
] });
|
|
3578
3773
|
}
|
|
3579
3774
|
function Rating({ palette: p, onRate }) {
|
|
3580
3775
|
return /* @__PURE__ */ jsxs3(View4, { style: styles4.rating, children: [
|
|
3581
|
-
/* @__PURE__ */
|
|
3582
|
-
/* @__PURE__ */
|
|
3776
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.ratingTitle, { color: p.text }], children: "How did we do?" }),
|
|
3777
|
+
/* @__PURE__ */ jsx5(View4, { accessibilityRole: "radiogroup", style: styles4.stars, children: [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "radio", accessibilityLabel: `${n} of 5`, onPress: () => onRate(n), hitSlop: 6, children: /* @__PURE__ */ jsx5(Text4, { style: [styles4.star, { color: p.primary }], children: "\u2605" }) }, n)) })
|
|
3583
3778
|
] });
|
|
3584
3779
|
}
|
|
3585
3780
|
function Help({ store, client, palette: p }) {
|
|
3586
3781
|
const snap = useMessenger(store);
|
|
3587
3782
|
const rc = client.getResourceCenter();
|
|
3588
|
-
const [q, setQ] =
|
|
3589
|
-
const [results, setResults] =
|
|
3590
|
-
const [faq, setFaq] =
|
|
3591
|
-
|
|
3783
|
+
const [q, setQ] = useState3(pendingSearch.current);
|
|
3784
|
+
const [results, setResults] = useState3(null);
|
|
3785
|
+
const [faq, setFaq] = useState3(null);
|
|
3786
|
+
useEffect3(() => {
|
|
3592
3787
|
pendingSearch.current = "";
|
|
3593
3788
|
}, []);
|
|
3594
|
-
|
|
3789
|
+
useEffect3(() => {
|
|
3595
3790
|
const query = q.trim();
|
|
3596
3791
|
if (query.length < 2) return setResults(null);
|
|
3597
3792
|
let alive = true;
|
|
@@ -3606,33 +3801,33 @@ function Help({ store, client, palette: p }) {
|
|
|
3606
3801
|
const articles = snap.articles;
|
|
3607
3802
|
const row = (key, title, sub, onPress, icon = "\u203A") => /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: title, onPress, style: ({ pressed }) => [styles4.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
|
|
3608
3803
|
/* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
|
|
3609
|
-
/* @__PURE__ */
|
|
3610
|
-
sub ? /* @__PURE__ */
|
|
3804
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.rowTitle, { color: p.text }], children: title }),
|
|
3805
|
+
sub ? /* @__PURE__ */ jsx5(Text4, { numberOfLines: 2, style: [styles4.rowSub, { color: p.muted }], children: sub }) : null
|
|
3611
3806
|
] }),
|
|
3612
|
-
/* @__PURE__ */
|
|
3807
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.chev, { color: p.muted }], children: icon })
|
|
3613
3808
|
] }, key);
|
|
3614
3809
|
const open = (a) => a.externalUrl ? client.navigate(a.externalUrl) : void store.openArticle(a.id);
|
|
3615
3810
|
const section = (title, children) => /* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
|
|
3616
|
-
/* @__PURE__ */
|
|
3811
|
+
/* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", style: [styles4.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
|
|
3617
3812
|
children
|
|
3618
3813
|
] }, title);
|
|
3619
3814
|
const audiences = client.getAudienceIds();
|
|
3620
3815
|
const sections = resourceCenterSections(articles?.collections ?? [], articles?.articles ?? [], audiences);
|
|
3621
3816
|
const listed = results?.filter((a) => isArticleListed(articles?.collections ?? [], a, audiences));
|
|
3622
3817
|
return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, keyboardShouldPersistTaps: "handled", children: [
|
|
3623
|
-
/* @__PURE__ */
|
|
3624
|
-
listed ? section(`Results for \u201C${q.trim()}\u201D`, listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => open(a))) : /* @__PURE__ */
|
|
3818
|
+
/* @__PURE__ */ jsx5(TextInput, { value: q, onChangeText: setQ, placeholder: "Search articles", placeholderTextColor: p.muted, accessibilityLabel: "Search help articles", returnKeyType: "search", style: [styles4.search, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(10, p.radius) }] }),
|
|
3819
|
+
listed ? section(`Results for \u201C${q.trim()}\u201D`, listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => open(a))) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.rowSub, { color: p.muted }], children: "No articles found." })) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3625
3820
|
recommended.length ? section("Recommended", recommended.map((g) => row(g.id, g.name, void 0, () => store.startGuide(g.key), "\u25B6"))) : null,
|
|
3626
|
-
!articles ? /* @__PURE__ */
|
|
3821
|
+
!articles ? /* @__PURE__ */ jsx5(ActivityIndicator, { color: p.muted }) : null,
|
|
3627
3822
|
sections.map((s) => section(s.collection ? s.path.join(" \u203A ") : "Articles", s.articles.map((a) => row(a.id, a.title, a.summary, () => open(a))))),
|
|
3628
3823
|
rc?.faqs.length ? section(
|
|
3629
3824
|
"FAQ",
|
|
3630
3825
|
rc.faqs.map((f) => /* @__PURE__ */ jsxs3(View4, { style: [styles4.row, styles4.faq, { borderColor: p.border }], children: [
|
|
3631
3826
|
/* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityState: { expanded: faq === f.id }, onPress: () => setFaq(faq === f.id ? null : f.id), style: styles4.faqHead, children: [
|
|
3632
|
-
/* @__PURE__ */
|
|
3633
|
-
/* @__PURE__ */
|
|
3827
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.rowTitle, styles4.flex, { color: p.text }], children: f.question }),
|
|
3828
|
+
/* @__PURE__ */ jsx5(Text4, { style: [styles4.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
|
|
3634
3829
|
] }),
|
|
3635
|
-
faq === f.id ? /* @__PURE__ */
|
|
3830
|
+
faq === f.id ? /* @__PURE__ */ jsx5(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
|
|
3636
3831
|
] }, f.id))
|
|
3637
3832
|
) : null,
|
|
3638
3833
|
rc?.links.length ? section("Links", rc.links.map((l) => row(l.id, l.label, void 0, () => client.navigate(l.url), "\u2197"))) : null
|
|
@@ -3640,10 +3835,10 @@ function Help({ store, client, palette: p }) {
|
|
|
3640
3835
|
] });
|
|
3641
3836
|
}
|
|
3642
3837
|
function Article({ article, palette: p, client }) {
|
|
3643
|
-
if (!article) return /* @__PURE__ */
|
|
3838
|
+
if (!article) return /* @__PURE__ */ jsx5(ActivityIndicator, { style: styles4.loading, color: p.muted });
|
|
3644
3839
|
return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, children: [
|
|
3645
|
-
/* @__PURE__ */
|
|
3646
|
-
/* @__PURE__ */
|
|
3840
|
+
/* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", style: [styles4.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
|
|
3841
|
+
/* @__PURE__ */ jsx5(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
|
|
3647
3842
|
] });
|
|
3648
3843
|
}
|
|
3649
3844
|
var styles4 = StyleSheet4.create({
|
|
@@ -3703,23 +3898,23 @@ var styles4 = StyleSheet4.create({
|
|
|
3703
3898
|
});
|
|
3704
3899
|
|
|
3705
3900
|
// src/ui/ResourceCenter.tsx
|
|
3706
|
-
import { useEffect as
|
|
3901
|
+
import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
|
|
3707
3902
|
import { Modal as Modal3, Platform as Platform3, Pressable as Pressable5, ScrollView as ScrollView4, StyleSheet as StyleSheet5, Text as Text5, TextInput as TextInput2, View as View5 } from "react-native";
|
|
3708
|
-
import { Fragment as
|
|
3903
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3709
3904
|
function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
|
|
3710
3905
|
const rc = client.getResourceCenter();
|
|
3711
|
-
const [query, setQuery] =
|
|
3712
|
-
const [results, setResults] =
|
|
3713
|
-
const [article, setArticle] =
|
|
3714
|
-
const [faq, setFaq] =
|
|
3715
|
-
|
|
3906
|
+
const [query, setQuery] = useState4("");
|
|
3907
|
+
const [results, setResults] = useState4(null);
|
|
3908
|
+
const [article, setArticle] = useState4(null);
|
|
3909
|
+
const [faq, setFaq] = useState4(null);
|
|
3910
|
+
useEffect4(() => {
|
|
3716
3911
|
if (!open) {
|
|
3717
3912
|
setQuery("");
|
|
3718
3913
|
setResults(null);
|
|
3719
3914
|
setArticle(null);
|
|
3720
3915
|
}
|
|
3721
3916
|
}, [open]);
|
|
3722
|
-
|
|
3917
|
+
useEffect4(() => {
|
|
3723
3918
|
const q = query.trim();
|
|
3724
3919
|
if (q.length < 2) {
|
|
3725
3920
|
setResults(null);
|
|
@@ -3735,8 +3930,8 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
|
|
|
3735
3930
|
};
|
|
3736
3931
|
}, [client, query]);
|
|
3737
3932
|
const guides = client.getGuides();
|
|
3738
|
-
const recommended =
|
|
3739
|
-
const checklists =
|
|
3933
|
+
const recommended = useMemo4(() => (rc?.recommendedGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind !== "checklist"), [rc, guides]);
|
|
3934
|
+
const checklists = useMemo4(() => (rc?.checklistGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind === "checklist"), [rc, guides]);
|
|
3740
3935
|
if (!rc) return null;
|
|
3741
3936
|
const close = () => client.closeResourceCenter();
|
|
3742
3937
|
const openArticle = (a) => {
|
|
@@ -3746,29 +3941,29 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
|
|
|
3746
3941
|
};
|
|
3747
3942
|
const row = (key, title, subtitle, onPress, icon = "\u203A") => /* @__PURE__ */ jsxs4(Pressable5, { accessibilityRole: "button", accessibilityLabel: title, onPress, style: ({ pressed }) => [styles5.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
|
|
3748
3943
|
/* @__PURE__ */ jsxs4(View5, { style: styles5.flex, children: [
|
|
3749
|
-
/* @__PURE__ */
|
|
3750
|
-
subtitle ? /* @__PURE__ */
|
|
3944
|
+
/* @__PURE__ */ jsx6(Text5, { style: [styles5.rowTitle, { color: p.text, fontFamily: p.fontFamily }], children: title }),
|
|
3945
|
+
subtitle ? /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowSub, { color: p.muted }], numberOfLines: 2, children: subtitle }) : null
|
|
3751
3946
|
] }),
|
|
3752
|
-
/* @__PURE__ */
|
|
3947
|
+
/* @__PURE__ */ jsx6(Text5, { style: [styles5.chev, { color: p.muted }], children: icon })
|
|
3753
3948
|
] }, key);
|
|
3754
3949
|
const section = (title, children) => /* @__PURE__ */ jsxs4(View5, { style: styles5.section, children: [
|
|
3755
|
-
/* @__PURE__ */
|
|
3950
|
+
/* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
|
|
3756
3951
|
children
|
|
3757
3952
|
] }, title);
|
|
3758
3953
|
const audiences = client.getAudienceIds();
|
|
3759
3954
|
const sections = resourceCenterSections(rc.collections, rc.articles, audiences);
|
|
3760
3955
|
const listed = results?.filter((a) => isArticleListed(rc.collections, a, audiences));
|
|
3761
|
-
return /* @__PURE__ */
|
|
3956
|
+
return /* @__PURE__ */ jsx6(Modal3, { visible: open, animationType: "slide", presentationStyle: Platform3.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => article ? setArticle(null) : close(), children: /* @__PURE__ */ jsxs4(View5, { style: [styles5.sheet, { backgroundColor: p.background, paddingTop: Platform3.OS === "ios" ? 8 : topInset }], children: [
|
|
3762
3957
|
/* @__PURE__ */ jsxs4(View5, { style: [styles5.header, { borderColor: p.border }], children: [
|
|
3763
|
-
article ? /* @__PURE__ */
|
|
3764
|
-
/* @__PURE__ */
|
|
3765
|
-
/* @__PURE__ */
|
|
3958
|
+
article ? /* @__PURE__ */ jsx6(Pressable5, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => setArticle(null), hitSlop: 10, children: /* @__PURE__ */ jsx6(Text5, { style: [styles5.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
|
|
3959
|
+
/* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.title, { color: p.text, fontFamily: p.fontFamily }], numberOfLines: 1, children: article ? "" : rc.title }),
|
|
3960
|
+
/* @__PURE__ */ jsx6(CloseButton, { palette: p, onPress: close, label: "Close help" })
|
|
3766
3961
|
] }),
|
|
3767
3962
|
article ? /* @__PURE__ */ jsxs4(ScrollView4, { contentContainerStyle: [styles5.content, { paddingBottom: bottomInset + 24 }], children: [
|
|
3768
|
-
/* @__PURE__ */
|
|
3769
|
-
/* @__PURE__ */
|
|
3963
|
+
/* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
|
|
3964
|
+
/* @__PURE__ */ jsx6(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
|
|
3770
3965
|
] }) : /* @__PURE__ */ jsxs4(ScrollView4, { contentContainerStyle: [styles5.content, { paddingBottom: bottomInset + 24 }], keyboardShouldPersistTaps: "handled", children: [
|
|
3771
|
-
/* @__PURE__ */
|
|
3966
|
+
/* @__PURE__ */ jsx6(
|
|
3772
3967
|
TextInput2,
|
|
3773
3968
|
{
|
|
3774
3969
|
value: query,
|
|
@@ -3783,8 +3978,8 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
|
|
|
3783
3978
|
),
|
|
3784
3979
|
listed ? section(
|
|
3785
3980
|
`Results for \u201C${query.trim()}\u201D`,
|
|
3786
|
-
listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))) : /* @__PURE__ */
|
|
3787
|
-
) : /* @__PURE__ */ jsxs4(
|
|
3981
|
+
listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))) : /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowSub, { color: p.muted }], children: "No articles found." })
|
|
3982
|
+
) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3788
3983
|
recommended.length ? section("Recommended", recommended.map((g) => row(g.id, g.name, void 0, () => (close(), client.startGuide(g.id)), "\u25B6"))) : null,
|
|
3789
3984
|
checklists.length ? section("Checklists", checklists.map((g) => row(g.id, g.name, void 0, () => (close(), client.openChecklist(g.id)), "\u2611"))) : null,
|
|
3790
3985
|
sections.map((s) => section(s.collection ? s.path.join(" \u203A ") : "Articles", s.articles.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))))),
|
|
@@ -3792,10 +3987,10 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
|
|
|
3792
3987
|
"FAQ",
|
|
3793
3988
|
rc.faqs.map((f) => /* @__PURE__ */ jsxs4(View5, { style: [styles5.row, styles5.faq, { borderColor: p.border }], children: [
|
|
3794
3989
|
/* @__PURE__ */ jsxs4(Pressable5, { accessibilityRole: "button", accessibilityState: { expanded: faq === f.id }, onPress: () => setFaq(faq === f.id ? null : f.id), style: styles5.faqHead, children: [
|
|
3795
|
-
/* @__PURE__ */
|
|
3796
|
-
/* @__PURE__ */
|
|
3990
|
+
/* @__PURE__ */ jsx6(Text5, { style: [styles5.rowTitle, styles5.flex, { color: p.text }], children: f.question }),
|
|
3991
|
+
/* @__PURE__ */ jsx6(Text5, { style: [styles5.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
|
|
3797
3992
|
] }),
|
|
3798
|
-
faq === f.id ? /* @__PURE__ */
|
|
3993
|
+
faq === f.id ? /* @__PURE__ */ jsx6(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
|
|
3799
3994
|
] }, f.id))
|
|
3800
3995
|
) : null,
|
|
3801
3996
|
rc.links.length ? section("Links", rc.links.map((l) => row(l.id, l.label, void 0, () => client.navigate(l.url), "\u2197"))) : null
|
|
@@ -3823,7 +4018,7 @@ var styles5 = StyleSheet5.create({
|
|
|
3823
4018
|
});
|
|
3824
4019
|
|
|
3825
4020
|
// src/ui/StepLayer.tsx
|
|
3826
|
-
import { useEffect as
|
|
4021
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
|
|
3827
4022
|
import { AccessibilityInfo, Animated, Easing, Pressable as Pressable6, StyleSheet as StyleSheet7, View as View7 } from "react-native";
|
|
3828
4023
|
|
|
3829
4024
|
// src/core/position.ts
|
|
@@ -3865,31 +4060,31 @@ function positionCard(target, size, viewport, insets, placement, offset) {
|
|
|
3865
4060
|
|
|
3866
4061
|
// src/ui/StepCard.tsx
|
|
3867
4062
|
import { Image as Image2, StyleSheet as StyleSheet6, Text as Text6, View as View6 } from "react-native";
|
|
3868
|
-
import { jsx as
|
|
4063
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3869
4064
|
function StepCard({ view, client, palette: p, width, style, compact }) {
|
|
3870
4065
|
const { step, guide } = view;
|
|
3871
4066
|
const buttons = visibleButtons(step, view.canGoBack);
|
|
3872
4067
|
const body = step.type === "custom" ? step.customHtml || step.body : step.body;
|
|
3873
4068
|
const showProgress = step.showProgress && view.total > 1;
|
|
3874
4069
|
const onLink = (url) => client.navigate(url);
|
|
3875
|
-
return /* @__PURE__ */
|
|
3876
|
-
step.imageUrl ? /* @__PURE__ */
|
|
4070
|
+
return /* @__PURE__ */ jsx7(Surface, { palette: p, style: [{ width }, style], children: /* @__PURE__ */ jsxs5(View6, { accessible: false, accessibilityViewIsModal: view.asModal || step.type === "modal", style: compact ? styles6.compact : styles6.inner, children: [
|
|
4071
|
+
step.imageUrl ? /* @__PURE__ */ jsx7(Image2, { source: { uri: step.imageUrl }, style: [styles6.image, { borderRadius: Math.max(0, p.radius - 4) }], resizeMode: "cover", accessibilityIgnoresInvertColors: true }) : null,
|
|
3877
4072
|
step.title ? /* @__PURE__ */ jsxs5(View6, { style: styles6.header, children: [
|
|
3878
|
-
/* @__PURE__ */
|
|
3879
|
-
step.showClose ? /* @__PURE__ */
|
|
4073
|
+
/* @__PURE__ */ jsx7(Text6, { accessibilityRole: "header", style: [styles6.title, { color: p.text, fontFamily: p.fontFamily }], children: step.title }),
|
|
4074
|
+
step.showClose ? /* @__PURE__ */ jsx7(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) : null
|
|
3880
4075
|
] }) : null,
|
|
3881
4076
|
body ? (
|
|
3882
4077
|
// Without a title the close button floats beside the body instead of taking a row.
|
|
3883
|
-
/* @__PURE__ */
|
|
4078
|
+
/* @__PURE__ */ jsx7(View6, { style: !step.title && step.showClose ? styles6.bodyBesideClose : void 0, children: /* @__PURE__ */ jsx7(RichText, { html: body, palette: p, onLink }) })
|
|
3884
4079
|
) : null,
|
|
3885
|
-
!step.title && step.showClose ? /* @__PURE__ */
|
|
4080
|
+
!step.title && step.showClose ? /* @__PURE__ */ jsx7(View6, { style: compact ? styles6.floatingCloseCompact : styles6.floatingClose, children: /* @__PURE__ */ jsx7(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) }) : null,
|
|
3886
4081
|
showProgress || buttons.length ? /* @__PURE__ */ jsxs5(View6, { style: styles6.footer, children: [
|
|
3887
4082
|
showProgress ? /* @__PURE__ */ jsxs5(Text6, { style: [styles6.progress, { color: p.muted }], accessibilityLabel: `Step ${view.index + 1} of ${view.total}`, children: [
|
|
3888
4083
|
view.index + 1,
|
|
3889
4084
|
" of ",
|
|
3890
4085
|
view.total
|
|
3891
|
-
] }) : /* @__PURE__ */
|
|
3892
|
-
/* @__PURE__ */
|
|
4086
|
+
] }) : /* @__PURE__ */ jsx7(View6, { style: styles6.flex }),
|
|
4087
|
+
/* @__PURE__ */ jsx7(View6, { style: styles6.buttons, children: buttons.map((b) => /* @__PURE__ */ jsx7(GButton, { label: b.label, variant: b.variant, palette: p, onPress: () => client.pressButton(guide.id, b) }, b.id)) })
|
|
3893
4088
|
] }) : null
|
|
3894
4089
|
] }) });
|
|
3895
4090
|
}
|
|
@@ -3914,10 +4109,10 @@ var styles6 = StyleSheet6.create({
|
|
|
3914
4109
|
});
|
|
3915
4110
|
|
|
3916
4111
|
// src/ui/StepLayer.tsx
|
|
3917
|
-
import { jsx as
|
|
4112
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3918
4113
|
function useTargetRect(client, targetId, origin) {
|
|
3919
|
-
const [rect, setRect] =
|
|
3920
|
-
|
|
4114
|
+
const [rect, setRect] = useState5(null);
|
|
4115
|
+
useEffect5(() => {
|
|
3921
4116
|
if (!targetId) {
|
|
3922
4117
|
setRect(null);
|
|
3923
4118
|
return;
|
|
@@ -3944,8 +4139,8 @@ function useTargetRect(client, targetId, origin) {
|
|
|
3944
4139
|
return rect;
|
|
3945
4140
|
}
|
|
3946
4141
|
function useFadeIn(key) {
|
|
3947
|
-
const opacity =
|
|
3948
|
-
|
|
4142
|
+
const opacity = useRef3(new Animated.Value(0)).current;
|
|
4143
|
+
useEffect5(() => {
|
|
3949
4144
|
opacity.setValue(0);
|
|
3950
4145
|
Animated.timing(opacity, { toValue: 1, duration: 160, easing: Easing.out(Easing.quad), useNativeDriver: true }).start();
|
|
3951
4146
|
}, [key, opacity]);
|
|
@@ -3957,34 +4152,34 @@ function StepLayer({ view, client, palette: p, geometry }) {
|
|
|
3957
4152
|
const opacity = useFadeIn(view.key);
|
|
3958
4153
|
const { viewport, insets } = geometry;
|
|
3959
4154
|
const cardWidth = Math.min(viewport.width - 32, step.width ?? 340);
|
|
3960
|
-
|
|
4155
|
+
useEffect5(() => {
|
|
3961
4156
|
if (step.type === "hotspot" && !view.expanded) return;
|
|
3962
4157
|
AccessibilityInfo.announceForAccessibility?.(announcement(view));
|
|
3963
4158
|
}, [view.key, view.expanded]);
|
|
3964
|
-
const backdrop = (children) => /* @__PURE__ */
|
|
4159
|
+
const backdrop = (children) => /* @__PURE__ */ jsx8(Pressable6, { accessible: false, style: [StyleSheet7.absoluteFill, { backgroundColor: p.overlay }], onPress: () => client.backdropPressed(view.guide.id), children });
|
|
3965
4160
|
const anchored = !view.asModal && ["tooltip", "popover", "spotlight", "hotspot"].includes(step.type);
|
|
3966
4161
|
if (!anchored && step.type === "banner") {
|
|
3967
4162
|
const top = step.position.startsWith("top");
|
|
3968
|
-
return /* @__PURE__ */
|
|
4163
|
+
return /* @__PURE__ */ jsx8(Animated.View, { pointerEvents: "box-none", style: [styles7.banner, top ? { top: insets.top + 8 } : { bottom: insets.bottom + 8 }, { left: insets.left + 8, right: insets.right + 8, opacity }], children: /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width: viewport.width - insets.left - insets.right - 16, compact: true }) });
|
|
3969
4164
|
}
|
|
3970
4165
|
if (!anchored && step.type === "slideout") {
|
|
3971
4166
|
const wide = viewport.width >= 600;
|
|
3972
4167
|
const right = !step.position.endsWith("left");
|
|
3973
4168
|
const width = wide ? Math.min(380, cardWidth) : viewport.width - insets.left - insets.right - 24;
|
|
3974
|
-
return /* @__PURE__ */
|
|
4169
|
+
return /* @__PURE__ */ jsx8(Animated.View, { pointerEvents: "box-none", style: [styles7.abs, { bottom: insets.bottom + 12, opacity }, wide ? right ? { right: insets.right + 16 } : { left: insets.left + 16 } : { left: insets.left + 12 }], children: /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width }) });
|
|
3975
4170
|
}
|
|
3976
4171
|
if (!anchored) {
|
|
3977
4172
|
return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, styles7.center, { opacity }], children: [
|
|
3978
4173
|
step.showOverlay ? backdrop() : null,
|
|
3979
|
-
/* @__PURE__ */
|
|
4174
|
+
/* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width: Math.min(viewport.width - 40, step.width ?? 360) })
|
|
3980
4175
|
] });
|
|
3981
4176
|
}
|
|
3982
4177
|
if (!rect) return null;
|
|
3983
4178
|
if (step.type === "hotspot") {
|
|
3984
4179
|
const size = step.hotspot?.size ?? 14;
|
|
3985
4180
|
return /* @__PURE__ */ jsxs6(View7, { pointerEvents: "box-none", style: StyleSheet7.absoluteFill, children: [
|
|
3986
|
-
/* @__PURE__ */
|
|
3987
|
-
view.expanded ? /* @__PURE__ */
|
|
4181
|
+
/* @__PURE__ */ jsx8(Hotspot, { x: rect.x + rect.width - size / 2, y: rect.y - size / 2, size, pulse: step.hotspot?.pulse ?? true, color: p.primary, onPress: () => client.toggleHotspot(view.guide.id), label: step.title || "Show tip" }),
|
|
4182
|
+
view.expanded ? /* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth }) : null
|
|
3988
4183
|
] });
|
|
3989
4184
|
}
|
|
3990
4185
|
if (step.type === "spotlight") {
|
|
@@ -4000,40 +4195,40 @@ function StepLayer({ view, client, palette: p, geometry }) {
|
|
|
4000
4195
|
[hole.x + hole.width - r, hole.y + hole.height - r, -2 * r, -2 * r]
|
|
4001
4196
|
];
|
|
4002
4197
|
return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, { opacity }], children: [
|
|
4003
|
-
/* @__PURE__ */
|
|
4004
|
-
/* @__PURE__ */
|
|
4005
|
-
/* @__PURE__ */
|
|
4006
|
-
/* @__PURE__ */
|
|
4007
|
-
r > 0 ? corners.map(([left, top, dx, dy], i) => /* @__PURE__ */
|
|
4008
|
-
/* @__PURE__ */
|
|
4009
|
-
/* @__PURE__ */
|
|
4198
|
+
/* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: 0, height: Math.max(0, hole.y) }] }),
|
|
4199
|
+
/* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: hole.y + hole.height, bottom: 0 }] }),
|
|
4200
|
+
/* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, width: Math.max(0, hole.x), top: hole.y, height: hole.height }] }),
|
|
4201
|
+
/* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: hole.x + hole.width, right: 0, top: hole.y, height: hole.height }] }),
|
|
4202
|
+
r > 0 ? corners.map(([left, top, dx, dy], i) => /* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, { left, top, width: r, height: r, overflow: "hidden" }], children: /* @__PURE__ */ jsx8(View7, { style: [styles7.abs, { left: dx, top: dy, width: 4 * r, height: 4 * r, borderRadius: 2 * r, borderWidth: r, borderColor: p.overlay }] }) }, i)) : null,
|
|
4203
|
+
/* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, { left: hole.x, top: hole.y, width: hole.width, height: hole.height, borderRadius: r, borderWidth: 2, borderColor: p.primary }] }),
|
|
4204
|
+
/* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect: hole, width: cardWidth })
|
|
4010
4205
|
] });
|
|
4011
4206
|
}
|
|
4012
4207
|
return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, { opacity }], children: [
|
|
4013
4208
|
step.showOverlay ? backdrop() : null,
|
|
4014
|
-
/* @__PURE__ */
|
|
4209
|
+
/* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth })
|
|
4015
4210
|
] });
|
|
4016
4211
|
}
|
|
4017
4212
|
var ARROW = 12;
|
|
4018
4213
|
function Anchored({ view, client, palette: p, geometry, rect, width }) {
|
|
4019
|
-
const [height, setHeight] =
|
|
4214
|
+
const [height, setHeight] = useState5(0);
|
|
4020
4215
|
const onLayout = (e) => setHeight(Math.round(e.nativeEvent.layout.height));
|
|
4021
4216
|
const offset = view.step.offset + ARROW / 2;
|
|
4022
4217
|
const pos = positionCard(rect, { width, height: height || 120 }, geometry.viewport, geometry.insets, view.step.placement, offset);
|
|
4023
4218
|
return /* @__PURE__ */ jsxs6(View7, { pointerEvents: "box-none", style: [styles7.abs, { left: pos.x, top: pos.y, width, opacity: height ? 1 : 0 }], onLayout, children: [
|
|
4024
|
-
/* @__PURE__ */
|
|
4025
|
-
/* @__PURE__ */
|
|
4219
|
+
/* @__PURE__ */ jsx8(Arrow, { side: pos.side, at: pos.arrow, color: p.background, border: p.border }),
|
|
4220
|
+
/* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width })
|
|
4026
4221
|
] });
|
|
4027
4222
|
}
|
|
4028
4223
|
function Arrow({ side, at, color, border }) {
|
|
4029
4224
|
const half = ARROW / 2;
|
|
4030
4225
|
const place = side === "bottom" ? { top: -half, left: at - half } : side === "top" ? { bottom: -half, left: at - half } : side === "right" ? { left: -half, top: at - half } : { right: -half, top: at - half };
|
|
4031
|
-
return /* @__PURE__ */
|
|
4226
|
+
return /* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, styles7.arrow, place, { backgroundColor: color, borderColor: border }] });
|
|
4032
4227
|
}
|
|
4033
4228
|
function Hotspot({ x, y, size, pulse, color, onPress, label }) {
|
|
4034
|
-
const scale =
|
|
4035
|
-
const [reduceMotion, setReduceMotion] =
|
|
4036
|
-
|
|
4229
|
+
const scale = useRef3(new Animated.Value(0)).current;
|
|
4230
|
+
const [reduceMotion, setReduceMotion] = useState5(false);
|
|
4231
|
+
useEffect5(() => {
|
|
4037
4232
|
let alive = true;
|
|
4038
4233
|
AccessibilityInfo.isReduceMotionEnabled?.().then((v) => alive && setReduceMotion(v), () => {
|
|
4039
4234
|
});
|
|
@@ -4041,21 +4236,21 @@ function Hotspot({ x, y, size, pulse, color, onPress, label }) {
|
|
|
4041
4236
|
alive = false;
|
|
4042
4237
|
};
|
|
4043
4238
|
}, []);
|
|
4044
|
-
|
|
4239
|
+
useEffect5(() => {
|
|
4045
4240
|
if (!pulse || reduceMotion) return;
|
|
4046
4241
|
const loop = Animated.loop(Animated.timing(scale, { toValue: 1, duration: 1400, easing: Easing.out(Easing.ease), useNativeDriver: true }));
|
|
4047
4242
|
loop.start();
|
|
4048
4243
|
return () => loop.stop();
|
|
4049
4244
|
}, [pulse, reduceMotion, scale]);
|
|
4050
4245
|
return /* @__PURE__ */ jsxs6(Pressable6, { accessibilityRole: "button", accessibilityLabel: label, onPress, hitSlop: 14, style: [styles7.abs, { left: x, top: y, width: size, height: size }], children: [
|
|
4051
|
-
pulse && !reduceMotion ? /* @__PURE__ */
|
|
4246
|
+
pulse && !reduceMotion ? /* @__PURE__ */ jsx8(
|
|
4052
4247
|
Animated.View,
|
|
4053
4248
|
{
|
|
4054
4249
|
pointerEvents: "none",
|
|
4055
4250
|
style: [styles7.abs, { width: size, height: size, borderRadius: size / 2, backgroundColor: color, opacity: scale.interpolate({ inputRange: [0, 1], outputRange: [0.55, 0] }), transform: [{ scale: scale.interpolate({ inputRange: [0, 1], outputRange: [1, 2.6] }) }] }]
|
|
4056
4251
|
}
|
|
4057
4252
|
) : null,
|
|
4058
|
-
/* @__PURE__ */
|
|
4253
|
+
/* @__PURE__ */ jsx8(View7, { style: { width: size, height: size, borderRadius: size / 2, backgroundColor: color, borderWidth: 2, borderColor: "#ffffff" } })
|
|
4059
4254
|
] });
|
|
4060
4255
|
}
|
|
4061
4256
|
var styles7 = StyleSheet7.create({
|
|
@@ -4066,7 +4261,7 @@ var styles7 = StyleSheet7.create({
|
|
|
4066
4261
|
});
|
|
4067
4262
|
|
|
4068
4263
|
// src/ui/Survey.tsx
|
|
4069
|
-
import { useEffect as
|
|
4264
|
+
import { useEffect as useEffect6, useState as useState6 } from "react";
|
|
4070
4265
|
import {
|
|
4071
4266
|
AccessibilityInfo as AccessibilityInfo2,
|
|
4072
4267
|
KeyboardAvoidingView,
|
|
@@ -4078,7 +4273,7 @@ import {
|
|
|
4078
4273
|
TextInput as TextInput3,
|
|
4079
4274
|
View as View8
|
|
4080
4275
|
} from "react-native";
|
|
4081
|
-
import { jsx as
|
|
4276
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4082
4277
|
function Survey({
|
|
4083
4278
|
view,
|
|
4084
4279
|
client,
|
|
@@ -4086,9 +4281,9 @@ function Survey({
|
|
|
4086
4281
|
geometry
|
|
4087
4282
|
}) {
|
|
4088
4283
|
const config = view.guide.config.survey;
|
|
4089
|
-
const [answers, setAnswers] =
|
|
4090
|
-
const [error, setError] =
|
|
4091
|
-
|
|
4284
|
+
const [answers, setAnswers] = useState6({});
|
|
4285
|
+
const [error, setError] = useState6("");
|
|
4286
|
+
useEffect6(() => {
|
|
4092
4287
|
setAnswers({});
|
|
4093
4288
|
setError("");
|
|
4094
4289
|
AccessibilityInfo2.announceForAccessibility?.(
|
|
@@ -4108,11 +4303,11 @@ function Survey({
|
|
|
4108
4303
|
return next;
|
|
4109
4304
|
});
|
|
4110
4305
|
const submit = () => {
|
|
4111
|
-
const
|
|
4306
|
+
const missing2 = config.questions.filter(
|
|
4112
4307
|
(q) => q.required && isQuestionVisible(q, answers) && answers[q.id] === void 0
|
|
4113
4308
|
);
|
|
4114
|
-
if (
|
|
4115
|
-
setError(`Please answer: ${
|
|
4309
|
+
if (missing2.length) {
|
|
4310
|
+
setError(`Please answer: ${missing2.map((q) => q.title).join(", ")}`);
|
|
4116
4311
|
return;
|
|
4117
4312
|
}
|
|
4118
4313
|
setError("");
|
|
@@ -4128,7 +4323,7 @@ function Survey({
|
|
|
4128
4323
|
behavior: Platform4.OS === "ios" ? "padding" : void 0,
|
|
4129
4324
|
style: StyleSheet8.absoluteFill,
|
|
4130
4325
|
children: [
|
|
4131
|
-
centered ? /* @__PURE__ */
|
|
4326
|
+
centered ? /* @__PURE__ */ jsx9(
|
|
4132
4327
|
Pressable7,
|
|
4133
4328
|
{
|
|
4134
4329
|
accessible: false,
|
|
@@ -4136,7 +4331,7 @@ function Survey({
|
|
|
4136
4331
|
onPress: close
|
|
4137
4332
|
}
|
|
4138
4333
|
) : null,
|
|
4139
|
-
/* @__PURE__ */
|
|
4334
|
+
/* @__PURE__ */ jsx9(
|
|
4140
4335
|
View8,
|
|
4141
4336
|
{
|
|
4142
4337
|
pointerEvents: "box-none",
|
|
@@ -4145,13 +4340,13 @@ function Survey({
|
|
|
4145
4340
|
centered ? styles8.center : styles8.bottom,
|
|
4146
4341
|
!centered && { paddingBottom: insets.bottom + 12 }
|
|
4147
4342
|
],
|
|
4148
|
-
children: /* @__PURE__ */
|
|
4343
|
+
children: /* @__PURE__ */ jsx9(
|
|
4149
4344
|
Surface,
|
|
4150
4345
|
{
|
|
4151
4346
|
palette: p,
|
|
4152
4347
|
style: { width, maxHeight: viewport.height - insets.top - insets.bottom - 40 },
|
|
4153
4348
|
children: view.submitted ? /* @__PURE__ */ jsxs7(View8, { style: styles8.inner, children: [
|
|
4154
|
-
/* @__PURE__ */
|
|
4349
|
+
/* @__PURE__ */ jsx9(
|
|
4155
4350
|
Text7,
|
|
4156
4351
|
{
|
|
4157
4352
|
accessibilityRole: "header",
|
|
@@ -4159,7 +4354,7 @@ function Survey({
|
|
|
4159
4354
|
children: config.thankYouTitle
|
|
4160
4355
|
}
|
|
4161
4356
|
),
|
|
4162
|
-
config.thankYouBody ? /* @__PURE__ */
|
|
4357
|
+
config.thankYouBody ? /* @__PURE__ */ jsx9(
|
|
4163
4358
|
RichText,
|
|
4164
4359
|
{
|
|
4165
4360
|
html: config.thankYouBody,
|
|
@@ -4167,18 +4362,18 @@ function Survey({
|
|
|
4167
4362
|
onLink: (u) => client.navigate(u)
|
|
4168
4363
|
}
|
|
4169
4364
|
) : null,
|
|
4170
|
-
/* @__PURE__ */
|
|
4365
|
+
/* @__PURE__ */ jsx9(GButton, { label: "Done", palette: p, onPress: close, style: styles8.end })
|
|
4171
4366
|
] }) : /* @__PURE__ */ jsxs7(ScrollView5, { contentContainerStyle: styles8.inner, keyboardShouldPersistTaps: "handled", children: [
|
|
4172
4367
|
config.questions.filter((q) => isQuestionVisible(q, answers)).map((q, i) => /* @__PURE__ */ jsxs7(View8, { style: styles8.question, children: [
|
|
4173
4368
|
/* @__PURE__ */ jsxs7(View8, { style: styles8.head, children: [
|
|
4174
4369
|
/* @__PURE__ */ jsxs7(Text7, { style: [styles8.qTitle, { color: p.text, fontFamily: p.fontFamily }], children: [
|
|
4175
4370
|
q.title,
|
|
4176
|
-
!q.required ? /* @__PURE__ */
|
|
4371
|
+
!q.required ? /* @__PURE__ */ jsx9(Text7, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
|
|
4177
4372
|
] }),
|
|
4178
|
-
i === 0 ? /* @__PURE__ */
|
|
4373
|
+
i === 0 ? /* @__PURE__ */ jsx9(CloseButton, { palette: p, onPress: close, label: "Dismiss survey" }) : null
|
|
4179
4374
|
] }),
|
|
4180
|
-
q.description ? /* @__PURE__ */
|
|
4181
|
-
/* @__PURE__ */
|
|
4375
|
+
q.description ? /* @__PURE__ */ jsx9(Text7, { style: [styles8.desc, { color: p.muted }], children: q.description }) : null,
|
|
4376
|
+
/* @__PURE__ */ jsx9(
|
|
4182
4377
|
QuestionInput,
|
|
4183
4378
|
{
|
|
4184
4379
|
q,
|
|
@@ -4188,8 +4383,8 @@ function Survey({
|
|
|
4188
4383
|
}
|
|
4189
4384
|
)
|
|
4190
4385
|
] }, q.id)),
|
|
4191
|
-
error ? /* @__PURE__ */
|
|
4192
|
-
/* @__PURE__ */
|
|
4386
|
+
error ? /* @__PURE__ */ jsx9(Text7, { accessibilityLiveRegion: "polite", style: [styles8.error], children: error }) : null,
|
|
4387
|
+
/* @__PURE__ */ jsx9(GButton, { label: "Submit", palette: p, onPress: submit, style: styles8.end })
|
|
4193
4388
|
] })
|
|
4194
4389
|
}
|
|
4195
4390
|
)
|
|
@@ -4225,7 +4420,7 @@ function QuestionInput({
|
|
|
4225
4420
|
const max = q.type === "nps" ? 10 : q.scaleMax ?? 5;
|
|
4226
4421
|
const values = Array.from({ length: max - min + 1 }, (_, i) => min + i);
|
|
4227
4422
|
return /* @__PURE__ */ jsxs7(View8, { children: [
|
|
4228
|
-
/* @__PURE__ */
|
|
4423
|
+
/* @__PURE__ */ jsx9(View8, { style: styles8.scale, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: values.map((n) => /* @__PURE__ */ jsx9(
|
|
4229
4424
|
Pressable7,
|
|
4230
4425
|
{
|
|
4231
4426
|
accessibilityRole: "radio",
|
|
@@ -4233,19 +4428,19 @@ function QuestionInput({
|
|
|
4233
4428
|
accessibilityLabel: String(n),
|
|
4234
4429
|
onPress: () => onChange(n),
|
|
4235
4430
|
style: [...chip(value === n), styles8.scaleItem],
|
|
4236
|
-
children: /* @__PURE__ */
|
|
4431
|
+
children: /* @__PURE__ */ jsx9(Text7, { style: chipText(value === n), children: n })
|
|
4237
4432
|
},
|
|
4238
4433
|
n
|
|
4239
4434
|
)) }),
|
|
4240
4435
|
q.lowLabel || q.highLabel ? /* @__PURE__ */ jsxs7(View8, { style: styles8.labels, children: [
|
|
4241
|
-
/* @__PURE__ */
|
|
4242
|
-
/* @__PURE__ */
|
|
4436
|
+
/* @__PURE__ */ jsx9(Text7, { style: [styles8.small, { color: p.muted }], children: q.lowLabel }),
|
|
4437
|
+
/* @__PURE__ */ jsx9(Text7, { style: [styles8.small, { color: p.muted }], children: q.highLabel })
|
|
4243
4438
|
] }) : null
|
|
4244
4439
|
] });
|
|
4245
4440
|
}
|
|
4246
4441
|
case "rating": {
|
|
4247
4442
|
const max = q.scaleMax ?? 5;
|
|
4248
|
-
return /* @__PURE__ */
|
|
4443
|
+
return /* @__PURE__ */ jsx9(View8, { style: styles8.stars, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: Array.from({ length: max }, (_, i) => i + 1).map((n) => /* @__PURE__ */ jsx9(
|
|
4249
4444
|
Pressable7,
|
|
4250
4445
|
{
|
|
4251
4446
|
accessibilityRole: "radio",
|
|
@@ -4253,7 +4448,7 @@ function QuestionInput({
|
|
|
4253
4448
|
accessibilityState: { selected: value === n },
|
|
4254
4449
|
onPress: () => onChange(n),
|
|
4255
4450
|
hitSlop: 4,
|
|
4256
|
-
children: /* @__PURE__ */
|
|
4451
|
+
children: /* @__PURE__ */ jsx9(
|
|
4257
4452
|
Text7,
|
|
4258
4453
|
{
|
|
4259
4454
|
style: [
|
|
@@ -4268,10 +4463,10 @@ function QuestionInput({
|
|
|
4268
4463
|
)) });
|
|
4269
4464
|
}
|
|
4270
4465
|
case "thumbs":
|
|
4271
|
-
return /* @__PURE__ */
|
|
4466
|
+
return /* @__PURE__ */ jsx9(View8, { style: styles8.row, accessibilityRole: "radiogroup", children: [
|
|
4272
4467
|
[true, "\u{1F44D}", "Thumbs up"],
|
|
4273
4468
|
[false, "\u{1F44E}", "Thumbs down"]
|
|
4274
|
-
].map(([val, icon, label]) => /* @__PURE__ */
|
|
4469
|
+
].map(([val, icon, label]) => /* @__PURE__ */ jsx9(
|
|
4275
4470
|
Pressable7,
|
|
4276
4471
|
{
|
|
4277
4472
|
accessibilityRole: "radio",
|
|
@@ -4279,7 +4474,7 @@ function QuestionInput({
|
|
|
4279
4474
|
accessibilityState: { selected: value === val },
|
|
4280
4475
|
onPress: () => onChange(val),
|
|
4281
4476
|
style: [...chip(value === val), styles8.thumb],
|
|
4282
|
-
children: /* @__PURE__ */
|
|
4477
|
+
children: /* @__PURE__ */ jsx9(Text7, { style: styles8.thumbText, children: icon })
|
|
4283
4478
|
},
|
|
4284
4479
|
String(val)
|
|
4285
4480
|
)) });
|
|
@@ -4289,7 +4484,7 @@ function QuestionInput({
|
|
|
4289
4484
|
const selected = new Set(
|
|
4290
4485
|
multi ? value ?? [] : value !== void 0 ? [String(value)] : []
|
|
4291
4486
|
);
|
|
4292
|
-
return /* @__PURE__ */
|
|
4487
|
+
return /* @__PURE__ */ jsx9(View8, { style: styles8.choices, children: q.options.map((o) => {
|
|
4293
4488
|
const on = selected.has(o.id);
|
|
4294
4489
|
return /* @__PURE__ */ jsxs7(
|
|
4295
4490
|
Pressable7,
|
|
@@ -4309,7 +4504,7 @@ function QuestionInput({
|
|
|
4309
4504
|
{ borderColor: on ? p.primary : p.border, borderRadius: Math.min(10, p.radius) }
|
|
4310
4505
|
],
|
|
4311
4506
|
children: [
|
|
4312
|
-
/* @__PURE__ */
|
|
4507
|
+
/* @__PURE__ */ jsx9(
|
|
4313
4508
|
View8,
|
|
4314
4509
|
{
|
|
4315
4510
|
style: [
|
|
@@ -4322,7 +4517,7 @@ function QuestionInput({
|
|
|
4322
4517
|
]
|
|
4323
4518
|
}
|
|
4324
4519
|
),
|
|
4325
|
-
/* @__PURE__ */
|
|
4520
|
+
/* @__PURE__ */ jsx9(Text7, { style: [styles8.choiceText, { color: p.text }], children: o.label })
|
|
4326
4521
|
]
|
|
4327
4522
|
},
|
|
4328
4523
|
o.id
|
|
@@ -4331,7 +4526,7 @@ function QuestionInput({
|
|
|
4331
4526
|
}
|
|
4332
4527
|
case "text":
|
|
4333
4528
|
default:
|
|
4334
|
-
return /* @__PURE__ */
|
|
4529
|
+
return /* @__PURE__ */ jsx9(
|
|
4335
4530
|
TextInput3,
|
|
4336
4531
|
{
|
|
4337
4532
|
accessibilityLabel: q.title,
|
|
@@ -4439,7 +4634,7 @@ function palette(theme, systemScheme, explicit) {
|
|
|
4439
4634
|
}
|
|
4440
4635
|
|
|
4441
4636
|
// src/ui/Overlay.tsx
|
|
4442
|
-
import { jsx as
|
|
4637
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4443
4638
|
function defaultInsets(width, height) {
|
|
4444
4639
|
if (Platform5.OS === "ios") {
|
|
4445
4640
|
const notch = Math.max(width, height) >= 812 && !Platform5.isPad;
|
|
@@ -4449,16 +4644,16 @@ function defaultInsets(width, height) {
|
|
|
4449
4644
|
return { top: StatusBar.currentHeight ?? 24, bottom: 16, left: 0, right: 0 };
|
|
4450
4645
|
}
|
|
4451
4646
|
function GuidelyOverlay({ client, insets: insetsProp, launcherOffset }) {
|
|
4452
|
-
const ui =
|
|
4647
|
+
const ui = useSyncExternalStore3(
|
|
4453
4648
|
useCallback2((cb) => client.subscribeUi(cb), [client]),
|
|
4454
4649
|
() => client.getUiState(),
|
|
4455
4650
|
() => client.getUiState()
|
|
4456
4651
|
);
|
|
4457
4652
|
const { width, height } = useWindowDimensions();
|
|
4458
4653
|
const scheme = useColorScheme();
|
|
4459
|
-
const ref =
|
|
4460
|
-
const [origin, setOrigin] =
|
|
4461
|
-
|
|
4654
|
+
const ref = useRef4(null);
|
|
4655
|
+
const [origin, setOrigin] = useState7({ x: 0, y: 0 });
|
|
4656
|
+
useEffect7(() => {
|
|
4462
4657
|
client.targets.setViewport(width, height);
|
|
4463
4658
|
}, [client, width, height]);
|
|
4464
4659
|
const measureOrigin = useCallback2(() => {
|
|
@@ -4474,21 +4669,21 @@ function GuidelyOverlay({ client, insets: insetsProp, launcherOffset }) {
|
|
|
4474
4669
|
const messengerLeft = messenger?.getSnapshot().config.launcher.position === "bottom-left";
|
|
4475
4670
|
const checklistOnSameSide = ui.checklists.some((c) => c.guide.config.checklist?.position === "bottom-left" === messengerLeft) && client.options.showChecklistLauncher !== false;
|
|
4476
4671
|
return /* @__PURE__ */ jsxs8(View9, { ref, collapsable: false, pointerEvents: "box-none", style: StyleSheet9.absoluteFill, onLayout: measureOrigin, children: [
|
|
4477
|
-
ui.checklists.map((c) => /* @__PURE__ */
|
|
4478
|
-
showRcLauncher ? /* @__PURE__ */
|
|
4672
|
+
ui.checklists.map((c) => /* @__PURE__ */ jsx10(Checklist, { view: c, client, palette: palette(client.themeFor(c.guide), scheme), geometry, showLauncher: client.options.showChecklistLauncher !== false, launcherBottom: launcherOffset }, c.guide.id)),
|
|
4673
|
+
showRcLauncher ? /* @__PURE__ */ jsx10(
|
|
4479
4674
|
Pressable8,
|
|
4480
4675
|
{
|
|
4481
4676
|
accessibilityRole: "button",
|
|
4482
4677
|
accessibilityLabel: client.getResourceCenter()?.launcher.label || "Help",
|
|
4483
4678
|
onPress: () => client.openResourceCenter(),
|
|
4484
4679
|
style: [styles9.rc, client.getResourceCenter()?.launcher.position === "bottom-left" ? { left: insets.left + 16 } : { right: insets.right + 16 }, { bottom: insets.bottom + launcherOffset + (ui.checklists.length ? 56 : 0), backgroundColor: basePalette.primary }],
|
|
4485
|
-
children: /* @__PURE__ */
|
|
4680
|
+
children: /* @__PURE__ */ jsx10(Text8, { style: [styles9.rcText, { color: basePalette.onPrimary }], children: "?" })
|
|
4486
4681
|
}
|
|
4487
4682
|
) : null,
|
|
4488
|
-
messenger && client.options.showMessengerLauncher !== false ? /* @__PURE__ */
|
|
4489
|
-
ui.survey ? /* @__PURE__ */
|
|
4490
|
-
ui.steps.map((s) => /* @__PURE__ */
|
|
4491
|
-
messenger ? /* @__PURE__ */
|
|
4683
|
+
messenger && client.options.showMessengerLauncher !== false ? /* @__PURE__ */ jsx10(MessengerLauncher, { store: messenger, palette: basePalette, style: [messengerLeft ? { left: insets.left + 16 } : { right: insets.right + 16 }, { bottom: insets.bottom + launcherOffset + (checklistOnSameSide ? 56 : 0) }] }) : null,
|
|
4684
|
+
ui.survey ? /* @__PURE__ */ jsx10(Survey, { view: ui.survey, client, palette: palette(client.themeFor(ui.survey.guide), scheme), geometry }) : null,
|
|
4685
|
+
ui.steps.map((s) => /* @__PURE__ */ jsx10(StepLayer, { view: s, client, palette: palette(client.themeFor(s.guide), scheme, s.guide.config.appearance), geometry }, s.key)),
|
|
4686
|
+
messenger ? /* @__PURE__ */ jsx10(MessengerSheet, { store: messenger, client, palette: basePalette, topInset: insets.top, bottomInset: insets.bottom }) : /* @__PURE__ */ jsx10(ResourceCenter, { client, palette: basePalette, open: ui.resourceCenterOpen, topInset: insets.top, bottomInset: insets.bottom })
|
|
4492
4687
|
] });
|
|
4493
4688
|
}
|
|
4494
4689
|
var styles9 = StyleSheet9.create({
|
|
@@ -4497,24 +4692,24 @@ var styles9 = StyleSheet9.create({
|
|
|
4497
4692
|
});
|
|
4498
4693
|
|
|
4499
4694
|
// src/ui/Target.tsx
|
|
4500
|
-
import { createContext, useCallback as useCallback3, useContext, useEffect as
|
|
4695
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect8, useRef as useRef5 } from "react";
|
|
4501
4696
|
import { View as View10 } from "react-native";
|
|
4502
|
-
import { jsx as
|
|
4503
|
-
var GuidelyContext =
|
|
4504
|
-
var GuidelyScreenContext =
|
|
4697
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
4698
|
+
var GuidelyContext = createContext2(null);
|
|
4699
|
+
var GuidelyScreenContext = createContext2(null);
|
|
4505
4700
|
function GuidelyScreenScope({ name, children }) {
|
|
4506
|
-
return /* @__PURE__ */
|
|
4701
|
+
return /* @__PURE__ */ jsx11(GuidelyScreenContext.Provider, { value: name, children });
|
|
4507
4702
|
}
|
|
4508
4703
|
function guidelyScreenLayout({ route, children }) {
|
|
4509
|
-
return /* @__PURE__ */
|
|
4704
|
+
return /* @__PURE__ */ jsx11(GuidelyScreenScope, { name: route.name, children });
|
|
4510
4705
|
}
|
|
4511
4706
|
var TAP_SLOP = 10;
|
|
4512
4707
|
function useGuidelyTarget(id) {
|
|
4513
|
-
const client =
|
|
4514
|
-
const screen =
|
|
4515
|
-
const ref =
|
|
4516
|
-
const start =
|
|
4517
|
-
|
|
4708
|
+
const client = useContext2(GuidelyContext);
|
|
4709
|
+
const screen = useContext2(GuidelyScreenContext);
|
|
4710
|
+
const ref = useRef5(null);
|
|
4711
|
+
const start = useRef5(null);
|
|
4712
|
+
useEffect8(() => {
|
|
4518
4713
|
if (!client || !id) return;
|
|
4519
4714
|
return client.targets.register(id, {
|
|
4520
4715
|
screen,
|
|
@@ -4541,7 +4736,7 @@ function useGuidelyTarget(id) {
|
|
|
4541
4736
|
}
|
|
4542
4737
|
function GuidelyTarget({ id, children, onLayout, onTouchStart, onTouchEnd, ...rest }) {
|
|
4543
4738
|
const t = useGuidelyTarget(id);
|
|
4544
|
-
return /* @__PURE__ */
|
|
4739
|
+
return /* @__PURE__ */ jsx11(
|
|
4545
4740
|
View10,
|
|
4546
4741
|
{
|
|
4547
4742
|
...rest,
|
|
@@ -4565,7 +4760,8 @@ function GuidelyTarget({ id, children, onLayout, onTouchStart, onTouchEnd, ...re
|
|
|
4565
4760
|
}
|
|
4566
4761
|
|
|
4567
4762
|
// src/index.tsx
|
|
4568
|
-
import {
|
|
4763
|
+
import { createFlagsClient as createFlagsClient3, InMemoryStickyBucketStore } from "@guidelytech/flags";
|
|
4764
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4569
4765
|
function deviceLocale() {
|
|
4570
4766
|
try {
|
|
4571
4767
|
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
@@ -4586,18 +4782,38 @@ function defaultBridge(appVersion) {
|
|
|
4586
4782
|
})
|
|
4587
4783
|
};
|
|
4588
4784
|
}
|
|
4589
|
-
function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...options }) {
|
|
4590
|
-
const [client, setClient] =
|
|
4591
|
-
const optionsRef =
|
|
4785
|
+
function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPayload, flagAttributes, ...options }) {
|
|
4786
|
+
const [client, setClient] = useState8(null);
|
|
4787
|
+
const optionsRef = useRef6(options);
|
|
4592
4788
|
optionsRef.current = options;
|
|
4789
|
+
const userRef = useRef6(user);
|
|
4790
|
+
userRef.current = user;
|
|
4791
|
+
const flagsOpts = options.flags === false ? null : options.flags ?? {};
|
|
4792
|
+
const payload = flagsPayload ?? flagsOpts?.payload ?? null;
|
|
4793
|
+
const payloadRef = useRef6(payload);
|
|
4794
|
+
payloadRef.current = payload;
|
|
4795
|
+
const appAttributes = useMemo5(() => ({ ...flagsOpts?.attributes ?? {}, ...flagAttributes ?? {} }), [stableStringify2(flagsOpts?.attributes ?? {}), stableStringify2(flagAttributes ?? {})]);
|
|
4796
|
+
const appAttributesRef = useRef6(appAttributes);
|
|
4797
|
+
appAttributesRef.current = appAttributes;
|
|
4593
4798
|
const { projectId, environment, apiUrl, storage } = options;
|
|
4594
|
-
|
|
4799
|
+
useEffect9(() => {
|
|
4595
4800
|
if (!projectId) return;
|
|
4596
4801
|
let c;
|
|
4597
4802
|
try {
|
|
4598
4803
|
const latest = optionsRef.current;
|
|
4599
4804
|
c = new GuidelyClient(
|
|
4600
|
-
{
|
|
4805
|
+
{
|
|
4806
|
+
...latest,
|
|
4807
|
+
projectId,
|
|
4808
|
+
environment,
|
|
4809
|
+
apiUrl,
|
|
4810
|
+
storage,
|
|
4811
|
+
// Known up front so flags evaluate for this user from the first render.
|
|
4812
|
+
user: userRef.current?.id ? userRef.current : void 0,
|
|
4813
|
+
flags: latest.flags === false ? false : { ...latest.flags ?? {}, payload: payloadRef.current ?? void 0, attributes: appAttributesRef.current },
|
|
4814
|
+
onNavigate: (url) => optionsRef.current.onNavigate?.(url),
|
|
4815
|
+
onError: (err) => optionsRef.current.onError?.(err)
|
|
4816
|
+
},
|
|
4601
4817
|
defaultBridge(latest.appVersion)
|
|
4602
4818
|
);
|
|
4603
4819
|
} catch (err) {
|
|
@@ -4606,7 +4822,7 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
|
|
|
4606
4822
|
}
|
|
4607
4823
|
setClient(c);
|
|
4608
4824
|
void c.init();
|
|
4609
|
-
const sub =
|
|
4825
|
+
const sub = AppState2.addEventListener("change", (s) => c.setAppState(s));
|
|
4610
4826
|
return () => {
|
|
4611
4827
|
sub.remove();
|
|
4612
4828
|
c.destroy();
|
|
@@ -4614,8 +4830,8 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
|
|
|
4614
4830
|
};
|
|
4615
4831
|
}, [projectId, environment, apiUrl, storage]);
|
|
4616
4832
|
const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : "";
|
|
4617
|
-
const hadUser =
|
|
4618
|
-
|
|
4833
|
+
const hadUser = useRef6(false);
|
|
4834
|
+
useEffect9(() => {
|
|
4619
4835
|
if (!client) return;
|
|
4620
4836
|
if (user && user.id) {
|
|
4621
4837
|
hadUser.current = true;
|
|
@@ -4625,10 +4841,15 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
|
|
|
4625
4841
|
client.reset();
|
|
4626
4842
|
}
|
|
4627
4843
|
}, [client, userKey]);
|
|
4628
|
-
|
|
4844
|
+
useEffect9(() => {
|
|
4845
|
+
client?.flags.setAttributes(appAttributes);
|
|
4846
|
+
}, [client, appAttributes]);
|
|
4847
|
+
const bootstrap = useBootstrap(payload, { ...user?.id ? { ...user.traits ?? {}, id: user.id } : {}, ...appAttributes });
|
|
4848
|
+
const flags = useMemo5(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
|
|
4849
|
+
return /* @__PURE__ */ jsx12(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ jsxs9(FlagsContext.Provider, { value: flags, children: [
|
|
4629
4850
|
children,
|
|
4630
|
-
client ? /* @__PURE__ */
|
|
4631
|
-
] });
|
|
4851
|
+
client ? /* @__PURE__ */ jsx12(GuidelyOverlay, { client, insets, launcherOffset }) : null
|
|
4852
|
+
] }) });
|
|
4632
4853
|
}
|
|
4633
4854
|
var NOOP_RESULTS = {
|
|
4634
4855
|
init: Promise.resolve(),
|
|
@@ -4652,20 +4873,38 @@ var NOOP_RESULTS = {
|
|
|
4652
4873
|
},
|
|
4653
4874
|
getState: { ready: false, destroyed: false }
|
|
4654
4875
|
};
|
|
4876
|
+
var noopFlags = new Proxy(createStaticFlags2(), {
|
|
4877
|
+
get(target, prop, receiver) {
|
|
4878
|
+
if (prop in target) return Reflect.get(target, prop, receiver);
|
|
4879
|
+
if (prop === "ready" || prop === "refresh" || prop === "flush") return () => Promise.resolve();
|
|
4880
|
+
if (prop === "getPayload") return () => null;
|
|
4881
|
+
if (prop === "forUser") return () => target;
|
|
4882
|
+
return typeof prop === "string" ? () => void 0 : void 0;
|
|
4883
|
+
}
|
|
4884
|
+
});
|
|
4885
|
+
var missing = (key, fallback) => ({ key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true });
|
|
4886
|
+
var NOOP_FUNCTIONS = {
|
|
4887
|
+
isFeatureEnabled: (_key, fallback = false) => fallback,
|
|
4888
|
+
getFeatureValue: (_key, fallback) => fallback,
|
|
4889
|
+
getFeatureDetails: (key, fallback) => missing(key, fallback),
|
|
4890
|
+
onFlagsChange: () => () => {
|
|
4891
|
+
}
|
|
4892
|
+
};
|
|
4655
4893
|
var noopClient = new Proxy({}, {
|
|
4656
4894
|
get(_target, prop) {
|
|
4657
4895
|
if (prop === "options") return { projectId: "" };
|
|
4896
|
+
if (prop === "flags") return noopFlags;
|
|
4658
4897
|
if (typeof prop !== "string") return void 0;
|
|
4659
|
-
return () => NOOP_RESULTS[prop];
|
|
4898
|
+
return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);
|
|
4660
4899
|
}
|
|
4661
4900
|
});
|
|
4662
4901
|
function useGuidely() {
|
|
4663
|
-
return
|
|
4902
|
+
return useContext3(GuidelyContext) ?? noopClient;
|
|
4664
4903
|
}
|
|
4665
4904
|
function useGuidelyReady() {
|
|
4666
|
-
const client =
|
|
4667
|
-
const [ready, setReady] =
|
|
4668
|
-
|
|
4905
|
+
const client = useContext3(GuidelyContext);
|
|
4906
|
+
const [ready, setReady] = useState8(() => !!client?.isClientReady());
|
|
4907
|
+
useEffect9(() => {
|
|
4669
4908
|
if (!client) return setReady(false);
|
|
4670
4909
|
setReady(client.isClientReady());
|
|
4671
4910
|
const off = client.on("ready", () => setReady(true));
|
|
@@ -4675,44 +4914,44 @@ function useGuidelyReady() {
|
|
|
4675
4914
|
return ready;
|
|
4676
4915
|
}
|
|
4677
4916
|
function useGuidelyIdentify(userId, traits, account) {
|
|
4678
|
-
const client =
|
|
4917
|
+
const client = useContext3(GuidelyContext);
|
|
4679
4918
|
const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);
|
|
4680
|
-
|
|
4919
|
+
useEffect9(() => {
|
|
4681
4920
|
if (client && userId) void client.identify(userId, traits, account);
|
|
4682
4921
|
}, [client, key]);
|
|
4683
4922
|
}
|
|
4684
4923
|
function useGuidelyEvent(event, handler) {
|
|
4685
|
-
const client =
|
|
4686
|
-
const ref =
|
|
4924
|
+
const client = useContext3(GuidelyContext);
|
|
4925
|
+
const ref = useRef6(handler);
|
|
4687
4926
|
ref.current = handler;
|
|
4688
|
-
|
|
4927
|
+
useEffect9(() => {
|
|
4689
4928
|
if (!client) return;
|
|
4690
4929
|
return client.on(event, ((...args) => ref.current(...args)));
|
|
4691
4930
|
}, [client, event]);
|
|
4692
4931
|
}
|
|
4693
4932
|
function useGuidelyScreen(name, properties) {
|
|
4694
|
-
const client =
|
|
4695
|
-
|
|
4933
|
+
const client = useContext3(GuidelyContext);
|
|
4934
|
+
useEffect9(() => {
|
|
4696
4935
|
if (client && name) client.screen(name, properties);
|
|
4697
4936
|
}, [client, name]);
|
|
4698
4937
|
}
|
|
4699
4938
|
function useGuidelyNavigation(navigationRef, options = {}) {
|
|
4700
|
-
const client =
|
|
4701
|
-
const nameRef =
|
|
4939
|
+
const client = useContext3(GuidelyContext);
|
|
4940
|
+
const nameRef = useRef6(options.screenName);
|
|
4702
4941
|
nameRef.current = options.screenName;
|
|
4703
4942
|
const report = () => {
|
|
4704
4943
|
const route = navigationRef.getCurrentRoute?.();
|
|
4705
4944
|
if (client && route?.name) client.screen(nameRef.current ? nameRef.current(route) : route.name);
|
|
4706
4945
|
};
|
|
4707
|
-
|
|
4946
|
+
useEffect9(report, [client]);
|
|
4708
4947
|
return { onReady: report, onStateChange: report };
|
|
4709
4948
|
}
|
|
4710
4949
|
function useUiSelector(select, fallback) {
|
|
4711
|
-
const client =
|
|
4712
|
-
const selectRef =
|
|
4950
|
+
const client = useContext3(GuidelyContext);
|
|
4951
|
+
const selectRef = useRef6(select);
|
|
4713
4952
|
selectRef.current = select;
|
|
4714
|
-
const [value, setValue] =
|
|
4715
|
-
|
|
4953
|
+
const [value, setValue] = useState8(() => client ? select(client) : fallback);
|
|
4954
|
+
useEffect9(() => {
|
|
4716
4955
|
if (!client) return;
|
|
4717
4956
|
let last = JSON.stringify(selectRef.current(client));
|
|
4718
4957
|
setValue(selectRef.current(client));
|
|
@@ -4746,15 +4985,21 @@ function useActiveGuide() {
|
|
|
4746
4985
|
}
|
|
4747
4986
|
export {
|
|
4748
4987
|
DEFAULT_API_URL,
|
|
4988
|
+
Feature,
|
|
4989
|
+
FeatureFlag,
|
|
4990
|
+
FeatureValue,
|
|
4991
|
+
FlagsProvider,
|
|
4749
4992
|
GuidelyClient,
|
|
4750
4993
|
GuidelyOverlay,
|
|
4751
4994
|
GuidelyProvider,
|
|
4752
4995
|
GuidelyScreenScope,
|
|
4753
4996
|
GuidelyTarget,
|
|
4997
|
+
InMemoryStickyBucketStore,
|
|
4754
4998
|
Messenger,
|
|
4755
4999
|
SDK_NAME,
|
|
4756
5000
|
SDK_VERSION,
|
|
4757
5001
|
TargetRegistry,
|
|
5002
|
+
createFlagsClient3 as createFlagsClient,
|
|
4758
5003
|
createSseParser,
|
|
4759
5004
|
guidelyScreenLayout,
|
|
4760
5005
|
htmlToText,
|
|
@@ -4764,6 +5009,12 @@ export {
|
|
|
4764
5009
|
targetIdsFor,
|
|
4765
5010
|
useActiveGuide,
|
|
4766
5011
|
useChecklistProgress,
|
|
5012
|
+
useFeatureValue,
|
|
5013
|
+
useFlag,
|
|
5014
|
+
useFlagDetails,
|
|
5015
|
+
useFlags,
|
|
5016
|
+
useFlagsClient,
|
|
5017
|
+
useFlagsReady,
|
|
4767
5018
|
useGuideState,
|
|
4768
5019
|
useGuidely,
|
|
4769
5020
|
useGuidelyEvent,
|