@marktiderman/genesis-support 0.0.0-stage → 0.2.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 +130 -2
- package/dist/block-fZjTS2sY.d.ts +39 -0
- package/dist/chunk-4VFYVQUF.js +278 -0
- package/dist/chunk-4VFYVQUF.js.map +1 -0
- package/dist/chunk-ZV4KJBUF.js +284 -0
- package/dist/chunk-ZV4KJBUF.js.map +1 -0
- package/dist/copy-CuTArBFg.d.ts +477 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/native.d.ts +207 -0
- package/dist/native.js +722 -0
- package/dist/native.js.map +1 -0
- package/dist/web.d.ts +137 -0
- package/dist/web.js +484 -0
- package/dist/web.js.map +1 -0
- package/package.json +252 -3
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
import { NativeBlock, NativeBlockRootProps } from '@marktiderman/genesis-native';
|
|
4
|
+
import { S as SUPPORT_BLOCK_ID, b as SupportPaths } from './block-fZjTS2sY.js';
|
|
5
|
+
import { u as ScreenshotContentType, M as SupportSupabase, c as ContextCollector, A as SupportCopyOverrides, x as SupportClient, z as SupportCopy, o as FeedbackType, j as FeedbackDelivery, H as SupportIdea, K as SupportReport } from './copy-CuTArBFg.js';
|
|
6
|
+
import { InboxPageProps, ThreadPageProps } from '@marktiderman/genesis-ui-native/layouts';
|
|
7
|
+
import '@marktiderman/genesis-features/blocks';
|
|
8
|
+
import '@supabase/supabase-js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The native modules the support block can use, as the shapes it needs. The
|
|
12
|
+
* app passes the real ones (all optional peers), so the block runs without
|
|
13
|
+
* any of them and the tests pass fakes:
|
|
14
|
+
*
|
|
15
|
+
* import * as ViewShot from "react-native-view-shot"; // screenshots, mark-up
|
|
16
|
+
* import { Accelerometer } from "expo-sensors"; // shake to report
|
|
17
|
+
*/
|
|
18
|
+
interface CaptureOptions {
|
|
19
|
+
format?: "png" | "jpg" | "webp";
|
|
20
|
+
quality?: number;
|
|
21
|
+
result?: "tmpfile" | "base64" | "data-uri";
|
|
22
|
+
width?: number;
|
|
23
|
+
height?: number;
|
|
24
|
+
}
|
|
25
|
+
/** `react-native-view-shot`: `captureScreen` (the whole screen) and `captureRef` (one view, for flattening mark-up). */
|
|
26
|
+
interface ViewShotLike {
|
|
27
|
+
captureScreen(options?: CaptureOptions): Promise<string>;
|
|
28
|
+
captureRef(view: unknown, options?: CaptureOptions): Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
interface AccelerometerMeasurement {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
z: number;
|
|
34
|
+
}
|
|
35
|
+
/** `expo-sensors`' `Accelerometer` (values in g). */
|
|
36
|
+
interface AccelerometerLike {
|
|
37
|
+
addListener(listener: (measurement: AccelerometerMeasurement) => void): {
|
|
38
|
+
remove(): void;
|
|
39
|
+
};
|
|
40
|
+
setUpdateInterval?(intervalMs: number): void;
|
|
41
|
+
}
|
|
42
|
+
interface ShakeOptions {
|
|
43
|
+
/** Acceleration, in g, that counts as a jolt. Default 2.3. */
|
|
44
|
+
threshold?: number;
|
|
45
|
+
/** Jolts needed within `windowMs`. Default 2. */
|
|
46
|
+
jolts?: number;
|
|
47
|
+
windowMs?: number;
|
|
48
|
+
/** Quiet time after a shake. Default 3000 ms. */
|
|
49
|
+
cooldownMs?: number;
|
|
50
|
+
intervalMs?: number;
|
|
51
|
+
now?: () => number;
|
|
52
|
+
}
|
|
53
|
+
/** Calls `onShake` when the phone is shaken: a few strong jolts close together, then a cooldown. Returns the stop. */
|
|
54
|
+
declare function listenForShake(accelerometer: AccelerometerLike, onShake: () => void, options?: ShakeOptions): () => void;
|
|
55
|
+
|
|
56
|
+
/** A screenshot waiting for the feedback screen. `uri` is a data URI. */
|
|
57
|
+
interface PendingScreenshot {
|
|
58
|
+
uri: string;
|
|
59
|
+
contentType: ScreenshotContentType;
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
}
|
|
63
|
+
/** What the feedback screen opens with: where the person was, and maybe a screenshot of it. */
|
|
64
|
+
interface PendingFeedback {
|
|
65
|
+
route: string | null;
|
|
66
|
+
screenshot: PendingScreenshot | null;
|
|
67
|
+
/** Extra context to send with the report (the devtools overlay's debug report). */
|
|
68
|
+
extra?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
interface SupportOptions {
|
|
71
|
+
/** The app's Supabase client (migration 0006 applied). */
|
|
72
|
+
supabase: SupportSupabase;
|
|
73
|
+
/** Opens a path (expo-router's `router.push`). */
|
|
74
|
+
navigate: (href: string) => void;
|
|
75
|
+
/** `Platform.OS`. Default from React Native. */
|
|
76
|
+
platform?: string;
|
|
77
|
+
/** The version signature sent with each report (`useVersionSignature().short`, or a function that reads it). */
|
|
78
|
+
appVersion?: string | (() => string | undefined);
|
|
79
|
+
/** The current route, as a hook (expo-router's `usePathname`). The root calls it. */
|
|
80
|
+
useCurrentRoute?: () => string;
|
|
81
|
+
/** More context (device facts, the update id). Merged over the defaults. */
|
|
82
|
+
collector?: ContextCollector;
|
|
83
|
+
/** `react-native-view-shot`. Without it there are no screenshots. */
|
|
84
|
+
viewShot?: ViewShotLike;
|
|
85
|
+
/** `expo-sensors`' `Accelerometer`. Without it (or with `shake: false`) shaking does nothing. */
|
|
86
|
+
accelerometer?: AccelerometerLike;
|
|
87
|
+
shake?: false | ShakeOptions;
|
|
88
|
+
/** Default `/support`. The route files live under it. */
|
|
89
|
+
basePath?: string;
|
|
90
|
+
copy?: SupportCopyOverrides;
|
|
91
|
+
/** The Settings rows' section (default `support`), or `false` for none. */
|
|
92
|
+
settings?: false | {
|
|
93
|
+
section?: string;
|
|
94
|
+
};
|
|
95
|
+
/** `false` leaves the idea board out. */
|
|
96
|
+
ideas?: boolean;
|
|
97
|
+
/** Every caught error, for reporting. None of them reaches the person as a crash. */
|
|
98
|
+
onError?: (error: Error, where: "capture" | "shake") => void;
|
|
99
|
+
}
|
|
100
|
+
interface SupportBlock extends NativeBlock<typeof SUPPORT_BLOCK_ID> {
|
|
101
|
+
readonly options: SupportOptions;
|
|
102
|
+
readonly client: SupportClient;
|
|
103
|
+
readonly copy: SupportCopy;
|
|
104
|
+
readonly paths: SupportPaths;
|
|
105
|
+
readonly collector: ContextCollector;
|
|
106
|
+
/** Capture the screen (if it can and `screenshot` isn't false), then open the feedback screen. */
|
|
107
|
+
openFeedback(options?: {
|
|
108
|
+
screenshot?: boolean;
|
|
109
|
+
extra?: Record<string, unknown>;
|
|
110
|
+
delayMs?: number;
|
|
111
|
+
}): Promise<void>;
|
|
112
|
+
/** Hand the feedback screen what it opens with (`takePending` empties it). */
|
|
113
|
+
takePending(): PendingFeedback | null;
|
|
114
|
+
peekPending(): PendingFeedback | null;
|
|
115
|
+
subscribePending(listener: () => void): () => void;
|
|
116
|
+
/** The route the person is on, as the root last saw it. */
|
|
117
|
+
currentRoute(): string | null;
|
|
118
|
+
readonly Root: ComponentType<NativeBlockRootProps>;
|
|
119
|
+
}
|
|
120
|
+
/** The support block, from `GenesisNativeApp blocks` (or `SupportProvider`). Throws outside one. */
|
|
121
|
+
declare function useSupport(): SupportBlock;
|
|
122
|
+
/** The same block, or `null` when support is off (for a button that hides itself). */
|
|
123
|
+
declare function useOptionalSupport(): SupportBlock | null;
|
|
124
|
+
declare function SupportProvider({ block, children }: {
|
|
125
|
+
block: SupportBlock;
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
}): react.JSX.Element;
|
|
128
|
+
declare const SUPPORT_QUERY_ROOT = "genesis-support";
|
|
129
|
+
/** How many reports have a staff reply the person hasn't read. */
|
|
130
|
+
declare function useUnreadReplies(): number;
|
|
131
|
+
declare function createSupport(options: SupportOptions): SupportBlock;
|
|
132
|
+
/** The pending feedback, re-rendering when it changes. */
|
|
133
|
+
declare function usePendingFeedback(): PendingFeedback | null;
|
|
134
|
+
|
|
135
|
+
interface FeedbackFormProps {
|
|
136
|
+
defaultType?: FeedbackType;
|
|
137
|
+
/** After a report is saved (go back, say thanks). The form has already shown its own confirmation, unless `sentAlert` is false. */
|
|
138
|
+
onSent?: (delivery: FeedbackDelivery) => void;
|
|
139
|
+
/** Confirm a sent report with an alert. Default true; false when `onSent` says so itself (a toast). */
|
|
140
|
+
sentAlert?: boolean;
|
|
141
|
+
testID?: string;
|
|
142
|
+
}
|
|
143
|
+
declare function FeedbackForm({ defaultType, onSent, sentAlert, testID }: FeedbackFormProps): react.JSX.Element;
|
|
144
|
+
|
|
145
|
+
type Point = {
|
|
146
|
+
x: number;
|
|
147
|
+
y: number;
|
|
148
|
+
};
|
|
149
|
+
/** The dots along a stroke, close enough to read as a line. */
|
|
150
|
+
declare function strokeDots(points: readonly Point[], spacing?: number): Point[];
|
|
151
|
+
declare function boxOf(from: Point, to: Point): {
|
|
152
|
+
left: number;
|
|
153
|
+
top: number;
|
|
154
|
+
width: number;
|
|
155
|
+
height: number;
|
|
156
|
+
};
|
|
157
|
+
interface ScreenshotAnnotatorProps {
|
|
158
|
+
screenshot: PendingScreenshot;
|
|
159
|
+
viewShot: ViewShotLike;
|
|
160
|
+
copy: SupportCopy["annotate"];
|
|
161
|
+
/** `marked`: the image now carries marks (pen or hidden parts). */
|
|
162
|
+
onDone: (screenshot: PendingScreenshot, marked: boolean) => void;
|
|
163
|
+
/** `hadMarks`: the person drew something and left without saving it. */
|
|
164
|
+
onCancel: (hadMarks: boolean) => void;
|
|
165
|
+
testID?: string;
|
|
166
|
+
}
|
|
167
|
+
declare function ScreenshotAnnotator({ screenshot, viewShot, copy, onDone, onCancel, testID }: ScreenshotAnnotatorProps): react.JSX.Element;
|
|
168
|
+
|
|
169
|
+
type InboxProps = Pick<InboxPageProps, "items" | "refreshing" | "onRefresh" | "emptyTitle" | "emptyBody" | "emptyActionLabel" | "onEmptyAction" | "sectionTitle" | "header" | "testID" | "loading" | "error" | "errorTitle" | "errorBody"> & {
|
|
170
|
+
headerTitle: string;
|
|
171
|
+
};
|
|
172
|
+
/** Help: the person's reports, newest first, with unread staff replies, and links to the idea board. */
|
|
173
|
+
declare function useSupportInbox(): InboxProps;
|
|
174
|
+
type ThreadProps = Pick<ThreadPageProps, "messages" | "composer" | "closedNotice" | "readAt" | "readLabel" | "emptyTitle" | "emptyBody" | "labels" | "testID"> & {
|
|
175
|
+
headerTitle: string;
|
|
176
|
+
report: SupportReport | null;
|
|
177
|
+
loading: boolean;
|
|
178
|
+
/** Read, and there is no such report (deleted, or not the person's): leave the screen. */
|
|
179
|
+
notFound: boolean;
|
|
180
|
+
};
|
|
181
|
+
/** One report as a conversation: the report itself, then the replies. Marks it read as it opens and as replies arrive. */
|
|
182
|
+
declare function useSupportThread(reportId: string, options?: {
|
|
183
|
+
pollMs?: number;
|
|
184
|
+
}): ThreadProps;
|
|
185
|
+
/** A vote toggle for an idea row. */
|
|
186
|
+
declare function VoteButton({ idea, onToggle, testID }: {
|
|
187
|
+
idea: SupportIdea;
|
|
188
|
+
onToggle: () => void;
|
|
189
|
+
testID?: string;
|
|
190
|
+
}): react.JSX.Element;
|
|
191
|
+
type BoardProps = Pick<InboxPageProps, "items" | "refreshing" | "onRefresh" | "emptyTitle" | "emptyBody" | "header" | "testID" | "loading" | "error" | "errorTitle" | "errorBody"> & {
|
|
192
|
+
headerTitle: string;
|
|
193
|
+
};
|
|
194
|
+
/** The idea board: most votes first, a vote button on each, and a field to suggest one. */
|
|
195
|
+
declare function useIdeaBoard(): BoardProps;
|
|
196
|
+
/** "You asked, we shipped": the shipped ideas the person suggested or voted for, with the FeatureId each shipped as. */
|
|
197
|
+
declare function useShippedIdeas(): Omit<BoardProps, "header">;
|
|
198
|
+
/**
|
|
199
|
+
* "Send feedback", for a devtools panel's slot or any screen: captures the
|
|
200
|
+
* screen, then opens the feedback screen. Renders nothing when support is off.
|
|
201
|
+
*/
|
|
202
|
+
declare function SendFeedbackButton({ testID, screenshot }: {
|
|
203
|
+
testID?: string;
|
|
204
|
+
screenshot?: boolean;
|
|
205
|
+
}): react.JSX.Element | null;
|
|
206
|
+
|
|
207
|
+
export { type AccelerometerLike, type AccelerometerMeasurement, type CaptureOptions, FeedbackForm, type FeedbackFormProps, type PendingFeedback, type PendingScreenshot, SUPPORT_QUERY_ROOT, ScreenshotAnnotator, type ScreenshotAnnotatorProps, SendFeedbackButton, type ShakeOptions, type SupportBlock, type SupportOptions, SupportProvider, type ViewShotLike, VoteButton, boxOf, createSupport, listenForShake, strokeDots, useIdeaBoard, useOptionalSupport, usePendingFeedback, useShippedIdeas, useSupport, useSupportInbox, useSupportThread, useUnreadReplies };
|