@webority/mobile-support 0.0.12 → 0.1.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.
Files changed (47) hide show
  1. package/lib/commonjs/SupportProvider.js +1 -335
  2. package/lib/commonjs/client.js +1 -164
  3. package/lib/commonjs/clipboard.js +1 -10
  4. package/lib/commonjs/components/Attachments.js +1 -246
  5. package/lib/commonjs/components/NewSupportTicket.js +1 -210
  6. package/lib/commonjs/components/PhotoTray.js +1 -141
  7. package/lib/commonjs/components/SupportContact.js +1 -134
  8. package/lib/commonjs/components/SupportTicketDetail.js +1 -348
  9. package/lib/commonjs/components/SupportTicketList.js +1 -279
  10. package/lib/commonjs/device.js +1 -29
  11. package/lib/commonjs/files.js +1 -37
  12. package/lib/commonjs/format.js +1 -29
  13. package/lib/commonjs/index.js +1 -97
  14. package/lib/commonjs/photos.js +1 -67
  15. package/lib/commonjs/report/ReportIssueButton.js +1 -57
  16. package/lib/commonjs/report/captureAndReport.js +1 -34
  17. package/lib/commonjs/report/index.js +1 -20
  18. package/lib/commonjs/share/index.js +1 -44
  19. package/lib/commonjs/types.js +1 -2
  20. package/lib/commonjs/validation.js +1 -45
  21. package/lib/module/SupportProvider.js +1 -323
  22. package/lib/module/client.js +1 -156
  23. package/lib/module/clipboard.js +1 -5
  24. package/lib/module/components/Attachments.js +1 -240
  25. package/lib/module/components/NewSupportTicket.js +1 -205
  26. package/lib/module/components/PhotoTray.js +1 -136
  27. package/lib/module/components/SupportContact.js +1 -129
  28. package/lib/module/components/SupportTicketDetail.js +1 -343
  29. package/lib/module/components/SupportTicketList.js +1 -273
  30. package/lib/module/device.js +1 -24
  31. package/lib/module/files.js +1 -31
  32. package/lib/module/format.js +1 -23
  33. package/lib/module/index.js +1 -10
  34. package/lib/module/photos.js +1 -61
  35. package/lib/module/report/ReportIssueButton.js +1 -52
  36. package/lib/module/report/captureAndReport.js +1 -29
  37. package/lib/module/report/index.js +1 -12
  38. package/lib/module/share/index.js +1 -37
  39. package/lib/module/types.js +0 -2
  40. package/lib/module/validation.js +1 -38
  41. package/lib/typescript/commonjs/SupportProvider.d.ts +4 -0
  42. package/lib/typescript/commonjs/client.d.ts +0 -1
  43. package/lib/typescript/commonjs/format.d.ts +6 -4
  44. package/lib/typescript/module/SupportProvider.d.ts +4 -0
  45. package/lib/typescript/module/client.d.ts +0 -1
  46. package/lib/typescript/module/format.d.ts +6 -4
  47. package/package.json +8 -6
