@webority/mobile-support 0.1.0 → 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/lib/module/SupportProvider.js +1 -1
- package/lib/module/client.js +1 -1
- package/lib/module/clipboard.js +1 -1
- package/lib/module/components/Attachments.js +1 -1
- package/lib/module/components/NewSupportTicket.js +1 -1
- package/lib/module/components/PhotoTray.js +1 -1
- package/lib/module/components/SupportContact.js +1 -1
- package/lib/module/components/SupportTicketDetail.js +1 -1
- package/lib/module/components/SupportTicketList.js +1 -1
- package/lib/module/device.js +1 -1
- package/lib/module/files.js +1 -1
- package/lib/module/format.js +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/photos.js +1 -1
- package/lib/module/report/ReportIssueButton.js +1 -1
- package/lib/module/report/captureAndReport.js +1 -1
- package/lib/module/report/index.js +1 -1
- package/lib/module/share/index.js +1 -1
- package/lib/module/types.js +1 -0
- package/lib/module/validation.js +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createContext
|
|
1
|
+
"use strict";import{createContext,useCallback,useContext,useEffect,useMemo,useRef,useState}from"react";import{toSupportApiError}from"./client.js";import{saveAttachmentToCache}from"./files.js";import{DEFAULT_TIME_ZONE}from"./format.js";import{jsx as _jsx}from"react/jsx-runtime";const SupportContext=createContext(null);export const SupportProvider=({client:e,openFile:t,timeZone:r=DEFAULT_TIME_ZONE,children:n})=>{const[o,u]=useState(null),[s,c]=useState(null),[a,l]=useState(!0),[i,p]=useState(0),S=useRef(new Map),f=useCallback(async()=>{l(!0);try{u(await e.getConfig()),c(null)}catch(e){c(toSupportApiError(e))}finally{l(!1)}},[e]);useEffect(()=>{S.current.clear(),f()},[f]);const m=useCallback(t=>{const r=S.current.get(t.id);if(r)return r;const n=e.getAttachmentDataUri(t);return S.current.set(t.id,n),n.catch(()=>S.current.delete(t.id)),n},[e]),C=useMemo(()=>t?async r=>{const n=await saveAttachmentToCache(r,()=>e.getAttachmentBytes(r.id));await t({uri:n,name:r.fileName,contentType:r.contentType})}:null,[e,t]),h=useCallback(()=>p(e=>e+1),[]),d=useMemo(()=>({client:e,config:o,configError:s,configLoading:a,reloadConfig:f,loadAttachment:m,openAttachment:C,timeZone:r,revision:i,bumpRevision:h}),[e,o,s,a,f,m,C,r,i,h]);return _jsx(SupportContext.Provider,{value:d,children:n})};const useSupportContext=()=>{const e=useContext(SupportContext);if(!e)throw new Error("[@webority/mobile-support] Support screens and hooks need a <SupportProvider client={...}> above them.");return e};export const useSupportClient=()=>useSupportContext().client;export const useAttachmentLoader=()=>useSupportContext().loadAttachment;export const useAttachmentOpener=()=>useSupportContext().openAttachment;export const useSupportTimeZone=()=>useSupportContext().timeZone;export const useSupportConfig=()=>{const{config:e,configLoading:t,configError:r,reloadConfig:n}=useSupportContext();return{config:e,loading:t,error:r,reload:n}};const DEFAULT_PAGE_SIZE=20;export const useSupportTickets=(e={})=>{const t=e.pageSize??20,{client:r,revision:n}=useSupportContext(),[o,u]=useState(null),[s,c]=useState(""),[a,l]=useState([]),[i,p]=useState(null),[S,f]=useState(0),[m,C]=useState(0),[h,d]=useState(!0),[g,x]=useState(!1),[k,E]=useState(!1),[y,b]=useState(null),[A,T]=useState(null),v=useRef(!0),w=useRef(0),R=useRef(!1),M=useRef(null);useEffect(()=>(v.current=!0,()=>{v.current=!1}),[]);const _=useCallback(e=>({status:o??void 0,search:s.trim()||void 0,page:e,pageSize:t}),[o,s,t]),j=useCallback(async e=>{w.current+=1;const t=w.current,current=()=>v.current&&t===w.current;R.current=!1,E(!1),"refresh"===e?x(!0):"replace"===e&&(d(!0),l([]));const n="replace"!==e||M.current!==r;try{const[e,t]=await Promise.all([r.listTickets(_(1)),n?r.getTicketCounts():null]);current()&&(l(e.items),f(e.page),C(e.totalPages),t&&(p(t),M.current=r),b(null),T(null))}catch(e){current()&&b(toSupportApiError(e))}finally{current()&&(d(!1),x(!1))}},[r,_]),P=useRef(n);useEffect(()=>{const e=P.current===n?"replace":"quiet";P.current=n,j(e)},[j,n]);const Z=S<m,L=useCallback(async()=>{if(R.current||h||g||!Z)return;const e=w.current,current=()=>v.current&&e===w.current;R.current=!0,E(!0),T(null);try{const e=await r.listTickets(_(S+1));current()&&(l(t=>{const r=new Set(t.map(e=>e.id));return[...t,...e.items.filter(e=>!r.has(e.id))]}),f(e.page),C(e.totalPages))}catch(e){current()&&T(toSupportApiError(e))}finally{current()&&(R.current=!1,E(!1))}},[r,_,S,Z,h,g]),D=useCallback(()=>j("refresh"),[j]),F=useCreateSupportTicket();return{tickets:a,counts:i,status:o,search:s,setStatus:u,setSearch:c,loading:h,refreshing:g,loadingMore:k,hasMore:Z,error:y,loadMoreError:A,refresh:D,loadMore:L,createTicket:F}};export const useCreateSupportTicket=()=>{const{client:e,bumpRevision:t}=useSupportContext();return useCallback(async r=>{const n=await e.createTicket(r);return t(),n},[e,t])};export const useSupportTicket=e=>{const{client:t,bumpRevision:r}=useSupportContext(),[n,o]=useState(null),[u,s]=useState(!0),[c,a]=useState(null),[l,i]=useState(null),[p,S]=useState(!1),f=useRef(!0);useEffect(()=>(f.current=!0,()=>{f.current=!1}),[]);const m=useCallback(async()=>{s(!0);try{const r=await t.getTicket(e);f.current&&(o(r),a(null))}catch(e){f.current&&a(toSupportApiError(e))}finally{f.current&&s(!1)}},[t,e]);useEffect(()=>{m()},[m]);const C=useCallback(async e=>{S(!0),i(null);try{const t=await e();return f.current&&o(t),r(),!0}catch(e){return f.current&&i(toSupportApiError(e)),!1}finally{f.current&&S(!1)}},[r]),h=useCallback(r=>C(()=>t.reply(e,r)),[C,t,e]),d=useCallback(()=>C(()=>t.reopen(e)),[C,t,e]),g=useCallback(r=>C(()=>t.rate(e,r)),[C,t,e]);return{ticket:n,loading:u,error:c,actionError:l,busy:p,reload:m,reply:h,reopen:d,rate:g}};
|
package/lib/module/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{encodeBase64
|
|
1
|
+
"use strict";import{encodeBase64}from"@webority/mobile-core";export const OFFLINE_MESSAGE="You are offline. Check your connection and try again.";export const GENERIC_MESSAGE="Something went wrong. Try again in a moment.";export class SupportApiError extends Error{constructor(e,t,r){super(t),this.name="SupportApiError",this.code=e,this.status=r}}export const toSupportApiError=e=>{if(e instanceof SupportApiError)return e;const t=e??{};if("RequestOfflineError"===t.name)return new SupportApiError("offline",OFFLINE_MESSAGE,null);const r="number"==typeof t.response?.status?t.response.status:null,n=t.response?.data;if(n&&"string"==typeof n.message&&n.message.trim()){const e="string"==typeof n.code?n.code:"unknown";return new SupportApiError(e,n.message,r)}return t.response?new SupportApiError("unknown",GENERIC_MESSAGE,r):new SupportApiError("network",OFFLINE_MESSAGE,null)};const UPLOAD_TIMEOUT_MS=3e5,MULTIPART={"Content-Type":"multipart/form-data"},appendParts=(e,t,r)=>{for(const n of r??[])e.append(t,{uri:n.uri,name:n.name,type:n.type})},appendText=(e,t,r)=>{void 0!==r&&""!==r&&e.append(t,r)},queryString=e=>{const t=[],add=(e,r)=>{void 0!==r&&""!==r&&t.push(`${e}=${encodeURIComponent(String(r))}`)};return add("status",e.status),add("search",e.search?.trim()),add("page",e.page),add("pageSize",e.pageSize),t.length>0?`?${t.join("&")}`:""};export const createSupportClient=(e,t="/api/support")=>{const r=t.replace(/\/+$/,""),ticketUrl=e=>`${r}/tickets/${encodeURIComponent(e)}`,call=async e=>{try{return(await e()).data}catch(e){throw toSupportApiError(e)}},getBytes=t=>call(()=>e.get(`${r}/attachments/${encodeURIComponent(t)}`,{responseType:"arraybuffer"}));return{basePath:r,getConfig:()=>call(()=>e.get(`${r}/settings`)),listTickets:(t={})=>call(()=>e.get(`${r}/tickets${queryString(t)}`)),getTicketCounts:()=>call(()=>e.get(`${r}/tickets/counts`)),getTicket:t=>call(()=>e.get(ticketUrl(t))),createTicket:t=>{const n=new FormData;return n.append("category",t.category),n.append("subject",t.subject),n.append("body",t.body),appendText(n,"pageUrl",t.pageUrl),appendText(n,"userAgent",t.userAgent),appendText(n,"appVersion",t.appVersion),appendParts(n,"files",t.files),appendParts(n,"screenshots",t.screenshots),call(()=>e.post(`${r}/tickets`,n,{headers:MULTIPART,timeout:3e5}))},reply:(t,r)=>{const n=new FormData;return n.append("body",r.body),appendParts(n,"files",r.files),appendParts(n,"screenshots",r.screenshots),call(()=>e.post(`${ticketUrl(t)}/replies`,n,{headers:MULTIPART,timeout:3e5}))},reopen:t=>call(()=>e.post(`${ticketUrl(t)}/reopen`)),rate:(t,r)=>call(()=>e.post(`${ticketUrl(t)}/rating`,{satisfaction:r.satisfaction,comment:r.comment?.trim()?r.comment.trim():null})),getAttachmentBytes:getBytes,getAttachmentDataUri:async e=>{const t=await getBytes(e.id);return`data:${e.contentType};base64,${encodeBase64(t)}`},confirmAction:t=>call(()=>e.post(`${r}/actions/confirm`,{token:t}))}};
|
package/lib/module/clipboard.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const copyText=e=>require("expo-clipboard").setStringAsync(e);
|
|
1
|
+
"use strict";export const copyText=e=>require("expo-clipboard").setStringAsync(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AppIcon
|
|
1
|
+
"use strict";import{AppIcon,IconButton,Spinner,Text,ToastHost,toast,useTheme,ZoomableImage}from"@webority/mobile-ui";import{useEffect,useMemo,useState}from"react";import{Image,Modal,Pressable,StyleSheet,View}from"react-native";import{GestureHandlerRootView}from"react-native-gesture-handler";import{formatBytes,isImage}from"../format.js";import{useAttachmentLoader,useAttachmentOpener}from"../SupportProvider.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const AttachmentThumb=({attachment:e,onOpen:t})=>{const s=useTheme(),i=useMemo(()=>buildStyles(s),[s]),a=useAttachmentLoader(),[r,o]=useState({status:"loading"});return useEffect(()=>{let t=!1;return a(e).then(e=>!t&&o({status:"ready",uri:e}),()=>!t&&o({status:"error"})),()=>{t=!0}},[a,e]),"loading"===r.status?_jsx(View,{style:i.thumb,testID:`support-attachment-${e.id}`,children:_jsx(Spinner,{size:"sm",accessibilityLabel:`Loading ${e.fileName}`})}):"error"===r.status?_jsx(View,{style:i.thumb,accessible:!0,accessibilityLabel:`${e.fileName} could not be loaded`,testID:`support-attachment-${e.id}`,children:_jsx(Text,{variant:"caption",color:"tertiary",align:"center",children:"Could not load"})}):_jsx(Pressable,{onPress:()=>t(r.uri,e.fileName),accessibilityRole:"imagebutton",accessibilityLabel:`Open ${e.fileName}`,testID:`support-attachment-${e.id}`,children:_jsx(Image,{source:{uri:r.uri},style:i.thumb,resizeMode:"cover"})})},FileRow=({attachment:e})=>{const t=useTheme(),s=useMemo(()=>buildStyles(t),[t]),i=useAttachmentOpener(),[a,r]=useState(!1),o=`${e.fileName}, ${formatBytes(e.sizeBytes)}`;if(!i)return _jsx(Text,{variant:"caption",color:"secondary",testID:`support-file-${e.id}`,children:o});return _jsxs(Pressable,{onPress:()=>{(async()=>{r(!0);try{await i(e)}catch{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:s.fileRow,testID:`support-file-${e.id}`,children:[_jsx(AppIcon,{name:"document",size:16,color:t.colors.text.link}),_jsx(Text,{variant:"caption",color:"link",children:o}),a&&_jsx(Spinner,{size:"sm",accessibilityLabel:`Opening ${e.fileName}`})]})};export const AttachmentList=({attachments:e,onOpenImage:t})=>{const s=useTheme(),i=useMemo(()=>buildStyles(s),[s]);if(0===e.length)return null;const a=e.filter(e=>isImage(e.contentType)),r=e.filter(e=>!isImage(e.contentType));return _jsxs(View,{style:i.list,children:[a.length>0&&_jsx(View,{style:i.thumbRow,children:a.map(e=>_jsx(AttachmentThumb,{attachment:e,onOpen:t},e.id))}),r.map(e=>_jsx(FileRow,{attachment:e},e.id))]})};export const ImageViewer=({image:e,onClose:t})=>{const s=useTheme(),i=useMemo(()=>buildStyles(s),[s]);return _jsx(Modal,{visible:null!==e,onRequestClose:t,animationType:"fade",transparent:!1,children:_jsxs(GestureHandlerRootView,{style:i.viewer,testID:"support-image-viewer",children:[e&&_jsx(ZoomableImage,{accessibilityLabel:e.name,style:i.viewerImageWrap,children:_jsx(Image,{source:{uri:e.uri},style:i.viewerImage,resizeMode:"contain"})}),_jsx(View,{style:i.viewerClose,children:_jsx(IconButton,{icon:({color:e,size:t})=>_jsx(AppIcon,{name:"close-circle",color:e,size:t}),accessibilityLabel:"Close image",onPress:t,variant:"glass"})}),_jsx(ToastHost,{})]})})};const THUMB=84,buildStyles=e=>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 +1 @@
|
|
|
1
|
-
import{Button
|
|
1
|
+
"use strict";import{Button,Input,Select,Spinner,Text,useForm,useTheme}from"@webority/mobile-ui";import{useMemo,useState}from"react";import{ScrollView,StyleSheet,View}from"react-native";import{toSupportApiError}from"../client.js";import{useCreateSupportTicket,useSupportConfig}from"../SupportProvider.js";import{validateBody,validateCategory,validateFiles,validateSubject}from"../validation.js";import{PhotoTray}from"./PhotoTray.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";const TicketForm=({config:e,styles:t,onCreated:o,onCancel:r,initialDraft:a})=>{const s=useCreateSupportTicket(),[n,i]=useState([]),[l,c]=useState(a?.screenshots??[]),[u,d]=useState(null),[p,g]=useState(null),m=useForm({initialValues:{category:a?.category??"",subject:a?.subject??"",body:a?.body??""},validate:{category:t=>validateCategory(t,e),subject:t=>validateSubject(t,e),body:t=>validateBody(t,e)},onSubmit:async t=>{const r=validateFiles([...l,...n],e);if(d(r??null),!r){g(null);try{const e=await s({category:t.category,subject:t.subject.trim(),body:t.body.trim(),pageUrl:a?.pageUrl,userAgent:a?.userAgent,appVersion:a?.appVersion,files:n,screenshots:l});o(e)}catch(e){g(toSupportApiError(e).message)}}}}),h=m.getFieldProps("category"),y=m.getFieldProps("subject"),b=m.getFieldProps("body"),x=l.length+n.length;return _jsxs(ScrollView,{contentContainerStyle:t.content,keyboardShouldPersistTaps:"handled",children:[_jsx(Text,{variant:"bodySmall",color:"secondary",children:"Tell us what you need. We reply by email and in this ticket."}),_jsx(Select,{label:"Category",placeholder:"Choose a category",required:!0,options:e.categories.map(e=>({label:e,value:e})),value:h.value||void 0,onChange:e=>h.setValue(String(e),!0),error:h.error,testID:"support-new-category"}),_jsx(Input,{label:"Subject",placeholder:"A short summary",required:!0,maxLength:e.subjectMaxLength,value:y.value,onChangeText:y.onChangeText,onBlur:y.onBlur,error:y.error,testID:"support-new-subject"}),_jsx(Input,{label:"Message",placeholder:"What happened, and what did you expect?",required:!0,multiline:!0,maxLength:e.bodyMaxLength,value:b.value,onChangeText:b.onChangeText,onBlur:b.onBlur,error:b.error,testID:"support-new-body"}),l.length>0&&_jsx(PhotoTray,{label:"Screenshot",photos:l,onChange:c,remaining:0,allowAdd:!1,maxBytes:e.maxAttachmentBytes,disabled:m.isSubmitting}),_jsx(PhotoTray,{label:"Photos",photos:n,onChange:i,remaining:e.maxAttachmentsPerMessage-x,maxBytes:e.maxAttachmentBytes,disabled:m.isSubmitting}),_jsx(Text,{variant:"caption",color:"tertiary",children:`Attach up to ${e.maxAttachmentsPerMessage} files.`}),(u||p)&&_jsx(Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:u??p}),_jsxs(View,{style:t.actions,children:[r&&_jsx(Button,{title:"Cancel",variant:"ghost",tone:"neutral",onPress:r,disabled:m.isSubmitting}),_jsx(Button,{title:"Send ticket",loading:m.isSubmitting,disabled:m.isSubmitting,onPress:m.submit,testID:"support-new-submit"})]})]})};export const NewSupportTicket=e=>{const t=useTheme(),o=useMemo(()=>buildStyles(t),[t]),{config:r,loading:a,error:s,reload:n}=useSupportConfig(),i=e.testID??"support-new-ticket";return _jsx(View,r?{style:o.root,testID:i,children:_jsx(TicketForm,{...e,config:r,styles:o})}:{style:o.center,testID:i,children:a?_jsx(Spinner,{accessibilityLabel:"Loading"}):_jsxs(_Fragment,{children:[_jsx(Text,{variant:"body",color:"error",align:"center",children:s?.message??"Support is not available right now."}),_jsx(Button,{title:"Try again",variant:"ghost",onPress:()=>{n()}})]})})};const buildStyles=e=>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 +1 @@
|
|
|
1
|
-
import{AppIcon
|
|
1
|
+
"use strict";import{AppIcon,Button,IconButton,Text,useTheme}from"@webority/mobile-ui";import{useMemo,useState}from"react";import{Image,StyleSheet,View}from"react-native";import{pickPhotos}from"../photos.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const PhotoTray=({photos:e,onChange:t,remaining:o,maxBytes:s,disabled:i,label:a,allowAdd:r=!0})=>{const l=useTheme(),n=useMemo(()=>buildStyles(l),[l]),[c,m]=useState(null),[u,h]=useState(null),add=async i=>{h(null),m(i);try{const a=await pickPhotos(i,{maxCount:o,maxBytes:s});a.length>0&&t([...e,...a])}catch(e){h(e instanceof Error&&"PhotoPermissionError"===e.name?e.message:"That photo could not be added. Try another one.")}finally{m(null)}},d=o<=0;return _jsxs(View,{style:n.root,children:[a?_jsx(Text,{variant:"label",color:"secondary",children:a}):null,e.length>0&&_jsx(View,{style:n.row,children:e.map((o,s)=>_jsxs(View,{style:n.item,children:[_jsx(Image,{source:{uri:o.uri},style:n.thumb,accessibilityLabel:o.name}),_jsx(View,{style:n.remove,children:_jsx(IconButton,{size:"sm",variant:"glass",icon:({color:e,size:t})=>_jsx(AppIcon,{name:"trash",color:e,size:t}),accessibilityLabel:`Remove ${o.name}`,disabled:i,onPress:()=>t(e.filter((e,t)=>t!==s))})})]},`${o.uri}-${s}`))}),r&&_jsxs(View,{style:n.actions,children:[_jsx(Button,{size:"sm",variant:"outline",tone:"neutral",leftIcon:"gallery",title:"Add photo",accessibilityLabel:"Add a photo from your library",loading:"library"===c,disabled:i||d||null!==c,onPress:()=>{add("library")}}),_jsx(Button,{size:"sm",variant:"outline",tone:"neutral",leftIcon:"camera",title:"Take photo",accessibilityLabel:"Take a photo with your camera",loading:"camera"===c,disabled:i||d||null!==c,onPress:()=>{add("camera")}})]}),u?_jsx(Text,{variant:"caption",color:"error",accessibilityLiveRegion:"polite",children:u}):null]})};const THUMB=72,buildStyles=e=>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}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Button
|
|
1
|
+
"use strict";import{Button,Card,Spinner,Text,toast,useTheme}from"@webority/mobile-ui";import{useMemo}from"react";import{Linking,StyleSheet,View}from"react-native";import{copyText}from"../clipboard.js";import{useSupportConfig}from"../SupportProvider.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SupportContact=({testID:t="support-contact"})=>{const e=useTheme(),s=useMemo(()=>buildStyles(e),[e]),{config:o,loading:i,error:a}=useSupportConfig();if(!o)return _jsx(Card,{variant:"outlined",testID:t,children:i?_jsx(Spinner,{size:"sm",accessibilityLabel:"Loading contact details"}):_jsx(Text,{variant:"bodySmall",color:"error",children:a?.message??"Contact details are not available right now."})});const r=o.supportEmail,l=o.supportPhone?.trim(),n=o.supportHours?.trim(),open=(t,e)=>{Linking.openURL(t).catch(()=>toast.error(e))};return _jsxs(Card,{variant:"outlined",bodyStyle:s.body,testID:t,children:[_jsx(Text,{variant:"title",accessibilityRole:"header",children:`Contact ${o.supportName||o.productName}`}),_jsxs(View,{style:s.row,children:[_jsx(Text,{variant:"label",color:"secondary",children:"Email"}),_jsx(Text,{variant:"body",selectable:!0,testID:"support-contact-email",children:r||"-"})]}),r?_jsxs(View,{style:s.actions,children:[_jsx(Button,{title:"Copy",size:"sm",variant:"outline",tone:"neutral",leftIcon:"copy",accessibilityLabel:`Copy ${r}`,onPress:()=>{(async()=>{try{await copyText(r),toast.success("Email address copied.")}catch{toast.error("Could not copy the email address.")}})()},testID:"support-contact-copy"}),_jsx(Button,{title:"Send email",size:"sm",variant:"outline",leftIcon:"mail",accessibilityLabel:`Send an email to ${r}`,onPress:()=>open(`mailto:${r}`,"No email app is set up on this device."),testID:"support-contact-mail"})]}):null,l?_jsxs(View,{style:s.row,children:[_jsx(Text,{variant:"label",color:"secondary",children:"Phone"}),_jsx(Button,{title:l,size:"sm",variant:"link",leftIcon:"call",accessibilityLabel:`Call ${l}`,onPress:()=>open(`tel:${l.replace(/[^\d+]/g,"")}`,"This device cannot make calls."),testID:"support-contact-phone"})]}):null,n?_jsxs(View,{style:s.row,children:[_jsx(Text,{variant:"label",color:"secondary",children:"Hours"}),_jsx(Text,{variant:"bodySmall",children:n})]}):null]})};const buildStyles=t=>StyleSheet.create({body:{gap:t.spacing.sm},row:{gap:t.spacing.xxs,alignItems:"flex-start"},actions:{flexDirection:"row",gap:t.spacing.sm}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Button
|
|
1
|
+
"use strict";import{Button,Card,Input,Spinner,StatusBadge,Text,useTheme}from"@webority/mobile-ui";import{useMemo,useState}from"react";import{ScrollView,StyleSheet,View}from"react-native";import{formatWhen}from"../format.js";import{useSupportConfig,useSupportTicket,useSupportTimeZone}from"../SupportProvider.js";import{validateBody,validateFiles}from"../validation.js";import{AttachmentList,ImageViewer}from"./Attachments.js";import{PhotoTray}from"./PhotoTray.js";import{STATUS_TONES}from"./SupportTicketList.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";const MessageBubble=({message:e,styles:t,timeZone:s,onOpenImage:i})=>{if("System"===e.authorKind)return _jsx(View,{style:t.system,testID:`support-message-${e.id}`,children:_jsx(Text,{variant:"caption",color:"tertiary",align:"center",children:`${e.body}, ${formatWhen(e.createdDateTimeUtc,s)}`})});const a="Customer"===e.authorKind;return _jsx(View,{style:[t.bubbleRow,a?t.mineRow:t.theirsRow],testID:`support-message-${e.id}`,children:_jsxs(View,{style:[t.bubble,a?t.mine:t.theirs],children:[_jsx(Text,{variant:"label",color:a?"primary":"secondary",children:e.authorName??"-"}),_jsx(Text,{variant:"body",selectable:!0,children:e.body}),_jsx(AttachmentList,{attachments:e.attachments,onOpenImage:i}),_jsx(Text,{variant:"caption",color:"tertiary",children:formatWhen(e.createdDateTimeUtc,s)})]})})},SATISFACTION_TEXT={Satisfied:"You said we handled this well.",Unsatisfied:"You said we did not handle this well."};export const SupportTicketDetail=({ticketId:e,testID:t="support-ticket-detail"})=>{const s=useTheme(),i=useMemo(()=>buildStyles(s),[s]),{config:a}=useSupportConfig(),n=useSupportTimeZone(),{ticket:o,loading:r,error:l,actionError:c,busy:d,reload:p,reply:m,reopen:u,rate:g}=useSupportTicket(e),[x,h]=useState(null),[y,b]=useState(""),[j,S]=useState([]),[f,_]=useState(null),[T,w]=useState(null),[v,I]=useState("");if(!o)return _jsx(View,{style:i.center,testID:t,children:r?_jsx(Spinner,{accessibilityLabel:"Loading the ticket"}):_jsxs(_Fragment,{children:[_jsx(Text,{variant:"body",color:"error",align:"center",children:l?.message??"That ticket could not be found."}),_jsx(Button,{title:"Try again",variant:"ghost",onPress:()=>{p()}})]})});const C=o.messages.filter(e=>!e.isInternal);return _jsxs(View,{style:i.root,testID:t,children:[_jsxs(ScrollView,{contentContainerStyle:i.content,keyboardShouldPersistTaps:"handled",children:[_jsxs(View,{style:i.head,children:[_jsx(Text,{variant:"h3",accessibilityRole:"header",children:o.subject}),_jsx(Text,{variant:"bodySmall",color:"secondary",children:`${o.number}, ${o.category}`}),_jsxs(View,{style:i.headRow,children:[_jsx(StatusBadge,{label:o.statusLabel,tone:STATUS_TONES[o.status]??"neutral",accessibilityLabel:`Status: ${o.statusLabel}`}),_jsx(Text,{variant:"caption",color:"tertiary",children:`Opened ${formatWhen(o.createdDateTimeUtc,n)}`})]})]}),o.canReopen&&_jsxs(Card,{variant:"outlined",bodyStyle:i.section,children:[_jsx(Text,{variant:"bodySmall",children:`This ticket is ${o.statusLabel}. Reopen it if you still need help.`}),_jsx(Button,{title:"Reopen ticket",variant:"outline",size:"sm",loading:d,disabled:d,onPress:()=>{u()},testID:"support-reopen"})]}),o.canRate&&_jsxs(Card,{variant:"outlined",bodyStyle:i.section,testID:"support-rating",children:[_jsx(Text,{variant:"label",children:"How did we do?"}),o.satisfaction&&_jsx(Text,{variant:"bodySmall",color:"secondary",children:SATISFACTION_TEXT[o.satisfaction]}),_jsxs(View,{style:i.ratingRow,children:[_jsx(Button,{title:"Satisfied",size:"sm",leftIcon:"like",variant:"Satisfied"===T?"solid":"outline",tone:"success",accessibilityState:{selected:"Satisfied"===T},onPress:()=>w("Satisfied"),testID:"support-rate-satisfied"}),_jsx(Button,{title:"Not satisfied",size:"sm",leftIcon:"dislike",variant:"Unsatisfied"===T?"solid":"outline",tone:"error",accessibilityState:{selected:"Unsatisfied"===T},onPress:()=>w("Unsatisfied"),testID:"support-rate-unsatisfied"})]}),T&&_jsxs(_Fragment,{children:[_jsx(Input,{label:"Anything to add? (optional)",value:v,onChangeText:I,multiline:!0,maxLength:a?.satisfactionCommentMaxLength,testID:"support-rating-comment"}),_jsx(Button,{title:"Send rating",size:"sm",loading:d,disabled:d,onPress:()=>{(async()=>{T&&await g({satisfaction:T,comment:v})&&(w(null),I(""))})()},testID:"support-rating-send"})]})]}),_jsx(View,{style:i.thread,accessibilityLabel:"Conversation",children:C.map(e=>_jsx(MessageBubble,{message:e,styles:i,timeZone:n,onOpenImage:(e,t)=>h({uri:e,name:t})},e.id))}),_jsxs(View,{style:i.section,children:[_jsx(Input,{label:"Your reply",placeholder:"Write a reply to the support team",value:y,onChangeText:b,multiline:!0,maxLength:a?.bodyMaxLength,error:f??void 0,testID:"support-reply-input"}),a&&_jsx(PhotoTray,{photos:j,onChange:S,remaining:a.maxAttachmentsPerMessage-j.length,maxBytes:a.maxAttachmentBytes,disabled:d}),o.canReopen&&_jsx(Text,{variant:"caption",color:"secondary",children:"Replying reopens this ticket."}),c&&_jsx(Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:c.message}),_jsx(Button,{title:"Send reply",leftIcon:"send",loading:d,disabled:d,onPress:()=>{(async()=>{const e=y.trim(),t=a?validateBody(e,a)??validateFiles(j,a):e?void 0:"Write a message.";t?_(t):(_(null),await m({body:e,files:j})&&(b(""),S([])))})()},testID:"support-reply-send"})]})]}),_jsx(ImageViewer,{image:x,onClose:()=>h(null)})]})};const buildStyles=e=>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},head:{gap:e.spacing.xxs},headRow:{flexDirection:"row",alignItems:"center",gap:e.spacing.sm,marginTop:e.spacing.xs},section:{gap:e.spacing.sm},ratingRow:{flexDirection:"row",gap:e.spacing.sm},thread:{gap:e.spacing.sm},system:{paddingHorizontal:e.spacing.lg,paddingVertical:e.spacing.xxs},bubbleRow:{flexDirection:"row"},mineRow:{justifyContent:"flex-end"},theirsRow:{justifyContent:"flex-start"},bubble:{maxWidth:"85%",borderRadius:e.radius.lg,padding:e.spacing.sm,gap:e.spacing.xxs},mine:{backgroundColor:e.colors.primaryMuted},theirs:{backgroundColor:e.colors.background.secondary}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Button
|
|
1
|
+
"use strict";import{Button,Card,Chip,EmptyState,SearchBar,Spinner,StatusBadge,Text,useTheme}from"@webority/mobile-ui";import{useMemo}from"react";import{FlatList,RefreshControl,ScrollView,StyleSheet,View}from"react-native";import{formatWhen}from"../format.js";import{useSupportTickets,useSupportTimeZone}from"../SupportProvider.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";export const STATUS_TONES={Open:"warning",InProgress:"info",Resolved:"success",Closed:"neutral"};const STATUS_FILTERS=[{value:"Open",label:"Open",count:"open"},{value:"InProgress",label:"In progress",count:"inProgress"},{value:"Resolved",label:"Resolved",count:"resolved"},{value:"Closed",label:"Closed",count:"closed"}],ALL="all";export const statusFilterOptions=(e,t)=>[{value:ALL,label:"All",count:e.total},...STATUS_FILTERS.filter(s=>e[s.count]>0||s.value===t).map(t=>({value:t.value,label:t.label,count:e[t.count]}))];const SEARCH_DEBOUNCE_MS=400;export const SupportTicketList=({onOpenTicket:e,onNewTicket:t,header:s,pageSize:r,testID:i="support-ticket-list"})=>{const o=useTheme(),a=useMemo(()=>buildStyles(o),[o]),{tickets:n,counts:l,status:c,search:u,setStatus:d,setSearch:p,loading:m,refreshing:h,loadingMore:x,error:g,loadMoreError:b,refresh:S,loadMore:y}=useSupportTickets({pageSize:r}),j=useSupportTimeZone(),_=(l?.total??0)>0,f=null!==c||""!==u.trim(),T=_jsxs(View,{style:a.header,children:[s,t&&_jsx(Button,{title:"New ticket",leftIcon:"add",onPress:t,fullWidth:!0}),g&&_jsxs(View,{style:a.error,accessibilityLiveRegion:"polite",children:[_jsx(Text,{variant:"bodySmall",color:"error",children:g.message}),_jsx(Button,{title:"Try again",size:"sm",variant:"ghost",onPress:()=>{S()}})]}),l&&_&&_jsxs(_Fragment,{children:[_jsx(SearchBar,{value:u,onChangeText:p,debounceMs:400,placeholder:"Search by subject or ticket number",accessibilityLabel:"Search tickets",testID:"support-ticket-search"}),_jsx(ScrollView,{horizontal:!0,showsHorizontalScrollIndicator:!1,contentContainerStyle:a.filters,accessibilityRole:"tablist",accessibilityLabel:"Filter by status",children:statusFilterOptions(l,c).map(e=>_jsx(Chip,{label:`${e.label} (${e.count})`,selected:(c??ALL)===e.value,onPress:()=>d(e.value===ALL?null:e.value),accessibilityLabel:`${e.label}, ${e.count} tickets`,testID:`support-filter-${e.value}`},e.value))})]})]});if(m&&null===l&&!g)return _jsx(View,{style:a.center,testID:i,children:_jsx(Spinner,{accessibilityLabel:"Loading your tickets"})});const v=x?_jsx(View,{style:a.footer,children:_jsx(Spinner,{size:"sm",accessibilityLabel:"Loading more tickets"})}):b?_jsxs(View,{style:[a.footer,a.error],accessibilityLiveRegion:"polite",children:[_jsx(Text,{variant:"bodySmall",color:"error",children:b.message}),_jsx(Button,{title:"Load more",size:"sm",variant:"ghost",onPress:()=>{y()}})]}):null;return _jsx(FlatList,{testID:i,data:n,keyExtractor:e=>e.id,contentContainerStyle:a.content,ListHeaderComponent:T,ListFooterComponent:v,onEndReached:()=>{y()},onEndReachedThreshold:.5,refreshControl:_jsx(RefreshControl,{refreshing:h,onRefresh:()=>{S()}}),ListEmptyComponent:m?_jsx(View,{style:a.footer,children:_jsx(Spinner,{accessibilityLabel:"Loading your tickets"})}):g?null:_jsx(EmptyState,_?{title:"No tickets match",description:f?"Try another status or search.":void 0}:{title:"No tickets yet",description:"Raise a ticket and it will show here with its status.",action:t?{label:"New ticket",onPress:t}:void 0}),renderItem:({item:t})=>_jsxs(Card,{onPress:()=>e(t),accessibilityLabel:`${t.subject}, ${t.statusLabel}, ticket ${t.number}`,bodyStyle:a.card,testID:`support-ticket-${t.id}`,children:[_jsxs(View,{style:a.cardTop,children:[_jsx(Text,{variant:"caption",color:"tertiary",children:t.number}),_jsx(StatusBadge,{label:t.statusLabel,tone:STATUS_TONES[t.status]??"neutral",size:"sm"})]}),_jsx(Text,{variant:"title",numberOfLines:2,children:t.subject}),_jsx(Text,{variant:"bodySmall",color:"secondary",children:t.category}),_jsxs(View,{style:a.cardBottom,children:[_jsx(Text,{variant:"caption",color:"tertiary",children:`Updated ${formatWhen(t.updatedDateTimeUtc,j)}`}),t.awaitingCustomer&&_jsx(Text,{variant:"caption",color:"warning",children:"Waiting for your reply"})]})]})})};const buildStyles=e=>StyleSheet.create({content:{padding:e.spacing.md,gap:e.spacing.sm},header:{gap:e.spacing.sm,marginBottom:e.spacing.xs},filters:{gap:e.spacing.xs},error:{gap:e.spacing.xs,alignItems:"flex-start"},center:{flex:1,alignItems:"center",justifyContent:"center",padding:e.spacing.xl},footer:{paddingVertical:e.spacing.md,alignItems:"center"},card:{gap:e.spacing.xxs},cardTop:{flexDirection:"row",justifyContent:"space-between",alignItems:"center"},cardBottom:{flexDirection:"row",justifyContent:"space-between",marginTop:e.spacing.xxs}});
|
package/lib/module/device.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getDeviceContext
|
|
1
|
+
"use strict";import{getDeviceContext}from"@webority/mobile-core";import{Platform}from"react-native";const PLATFORM_NAMES={ios:"iOS",android:"Android"};export const describeDevice=()=>{const e=getDeviceContext(),o=[`${PLATFORM_NAMES[Platform.OS]??Platform.OS} ${e?.osVersion??String(Platform.Version??"")}`.trim()];return e?.deviceModel&&o.push(e.deviceModel),o.join("; ")};
|
package/lib/module/files.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{File
|
|
1
|
+
"use strict";import{File,Paths}from"expo-file-system";const FOLDER="support-attachments";export const safeFileName=t=>{const e=t.split(/[/\\]/).pop()??"",r=Array.from(e).filter(t=>(t.codePointAt(0)??0)>31).join("").trim();return""===r||"."===r||".."===r?"attachment":r};export const saveAttachmentToCache=async(t,e)=>{const r=new File(Paths.cache,FOLDER,`${t.id}-${safeFileName(t.fileName)}`);if(r.exists&&r.size>0)return r.uri;const i=await e();return r.create({intermediates:!0,overwrite:!0}),r.write(new Uint8Array(i)),r.uri};
|
package/lib/module/format.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DEFAULT_TIME_ZONE="Asia/Kolkata";export const formatWhen=(t,e="Asia/Kolkata")=>{if(!t)return"-";const o=new Date(t);if(Number.isNaN(o.getTime()))return"-";const r={};for(const t of new Intl.DateTimeFormat("en-US",{timeZone:e,day:"2-digit",month:"short",year:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).formatToParts(o))r[t.type]=t.value;return`${r.day}-${r.month}-${r.year}, ${r.hour}:${r.minute} ${(r.dayPeriod??"").toUpperCase()}`};export const formatBytes=t=>t>=1048576?`${(t/1048576).toFixed(1)} MB`:t>=1024?`${Math.round(t/1024)} KB`:`${t} bytes`;export const isImage=t=>t.startsWith("image/");
|
|
1
|
+
"use strict";export const DEFAULT_TIME_ZONE="Asia/Kolkata";export const formatWhen=(t,e="Asia/Kolkata")=>{if(!t)return"-";const o=new Date(t);if(Number.isNaN(o.getTime()))return"-";const r={};for(const t of new Intl.DateTimeFormat("en-US",{timeZone:e,day:"2-digit",month:"short",year:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).formatToParts(o))r[t.type]=t.value;return`${r.day}-${r.month}-${r.year}, ${r.hour}:${r.minute} ${(r.dayPeriod??"").toUpperCase()}`};export const formatBytes=t=>t>=1048576?`${(t/1048576).toFixed(1)} MB`:t>=1024?`${Math.round(t/1024)} KB`:`${t} bytes`;export const isImage=t=>t.startsWith("image/");
|
package/lib/module/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createSupportClient,SupportApiError,toSupportApiError}from"./client.js";export{NewSupportTicket}from"./components/NewSupportTicket.js";export{SupportContact}from"./components/SupportContact.js";export{SupportTicketDetail}from"./components/SupportTicketDetail.js";export{SupportTicketList}from"./components/SupportTicketList.js";export{describeDevice}from"./device.js";export{SupportProvider,useCreateSupportTicket,useSupportClient,useSupportConfig,useSupportTicket,useSupportTickets}from"./SupportProvider.js";
|
|
1
|
+
"use strict";export{createSupportClient,SupportApiError,toSupportApiError}from"./client.js";export{NewSupportTicket}from"./components/NewSupportTicket.js";export{SupportContact}from"./components/SupportContact.js";export{SupportTicketDetail}from"./components/SupportTicketDetail.js";export{SupportTicketList}from"./components/SupportTicketList.js";export{describeDevice}from"./device.js";export{SupportProvider,useCreateSupportTicket,useSupportClient,useSupportConfig,useSupportTicket,useSupportTickets}from"./SupportProvider.js";
|
package/lib/module/photos.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{compressImage
|
|
1
|
+
"use strict";import{compressImage}from"@webority/mobile-core/compressor";const picker=()=>require("expo-image-picker");export class PhotoPermissionError extends Error{constructor(e){super("camera"===e?"Allow camera access in Settings to take a photo.":"Allow photo access in Settings to attach a photo."),this.name="PhotoPermissionError",this.source=e}}const CAPTURE_OPTIONS={mediaTypes:["images"],quality:.8,allowsEditing:!1},MAX_SIDE=1920;export const pickPhotos=async(e,t)=>{if(t.maxCount<=0)return[];const s=picker();if(!("camera"===e?await s.requestCameraPermissionsAsync():await s.requestMediaLibraryPermissionsAsync()).granted)throw new PhotoPermissionError(e);const o="camera"===e?await s.launchCameraAsync(CAPTURE_OPTIONS):await s.launchImageLibraryAsync({...CAPTURE_OPTIONS,allowsMultipleSelection:t.maxCount>1,selectionLimit:t.maxCount});if(o.canceled||!o.assets)return[];const a=[];for(const e of o.assets.slice(0,t.maxCount)){const s=await compressImage(e.uri,{maxSize:1920,maxBytes:t.maxBytes,fileSize:e.fileSize??void 0,name:e.fileName??`photo-${Date.now()}-${a.length+1}`});a.push({uri:s.part.uri,name:s.part.name,type:s.part.type,size:s.size??void 0})}return a};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Button
|
|
1
|
+
"use strict";import{Button}from"@webority/mobile-ui";import{useState}from"react";import{captureAndReport}from"./captureAndReport.js";import{jsx as _jsx}from"react/jsx-runtime";export const ReportIssueButton=({onReport:t,onCaptureError:e,screenName:r,appVersion:s,category:o,subject:a,title:i="Report an issue",accessibilityLabel:n="Report an issue with this screen",testID:p="support-report-issue",...c})=>{const[u,m]=useState(!1);return _jsx(Button,{leftIcon:"message-question",...c,title:i,accessibilityLabel:n,testID:p,loading:u,onPress:()=>{(async()=>{m(!0);try{t(await captureAndReport({screenName:r,appVersion:s,category:o,subject:a}))}catch(i){e?.(i),t({category:o,subject:a,pageUrl:r,appVersion:s})}finally{m(!1)}})()}})};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{describeDevice
|
|
1
|
+
"use strict";import{describeDevice}from"../device.js";const viewShot=()=>require("react-native-view-shot");export const captureAndReport=async e=>{const t=await viewShot().captureScreen({format:"jpg",quality:.8,result:"tmpfile"});return{category:e.category,subject:e.subject,pageUrl:e.screenName,userAgent:describeDevice(),appVersion:e.appVersion,screenshots:[{uri:t,name:`screenshot-${Date.now()}.jpg`,type:"image/jpeg"}]}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{captureAndReport}from"./captureAndReport.js";export{ReportIssueButton}from"./ReportIssueButton.js";
|
|
1
|
+
"use strict";export{captureAndReport}from"./captureAndReport.js";export{ReportIssueButton}from"./ReportIssueButton.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{shareFile
|
|
1
|
+
"use strict";import{shareFile}from"@webority/mobile-core/share";export class SupportShareUnavailableError extends Error{constructor(){super("Opening files is not available on this device."),this.name="SupportShareUnavailableError"}}export const shareSupportFile=async r=>{try{await shareFile(r.uri,{mimeType:r.contentType,dialogTitle:r.name})}catch(r){const e=r;if("ShareError"===e?.name&&"unavailable"===e.code)throw new SupportShareUnavailableError;throw r}};
|
package/lib/module/types.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/lib/module/validation.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const validateCategory=(e,t)=>t.categories.includes(e)?void 0:"Choose one of the listed categories.";export const validateSubject=(e,t)=>{const a=e.trim();return a?a.length>t.subjectMaxLength?`Keep the subject under ${t.subjectMaxLength} characters.`:void 0:"Add a short subject."};export const validateBody=(e,t)=>{const a=e.trim();return a?a.length>t.bodyMaxLength?`Keep the message under ${t.bodyMaxLength} characters.`:void 0:"Write a message."};export const validateFiles=(e,t)=>{if(e.length>t.maxAttachmentsPerMessage)return`Attach up to ${t.maxAttachmentsPerMessage} files to one message.`;for(const a of e){if(t.allowedContentTypes.length>0&&!t.allowedContentTypes.includes(a.type))return`${a.name} can't be attached. Use an image, a PDF or a text file.`;if(void 0!==a.size&&a.size>t.maxAttachmentBytes){const e=Math.floor(t.maxAttachmentBytes/1048576);return`${a.name} is too large. The limit is ${e} MB per file.`}}};
|
|
1
|
+
"use strict";export const validateCategory=(e,t)=>t.categories.includes(e)?void 0:"Choose one of the listed categories.";export const validateSubject=(e,t)=>{const a=e.trim();return a?a.length>t.subjectMaxLength?`Keep the subject under ${t.subjectMaxLength} characters.`:void 0:"Add a short subject."};export const validateBody=(e,t)=>{const a=e.trim();return a?a.length>t.bodyMaxLength?`Keep the message under ${t.bodyMaxLength} characters.`:void 0:"Write a message."};export const validateFiles=(e,t)=>{if(e.length>t.maxAttachmentsPerMessage)return`Attach up to ${t.maxAttachmentsPerMessage} files to one message.`;for(const a of e){if(t.allowedContentTypes.length>0&&!t.allowedContentTypes.includes(a.type))return`${a.name} can't be attached. Use an image, a PDF or a text file.`;if(void 0!==a.size&&a.size>t.maxAttachmentBytes){const e=Math.floor(t.maxAttachmentBytes/1048576);return`${a.name} is too large. The limit is ${e} MB per file.`}}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/mobile-support",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Customer support tickets for Webority React Native apps: the support API client, hooks, ticket screens, contact card and a report-an-issue button that attaches a screenshot.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"typecheck": "tsc --noEmit"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@webority/mobile-core": ">=0.
|
|
74
|
-
"@webority/mobile-ui": ">=0.
|
|
73
|
+
"@webority/mobile-core": ">=0.2.0 <0.3.0",
|
|
74
|
+
"@webority/mobile-ui": ">=0.2.0 <0.3.0",
|
|
75
75
|
"expo": ">=57.0.0",
|
|
76
76
|
"expo-clipboard": ">=57.0.0",
|
|
77
77
|
"expo-file-system": ">=57.0.0",
|