@@ -1,246 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ImageViewer = exports.AttachmentList = void 0;
7
- var _mobileUi = require("@webority/mobile-ui");
8
- var _react = require("react");
9
- var _reactNative = require("react-native");
10
- var _reactNativeGestureHandler = require("react-native-gesture-handler");
11
- var _format = require("../format.js");
12
- var _SupportProvider = require("../SupportProvider.js");
13
- var _jsxRuntime = require("react/jsx-runtime");
14
- const AttachmentThumb = ({
15
- attachment,
16
- onOpen
17
- }) => {
18
- const theme = (0, _mobileUi.useTheme)();
19
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
20
- const load = (0, _SupportProvider.useAttachmentLoader)();
21
- const [state, setState] = (0, _react.useState)({
22
- status: 'loading'
23
- });
24
- (0, _react.useEffect)(() => {
25
- let cancelled = false;
26
- load(attachment).then(uri => !cancelled && setState({
27
- status: 'ready',
28
- uri
29
- }), () => !cancelled && setState({
30
- status: 'error'
31
- }));
32
- return () => {
33
- cancelled = true;
34
- };
35
- }, [load, attachment]);
36
- if (state.status === 'loading') {
37
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
38
- style: styles.thumb,
39
- testID: `support-attachment-${attachment.id}`,
40
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Spinner, {
41
- size: "sm",
42
- accessibilityLabel: `Loading ${attachment.fileName}`
43
- })
44
- });
45
- }
46
- if (state.status === 'error') {
47
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
48
- style: styles.thumb,
49
- accessible: true,
50
- accessibilityLabel: `${attachment.fileName} could not be loaded`,
51
- testID: `support-attachment-${attachment.id}`,
52
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
53
- variant: "caption",
54
- color: "tertiary",
55
- align: "center",
56
- children: "Could not load"
57
- })
58
- });
59
- }
60
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
61
- onPress: () => onOpen(state.uri, attachment.fileName),
62
- accessibilityRole: "imagebutton",
63
- accessibilityLabel: `Open ${attachment.fileName}`,
64
- testID: `support-attachment-${attachment.id}`,
65
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
66
- source: {
67
- uri: state.uri
68
- },
69
- style: styles.thumb,
70
- resizeMode: "cover"
71
- })
72
- });
73
- };
74
-
75
- /**
76
- * A file that is not an image. Tappable when the app gave SupportProvider an
77
- * `openFile`: it is downloaded through the signed-in client, then handed to the OS.
78
- */
79
- const FileRow = ({
80
- attachment
81
- }) => {
82
- const theme = (0, _mobileUi.useTheme)();
83
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
84
- const open = (0, _SupportProvider.useAttachmentOpener)();
85
- const [opening, setOpening] = (0, _react.useState)(false);
86
- const label = `${attachment.fileName}, ${(0, _format.formatBytes)(attachment.sizeBytes)}`;
87
- if (!open) {
88
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
89
- variant: "caption",
90
- color: "secondary",
91
- testID: `support-file-${attachment.id}`,
92
- children: label
93
- });
94
- }
95
- const onPress = async () => {
96
- setOpening(true);
97
- try {
98
- await open(attachment);
99
- } catch {
100
- _mobileUi.toast.error(`Could not open ${attachment.fileName}. Try again.`);
101
- } finally {
102
- setOpening(false);
103
- }
104
- };
105
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
106
- onPress: () => void onPress(),
107
- disabled: opening,
108
- accessibilityRole: "button",
109
- accessibilityLabel: `Open ${attachment.fileName}`,
110
- accessibilityState: {
111
- busy: opening,
112
- disabled: opening
113
- },
114
- style: styles.fileRow,
115
- testID: `support-file-${attachment.id}`,
116
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.AppIcon, {
117
- name: "document",
118
- size: 16,
119
- color: theme.colors.text.link
120
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
121
- variant: "caption",
122
- color: "link",
123
- children: label
124
- }), opening && /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Spinner, {
125
- size: "sm",
126
- accessibilityLabel: `Opening ${attachment.fileName}`
127
- })]
128
- });
129
- };
130
-
131
- /** A message's files: images as tappable thumbnails, anything else as a file row. */
132
- const AttachmentList = ({
133
- attachments,
134
- onOpenImage
135
- }) => {
136
- const theme = (0, _mobileUi.useTheme)();
137
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
138
- if (attachments.length === 0) {
139
- return null;
140
- }
141
- const images = attachments.filter(a => (0, _format.isImage)(a.contentType));
142
- const others = attachments.filter(a => !(0, _format.isImage)(a.contentType));
143
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
144
- style: styles.list,
145
- children: [images.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
146
- style: styles.thumbRow,
147
- children: images.map(a => /*#__PURE__*/(0, _jsxRuntime.jsx)(AttachmentThumb, {
148
- attachment: a,
149
- onOpen: onOpenImage
150
- }, a.id))
151
- }), others.map(a => /*#__PURE__*/(0, _jsxRuntime.jsx)(FileRow, {
152
- attachment: a
153
- }, a.id))]
154
- });
155
- };
156
-
157
- /** Full-screen view of one image with pinch to zoom. */
158
- exports.AttachmentList = AttachmentList;
159
- const ImageViewer = ({
160
- image,
161
- onClose
162
- }) => {
163
- const theme = (0, _mobileUi.useTheme)();
164
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
165
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
166
- visible: image !== null,
167
- onRequestClose: onClose,
168
- animationType: "fade",
169
- transparent: false,
170
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeGestureHandler.GestureHandlerRootView, {
171
- style: styles.viewer,
172
- testID: "support-image-viewer",
173
- children: [image && /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.ZoomableImage, {
174
- accessibilityLabel: image.name,
175
- style: styles.viewerImageWrap,
176
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
177
- source: {
178
- uri: image.uri
179
- },
180
- style: styles.viewerImage,
181
- resizeMode: "contain"
182
- })
183
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
184
- style: styles.viewerClose,
185
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.IconButton, {
186
- icon: ({
187
- color,
188
- size
189
- }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.AppIcon, {
190
- name: "close-circle",
191
- color: color,
192
- size: size
193
- }),
194
- accessibilityLabel: "Close image",
195
- onPress: onClose,
196
- variant: "glass"
197
- })
198
- })]
199
- })
200
- });
201
- };
202
- exports.ImageViewer = ImageViewer;
203
- const THUMB = 84;
204
- const buildStyles = theme => _reactNative.StyleSheet.create({
205
- list: {
206
- gap: theme.spacing.xs,
207
- marginTop: theme.spacing.sm
208
- },
209
- fileRow: {
210
- flexDirection: 'row',
211
- alignItems: 'center',
212
- gap: theme.spacing.xxs,
213
- alignSelf: 'flex-start'
214
- },
215
- thumbRow: {
216
- flexDirection: 'row',
217
- flexWrap: 'wrap',
218
- gap: theme.spacing.xs
219
- },
220
- thumb: {
221
- width: THUMB,
222
- height: THUMB,
223
- borderRadius: theme.radius.sm,
224
- backgroundColor: theme.colors.background.secondary,
225
- alignItems: 'center',
226
- justifyContent: 'center',
227
- overflow: 'hidden'
228
- },
229
- viewer: {
230
- flex: 1,
231
- backgroundColor: theme.colors.background.inverse
232
- },
233
- viewerImageWrap: {
234
- flex: 1
235
- },
236
- viewerImage: {
237
- flex: 1,
238
- width: '100%'
239
- },
240
- viewerClose: {
241
- position: 'absolute',
242
- top: theme.spacing['2xl'],
243
- right: theme.spacing.md
244
- }
245
- });
246
- //# sourceMappingURL=Attachments.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ImageViewer=exports.AttachmentList=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_reactNativeGestureHandler=require("react-native-gesture-handler"),_format=require("../format.js"),_SupportProvider=require("../SupportProvider.js"),_jsxRuntime=require("react/jsx-runtime");const AttachmentThumb=({attachment:e,onOpen:t})=>{const i=(0,_mobileUi.useTheme)(),s=(0,_react.useMemo)(()=>buildStyles(i),[i]),a=(0,_SupportProvider.useAttachmentLoader)(),[r,n]=(0,_react.useState)({status:"loading"});return(0,_react.useEffect)(()=>{let t=!1;return a(e).then(e=>!t&&n({status:"ready",uri:e}),()=>!t&&n({status:"error"})),()=>{t=!0}},[a,e]),"loading"===r.status?(0,_jsxRuntime.jsx)(_reactNative.View,{style:s.thumb,testID:`support-attachment-${e.id}`,children:(0,_jsxRuntime.jsx)(_mobileUi.Spinner,{size:"sm",accessibilityLabel:`Loading ${e.fileName}`})}):"error"===r.status?(0,_jsxRuntime.jsx)(_reactNative.View,{style:s.thumb,accessible:!0,accessibilityLabel:`${e.fileName} could not be loaded`,testID:`support-attachment-${e.id}`,children:(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"tertiary",align:"center",children:"Could not load"})}):(0,_jsxRuntime.jsx)(_reactNative.Pressable,{onPress:()=>t(r.uri,e.fileName),accessibilityRole:"imagebutton",accessibilityLabel:`Open ${e.fileName}`,testID:`support-attachment-${e.id}`,children:(0,_jsxRuntime.jsx)(_reactNative.Image,{source:{uri:r.uri},style:s.thumb,resizeMode:"cover"})})},FileRow=({attachment:e})=>{const t=(0,_mobileUi.useTheme)(),i=(0,_react.useMemo)(()=>buildStyles(t),[t]),s=(0,_SupportProvider.useAttachmentOpener)(),[a,r]=(0,_react.useState)(!1),n=`${e.fileName}, ${(0,_format.formatBytes)(e.sizeBytes)}`;if(!s)return(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"secondary",testID:`support-file-${e.id}`,children:n});return(0,_jsxRuntime.jsxs)(_reactNative.Pressable,{onPress:()=>{(async()=>{r(!0);try{await s(e)}catch{_mobileUi.toast.error(`Could not open ${e.fileName}. Try again.`)}finally{r(!1)}})()},disabled:a,accessibilityRole:"button",accessibilityLabel:`Open ${e.fileName}`,accessibilityState:{busy:a,disabled:a},style:i.fileRow,testID:`support-file-${e.id}`,children:[(0,_jsxRuntime.jsx)(_mobileUi.AppIcon,{name:"document",size:16,color:t.colors.text.link}),(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"link",children:n}),a&&(0,_jsxRuntime.jsx)(_mobileUi.Spinner,{size:"sm",accessibilityLabel:`Opening ${e.fileName}`})]})},AttachmentList=({attachments:e,onOpenImage:t})=>{const i=(0,_mobileUi.useTheme)(),s=(0,_react.useMemo)(()=>buildStyles(i),[i]);if(0===e.length)return null;const a=e.filter(e=>(0,_format.isImage)(e.contentType)),r=e.filter(e=>!(0,_format.isImage)(e.contentType));return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:s.list,children:[a.length>0&&(0,_jsxRuntime.jsx)(_reactNative.View,{style:s.thumbRow,children:a.map(e=>(0,_jsxRuntime.jsx)(AttachmentThumb,{attachment:e,onOpen:t},e.id))}),r.map(e=>(0,_jsxRuntime.jsx)(FileRow,{attachment:e},e.id))]})};exports.AttachmentList=AttachmentList;const ImageViewer=({image:e,onClose:t})=>{const i=(0,_mobileUi.useTheme)(),s=(0,_react.useMemo)(()=>buildStyles(i),[i]);return(0,_jsxRuntime.jsx)(_reactNative.Modal,{visible:null!==e,onRequestClose:t,animationType:"fade",transparent:!1,children:(0,_jsxRuntime.jsxs)(_reactNativeGestureHandler.GestureHandlerRootView,{style:s.viewer,testID:"support-image-viewer",children:[e&&(0,_jsxRuntime.jsx)(_mobileUi.ZoomableImage,{accessibilityLabel:e.name,style:s.viewerImageWrap,children:(0,_jsxRuntime.jsx)(_reactNative.Image,{source:{uri:e.uri},style:s.viewerImage,resizeMode:"contain"})}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:s.viewerClose,children:(0,_jsxRuntime.jsx)(_mobileUi.IconButton,{icon:({color:e,size:t})=>(0,_jsxRuntime.jsx)(_mobileUi.AppIcon,{name:"close-circle",color:e,size:t}),accessibilityLabel:"Close image",onPress:t,variant:"glass"})}),(0,_jsxRuntime.jsx)(_mobileUi.ToastHost,{})]})})};exports.ImageViewer=ImageViewer;const THUMB=84,buildStyles=e=>_reactNative.StyleSheet.create({list:{gap:e.spacing.xs,marginTop:e.spacing.sm},fileRow:{flexDirection:"row",alignItems:"center",gap:e.spacing.xxs,alignSelf:"flex-start"},thumbRow:{flexDirection:"row",flexWrap:"wrap",gap:e.spacing.xs},thumb:{width:84,height:84,borderRadius:e.radius.sm,backgroundColor:e.colors.background.secondary,alignItems:"center",justifyContent:"center",overflow:"hidden"},viewer:{flex:1,backgroundColor:e.colors.background.inverse},viewerImageWrap:{flex:1},viewerImage:{flex:1,width:"100%"},viewerClose:{position:"absolute",top:e.spacing["2xl"],right:e.spacing.md}});
@@ -1,210 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.NewSupportTicket = void 0;
7
- var _mobileUi = require("@webority/mobile-ui");
8
- var _react = require("react");
9
- var _reactNative = require("react-native");
10
- var _client = require("../client.js");
11
- var _SupportProvider = require("../SupportProvider.js");
12
- var _validation = require("../validation.js");
13
- var _PhotoTray = require("./PhotoTray.js");
14
- var _jsxRuntime = require("react/jsx-runtime");
15
- const TicketForm = ({
16
- config,
17
- styles,
18
- onCreated,
19
- onCancel,
20
- initialDraft
21
- }) => {
22
- const createTicket = (0, _SupportProvider.useCreateSupportTicket)();
23
- const [photos, setPhotos] = (0, _react.useState)([]);
24
- const [screenshots, setScreenshots] = (0, _react.useState)(initialDraft?.screenshots ?? []);
25
- const [filesError, setFilesError] = (0, _react.useState)(null);
26
- const [submitError, setSubmitError] = (0, _react.useState)(null);
27
- const form = (0, _mobileUi.useForm)({
28
- initialValues: {
29
- category: initialDraft?.category ?? '',
30
- subject: initialDraft?.subject ?? '',
31
- body: initialDraft?.body ?? ''
32
- },
33
- validate: {
34
- category: v => (0, _validation.validateCategory)(v, config),
35
- subject: v => (0, _validation.validateSubject)(v, config),
36
- body: v => (0, _validation.validateBody)(v, config)
37
- },
38
- onSubmit: async values => {
39
- const fileProblem = (0, _validation.validateFiles)([...screenshots, ...photos], config);
40
- setFilesError(fileProblem ?? null);
41
- if (fileProblem) {
42
- return;
43
- }
44
- setSubmitError(null);
45
- try {
46
- const created = await createTicket({
47
- category: values.category,
48
- subject: values.subject.trim(),
49
- body: values.body.trim(),
50
- pageUrl: initialDraft?.pageUrl,
51
- userAgent: initialDraft?.userAgent,
52
- appVersion: initialDraft?.appVersion,
53
- files: photos,
54
- screenshots
55
- });
56
- onCreated(created);
57
- } catch (e) {
58
- setSubmitError((0, _client.toSupportApiError)(e).message);
59
- }
60
- }
61
- });
62
- const category = form.getFieldProps('category');
63
- const subject = form.getFieldProps('subject');
64
- const body = form.getFieldProps('body');
65
- const attached = screenshots.length + photos.length;
66
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
67
- contentContainerStyle: styles.content,
68
- keyboardShouldPersistTaps: "handled",
69
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
70
- variant: "bodySmall",
71
- color: "secondary",
72
- children: "Tell us what you need. We reply by email and in this ticket."
73
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Select, {
74
- label: "Category",
75
- placeholder: "Choose a category",
76
- required: true,
77
- options: config.categories.map(c => ({
78
- label: c,
79
- value: c
80
- })),
81
- value: category.value || undefined,
82
- onChange: v => category.setValue(String(v), true),
83
- error: category.error,
84
- testID: "support-new-category"
85
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Input, {
86
- label: "Subject",
87
- placeholder: "A short summary",
88
- required: true,
89
- maxLength: config.subjectMaxLength,
90
- value: subject.value,
91
- onChangeText: subject.onChangeText,
92
- onBlur: subject.onBlur,
93
- error: subject.error,
94
- testID: "support-new-subject"
95
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Input, {
96
- label: "Message",
97
- placeholder: "What happened, and what did you expect?",
98
- required: true,
99
- multiline: true,
100
- maxLength: config.bodyMaxLength,
101
- value: body.value,
102
- onChangeText: body.onChangeText,
103
- onBlur: body.onBlur,
104
- error: body.error,
105
- testID: "support-new-body"
106
- }), screenshots.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_PhotoTray.PhotoTray, {
107
- label: "Screenshot",
108
- photos: screenshots,
109
- onChange: setScreenshots,
110
- remaining: 0,
111
- allowAdd: false,
112
- maxBytes: config.maxAttachmentBytes,
113
- disabled: form.isSubmitting
114
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_PhotoTray.PhotoTray, {
115
- label: "Photos",
116
- photos: photos,
117
- onChange: setPhotos,
118
- remaining: config.maxAttachmentsPerMessage - attached,
119
- maxBytes: config.maxAttachmentBytes,
120
- disabled: form.isSubmitting
121
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
122
- variant: "caption",
123
- color: "tertiary",
124
- children: `Attach up to ${config.maxAttachmentsPerMessage} files.`
125
- }), (filesError || submitError) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
126
- variant: "bodySmall",
127
- color: "error",
128
- accessibilityLiveRegion: "polite",
129
- children: filesError ?? submitError
130
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
131
- style: styles.actions,
132
- children: [onCancel && /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Button, {
133
- title: "Cancel",
134
- variant: "ghost",
135
- tone: "neutral",
136
- onPress: onCancel,
137
- disabled: form.isSubmitting
138
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Button, {
139
- title: "Send ticket",
140
- loading: form.isSubmitting,
141
- disabled: form.isSubmitting,
142
- onPress: form.submit,
143
- testID: "support-new-submit"
144
- })]
145
- })]
146
- });
147
- };
148
- const NewSupportTicket = props => {
149
- const theme = (0, _mobileUi.useTheme)();
150
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
151
- const {
152
- config,
153
- loading,
154
- error,
155
- reload
156
- } = (0, _SupportProvider.useSupportConfig)();
157
- const testID = props.testID ?? 'support-new-ticket';
158
- if (!config) {
159
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
160
- style: styles.center,
161
- testID: testID,
162
- children: loading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Spinner, {
163
- accessibilityLabel: "Loading"
164
- }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
165
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
166
- variant: "body",
167
- color: "error",
168
- align: "center",
169
- children: error?.message ?? 'Support is not available right now.'
170
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Button, {
171
- title: "Try again",
172
- variant: "ghost",
173
- onPress: () => void reload()
174
- })]
175
- })
176
- });
177
- }
178
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
179
- style: styles.root,
180
- testID: testID,
181
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(TicketForm, {
182
- ...props,
183
- config: config,
184
- styles: styles
185
- })
186
- });
187
- };
188
- exports.NewSupportTicket = NewSupportTicket;
189
- const buildStyles = theme => _reactNative.StyleSheet.create({
190
- root: {
191
- flex: 1
192
- },
193
- content: {
194
- padding: theme.spacing.md,
195
- gap: theme.spacing.md
196
- },
197
- center: {
198
- flex: 1,
199
- alignItems: 'center',
200
- justifyContent: 'center',
201
- padding: theme.spacing.xl,
202
- gap: theme.spacing.sm
203
- },
204
- actions: {
205
- flexDirection: 'row',
206
- justifyContent: 'flex-end',
207
- gap: theme.spacing.sm
208
- }
209
- });
210
- //# sourceMappingURL=NewSupportTicket.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.NewSupportTicket=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_client=require("../client.js"),_SupportProvider=require("../SupportProvider.js"),_validation=require("../validation.js"),_PhotoTray=require("./PhotoTray.js"),_jsxRuntime=require("react/jsx-runtime");const TicketForm=({config:e,styles:t,onCreated:i,onCancel:r,initialDraft:a})=>{const o=(0,_SupportProvider.useCreateSupportTicket)(),[n,s]=(0,_react.useState)([]),[l,u]=(0,_react.useState)(a?.screenshots??[]),[c,d]=(0,_react.useState)(null),[m,p]=(0,_react.useState)(null),g=(0,_mobileUi.useForm)({initialValues:{category:a?.category??"",subject:a?.subject??"",body:a?.body??""},validate:{category:t=>(0,_validation.validateCategory)(t,e),subject:t=>(0,_validation.validateSubject)(t,e),body:t=>(0,_validation.validateBody)(t,e)},onSubmit:async t=>{const r=(0,_validation.validateFiles)([...l,...n],e);if(d(r??null),!r){p(null);try{const e=await o({category:t.category,subject:t.subject.trim(),body:t.body.trim(),pageUrl:a?.pageUrl,userAgent:a?.userAgent,appVersion:a?.appVersion,files:n,screenshots:l});i(e)}catch(e){p((0,_client.toSupportApiError)(e).message)}}}}),x=g.getFieldProps("category"),b=g.getFieldProps("subject"),_=g.getFieldProps("body"),h=l.length+n.length;return(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{contentContainerStyle:t.content,keyboardShouldPersistTaps:"handled",children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"secondary",children:"Tell us what you need. We reply by email and in this ticket."}),(0,_jsxRuntime.jsx)(_mobileUi.Select,{label:"Category",placeholder:"Choose a category",required:!0,options:e.categories.map(e=>({label:e,value:e})),value:x.value||void 0,onChange:e=>x.setValue(String(e),!0),error:x.error,testID:"support-new-category"}),(0,_jsxRuntime.jsx)(_mobileUi.Input,{label:"Subject",placeholder:"A short summary",required:!0,maxLength:e.subjectMaxLength,value:b.value,onChangeText:b.onChangeText,onBlur:b.onBlur,error:b.error,testID:"support-new-subject"}),(0,_jsxRuntime.jsx)(_mobileUi.Input,{label:"Message",placeholder:"What happened, and what did you expect?",required:!0,multiline:!0,maxLength:e.bodyMaxLength,value:_.value,onChangeText:_.onChangeText,onBlur:_.onBlur,error:_.error,testID:"support-new-body"}),l.length>0&&(0,_jsxRuntime.jsx)(_PhotoTray.PhotoTray,{label:"Screenshot",photos:l,onChange:u,remaining:0,allowAdd:!1,maxBytes:e.maxAttachmentBytes,disabled:g.isSubmitting}),(0,_jsxRuntime.jsx)(_PhotoTray.PhotoTray,{label:"Photos",photos:n,onChange:s,remaining:e.maxAttachmentsPerMessage-h,maxBytes:e.maxAttachmentBytes,disabled:g.isSubmitting}),(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"tertiary",children:`Attach up to ${e.maxAttachmentsPerMessage} files.`}),(c||m)&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:c??m}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:t.actions,children:[r&&(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:"Cancel",variant:"ghost",tone:"neutral",onPress:r,disabled:g.isSubmitting}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:"Send ticket",loading:g.isSubmitting,disabled:g.isSubmitting,onPress:g.submit,testID:"support-new-submit"})]})]})},NewSupportTicket=e=>{const t=(0,_mobileUi.useTheme)(),i=(0,_react.useMemo)(()=>buildStyles(t),[t]),{config:r,loading:a,error:o,reload:n}=(0,_SupportProvider.useSupportConfig)(),s=e.testID??"support-new-ticket";return r?(0,_jsxRuntime.jsx)(_reactNative.View,{style:i.root,testID:s,children:(0,_jsxRuntime.jsx)(TicketForm,{...e,config:r,styles:i})}):(0,_jsxRuntime.jsx)(_reactNative.View,{style:i.center,testID:s,children:a?(0,_jsxRuntime.jsx)(_mobileUi.Spinner,{accessibilityLabel:"Loading"}):(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",color:"error",align:"center",children:o?.message??"Support is not available right now."}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:"Try again",variant:"ghost",onPress:()=>{n()}})]})})};exports.NewSupportTicket=NewSupportTicket;const buildStyles=e=>_reactNative.StyleSheet.create({root:{flex:1},content:{padding:e.spacing.md,gap:e.spacing.md},center:{flex:1,alignItems:"center",justifyContent:"center",padding:e.spacing.xl,gap:e.spacing.sm},actions:{flexDirection:"row",justifyContent:"flex-end",gap:e.spacing.sm}});
@@ -1,141 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.PhotoTray = void 0;
7
- var _mobileUi = require("@webority/mobile-ui");
8
- var _react = require("react");
9
- var _reactNative = require("react-native");
10
- var _photos = require("../photos.js");
11
- var _jsxRuntime = require("react/jsx-runtime");
12
- /** Photos on a message: thumbnails with remove buttons, plus library and camera buttons. */
13
- const PhotoTray = ({
14
- photos,
15
- onChange,
16
- remaining,
17
- maxBytes,
18
- disabled,
19
- label,
20
- allowAdd = true
21
- }) => {
22
- const theme = (0, _mobileUi.useTheme)();
23
- const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
24
- const [picking, setPicking] = (0, _react.useState)(null);
25
- const [error, setError] = (0, _react.useState)(null);
26
- const add = async source => {
27
- setError(null);
28
- setPicking(source);
29
- try {
30
- const picked = await (0, _photos.pickPhotos)(source, {
31
- maxCount: remaining,
32
- maxBytes
33
- });
34
- if (picked.length > 0) {
35
- onChange([...photos, ...picked]);
36
- }
37
- } catch (e) {
38
- setError(e instanceof Error && e.name === 'PhotoPermissionError' ? e.message : 'That photo could not be added. Try another one.');
39
- } finally {
40
- setPicking(null);
41
- }
42
- };
43
- const full = remaining <= 0;
44
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
45
- style: styles.root,
46
- children: [label ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
47
- variant: "label",
48
- color: "secondary",
49
- children: label
50
- }) : null, photos.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
51
- style: styles.row,
52
- children: photos.map((photo, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
53
- style: styles.item,
54
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
55
- source: {
56
- uri: photo.uri
57
- },
58
- style: styles.thumb,
59
- accessibilityLabel: photo.name
60
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
61
- style: styles.remove,
62
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.IconButton, {
63
- size: "sm",
64
- variant: "glass",
65
- icon: ({
66
- color,
67
- size
68
- }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.AppIcon, {
69
- name: "trash",
70
- color: color,
71
- size: size
72
- }),
73
- accessibilityLabel: `Remove ${photo.name}`,
74
- disabled: disabled,
75
- onPress: () => onChange(photos.filter((_, i) => i !== index))
76
- })
77
- })]
78
- }, `${photo.uri}-${index}`))
79
- }), allowAdd && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
80
- style: styles.actions,
81
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Button, {
82
- size: "sm",
83
- variant: "outline",
84
- tone: "neutral",
85
- leftIcon: "gallery",
86
- title: "Add photo",
87
- accessibilityLabel: "Add a photo from your library",
88
- loading: picking === 'library',
89
- disabled: disabled || full || picking !== null,
90
- onPress: () => void add('library')
91
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Button, {
92
- size: "sm",
93
- variant: "outline",
94
- tone: "neutral",
95
- leftIcon: "camera",
96
- title: "Take photo",
97
- accessibilityLabel: "Take a photo with your camera",
98
- loading: picking === 'camera',
99
- disabled: disabled || full || picking !== null,
100
- onPress: () => void add('camera')
101
- })]
102
- }), error ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_mobileUi.Text, {
103
- variant: "caption",
104
- color: "error",
105
- accessibilityLiveRegion: "polite",
106
- children: error
107
- }) : null]
108
- });
109
- };
110
- exports.PhotoTray = PhotoTray;
111
- const THUMB = 72;
112
- const buildStyles = theme => _reactNative.StyleSheet.create({
113
- root: {
114
- gap: theme.spacing.xs
115
- },
116
- row: {
117
- flexDirection: 'row',
118
- flexWrap: 'wrap',
119
- gap: theme.spacing.xs
120
- },
121
- item: {
122
- width: THUMB,
123
- height: THUMB
124
- },
125
- thumb: {
126
- width: THUMB,
127
- height: THUMB,
128
- borderRadius: theme.radius.sm,
129
- backgroundColor: theme.colors.background.secondary
130
- },
131
- remove: {
132
- position: 'absolute',
133
- top: theme.spacing.xxs,
134
- right: theme.spacing.xxs
135
- },
136
- actions: {
137
- flexDirection: 'row',
138
- gap: theme.spacing.sm
139
- }
140
- });
141
- //# sourceMappingURL=PhotoTray.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.PhotoTray=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_photos=require("../photos.js"),_jsxRuntime=require("react/jsx-runtime");const PhotoTray=({photos:e,onChange:t,remaining:i,maxBytes:o,disabled:a,label:r,allowAdd:s=!0})=>{const l=(0,_mobileUi.useTheme)(),n=(0,_react.useMemo)(()=>buildStyles(l),[l]),[c,u]=(0,_react.useState)(null),[m,x]=(0,_react.useState)(null),add=async a=>{x(null),u(a);try{const r=await(0,_photos.pickPhotos)(a,{maxCount:i,maxBytes:o});r.length>0&&t([...e,...r])}catch(e){x(e instanceof Error&&"PhotoPermissionError"===e.name?e.message:"That photo could not be added. Try another one.")}finally{u(null)}},d=i<=0;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:n.root,children:[r?(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"label",color:"secondary",children:r}):null,e.length>0&&(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.row,children:e.map((i,o)=>(0,_jsxRuntime.jsxs)(_reactNative.View,{style:n.item,children:[(0,_jsxRuntime.jsx)(_reactNative.Image,{source:{uri:i.uri},style:n.thumb,accessibilityLabel:i.name}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.remove,children:(0,_jsxRuntime.jsx)(_mobileUi.IconButton,{size:"sm",variant:"glass",icon:({color:e,size:t})=>(0,_jsxRuntime.jsx)(_mobileUi.AppIcon,{name:"trash",color:e,size:t}),accessibilityLabel:`Remove ${i.name}`,disabled:a,onPress:()=>t(e.filter((e,t)=>t!==o))})})]},`${i.uri}-${o}`))}),s&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:n.actions,children:[(0,_jsxRuntime.jsx)(_mobileUi.Button,{size:"sm",variant:"outline",tone:"neutral",leftIcon:"gallery",title:"Add photo",accessibilityLabel:"Add a photo from your library",loading:"library"===c,disabled:a||d||null!==c,onPress:()=>{add("library")}}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{size:"sm",variant:"outline",tone:"neutral",leftIcon:"camera",title:"Take photo",accessibilityLabel:"Take a photo with your camera",loading:"camera"===c,disabled:a||d||null!==c,onPress:()=>{add("camera")}})]}),m?(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"error",accessibilityLiveRegion:"polite",children:m}):null]})};exports.PhotoTray=PhotoTray;const THUMB=72,buildStyles=e=>_reactNative.StyleSheet.create({root:{gap:e.spacing.xs},row:{flexDirection:"row",flexWrap:"wrap",gap:e.spacing.xs},item:{width:72,height:72},thumb:{width:72,height:72,borderRadius:e.radius.sm,backgroundColor:e.colors.background.secondary},remove:{position:"absolute",top:e.spacing.xxs,right:e.spacing.xxs},actions:{flexDirection:"row",gap:e.spacing.sm}});