opendesk-react-native 0.1.2 → 0.1.6
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/README.md +157 -133
- package/assets/chime.wav +0 -0
- package/lib/api.js +25 -8
- package/lib/client.d.ts +9 -0
- package/lib/client.js +14 -0
- package/lib/errors.d.ts +6 -2
- package/lib/errors.js +6 -2
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/realtime.d.ts +5 -0
- package/lib/realtime.js +9 -1
- package/lib/sound.d.ts +22 -0
- package/lib/sound.js +33 -0
- package/lib/types.d.ts +2 -0
- package/lib/ui/OpenDeskChat.d.ts +5 -1
- package/lib/ui/OpenDeskChat.js +41 -11
- package/lib/ui/keyboard.d.ts +15 -0
- package/lib/ui/keyboard.js +62 -0
- package/lib/ui/strings.d.ts +2 -0
- package/lib/ui/strings.js +2 -0
- package/package.json +46 -45
- package/src/api.ts +195 -172
- package/src/assets.d.ts +5 -0
- package/src/client.ts +680 -659
- package/src/errors.ts +20 -16
- package/src/index.ts +27 -25
- package/src/realtime.ts +174 -166
- package/src/sound.ts +40 -0
- package/src/types.ts +202 -200
- package/src/ui/OpenDeskChat.tsx +1408 -1318
- package/src/ui/keyboard.ts +68 -0
- package/src/ui/strings.ts +59 -57
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, type RefObject } from 'react';
|
|
2
|
+
import { Keyboard, LayoutAnimation, Platform, type KeyboardEvent, type View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How far the keyboard covers the view in `ref`, for use as its bottom padding.
|
|
6
|
+
*
|
|
7
|
+
* KeyboardAvoidingView compares the keyboard with a layout relative to its parent, so it misses
|
|
8
|
+
* anything above that parent (a header, a sheet), and on Android it relies on the window
|
|
9
|
+
* shrinking, which edge-to-edge apps no longer do. This measures the view in window coordinates
|
|
10
|
+
* instead and works the same on both platforms. Call `onLayout` from the view's `onLayout` so a
|
|
11
|
+
* window that does shrink is measured again and the padding drops back.
|
|
12
|
+
*/
|
|
13
|
+
export function useKeyboardInset(ref: RefObject<View | null>, extraOffset = 0): { inset: number; onLayout: () => void } {
|
|
14
|
+
const [inset, setInset] = useState(0);
|
|
15
|
+
const keyboard = useRef<KeyboardEvent | null>(null);
|
|
16
|
+
|
|
17
|
+
const measure = useCallback(() => {
|
|
18
|
+
const event = keyboard.current;
|
|
19
|
+
const view = ref.current;
|
|
20
|
+
if (!event || !view) {
|
|
21
|
+
setInset(0);
|
|
22
|
+
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
view.measureInWindow((_x, y, _width, height) => {
|
|
26
|
+
if (keyboard.current !== event) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const next = Math.max(0, Math.round(y + height - event.endCoordinates.screenY + extraOffset));
|
|
30
|
+
setInset((current) => {
|
|
31
|
+
if (current !== next) {
|
|
32
|
+
animateWithKeyboard(event);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return next;
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}, [ref, extraOffset]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const show = (event: KeyboardEvent) => {
|
|
42
|
+
keyboard.current = event;
|
|
43
|
+
measure();
|
|
44
|
+
};
|
|
45
|
+
const hide = (event: KeyboardEvent) => {
|
|
46
|
+
keyboard.current = null;
|
|
47
|
+
animateWithKeyboard(event);
|
|
48
|
+
setInset(0);
|
|
49
|
+
};
|
|
50
|
+
const subscriptions =
|
|
51
|
+
Platform.OS === 'ios'
|
|
52
|
+
? [Keyboard.addListener('keyboardWillChangeFrame', show), Keyboard.addListener('keyboardWillHide', hide)]
|
|
53
|
+
: [Keyboard.addListener('keyboardDidShow', show), Keyboard.addListener('keyboardDidHide', hide)];
|
|
54
|
+
|
|
55
|
+
return () => subscriptions.forEach((subscription) => subscription.remove());
|
|
56
|
+
}, [measure]);
|
|
57
|
+
|
|
58
|
+
return { inset, onLayout: measure };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** iOS reports the keyboard's own curve, so the padding can move in step with it. */
|
|
62
|
+
function animateWithKeyboard(event: KeyboardEvent | null | undefined): void {
|
|
63
|
+
if (Platform.OS !== 'ios' || !event?.duration) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const type = LayoutAnimation.Types[event.easing] ?? LayoutAnimation.Types.keyboard;
|
|
67
|
+
LayoutAnimation.configureNext({ duration: event.duration, update: { duration: event.duration, type } });
|
|
68
|
+
}
|
package/src/ui/strings.ts
CHANGED
|
@@ -1,57 +1,59 @@
|
|
|
1
|
-
/** The web widget's English copy, so both channels read the same. Override any key via `strings`. */
|
|
2
|
-
export const defaultStrings = {
|
|
3
|
-
support: 'Support',
|
|
4
|
-
closeChat: 'Close chat',
|
|
5
|
-
greeting: 'Hi there. How can we help?',
|
|
6
|
-
replyTime: 'We usually reply within a few hours.',
|
|
7
|
-
replyTimeAi: '{name} answers right away, the team within a few hours.',
|
|
8
|
-
unavailable: 'Chat is unavailable',
|
|
9
|
-
unavailableHint: 'Check your connection and try again later.',
|
|
10
|
-
sendMessage: 'Send us a message',
|
|
11
|
-
yourConversations: 'Your conversations',
|
|
12
|
-
conversation: 'Conversation',
|
|
13
|
-
typing: '{name} is typing…',
|
|
14
|
-
botTyping: '{name} is writing…',
|
|
15
|
-
searchHelp: 'Search the help center',
|
|
16
|
-
poweredBy: 'Powered by opendesk',
|
|
17
|
-
back: 'Back to conversations',
|
|
18
|
-
emptyHint: "Ask us anything. We'll reply here, and by email if you leave one.",
|
|
19
|
-
notSent: 'Not sent.',
|
|
20
|
-
sending: 'Sending…',
|
|
21
|
-
uploading: 'Uploading…',
|
|
22
|
-
removeFile: 'Remove {name}',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
}
|
|
1
|
+
/** The web widget's English copy, so both channels read the same. Override any key via `strings`. */
|
|
2
|
+
export const defaultStrings = {
|
|
3
|
+
support: 'Support',
|
|
4
|
+
closeChat: 'Close chat',
|
|
5
|
+
greeting: 'Hi there. How can we help?',
|
|
6
|
+
replyTime: 'We usually reply within a few hours.',
|
|
7
|
+
replyTimeAi: '{name} answers right away, the team within a few hours.',
|
|
8
|
+
unavailable: 'Chat is unavailable',
|
|
9
|
+
unavailableHint: 'Check your connection and try again later.',
|
|
10
|
+
sendMessage: 'Send us a message',
|
|
11
|
+
yourConversations: 'Your conversations',
|
|
12
|
+
conversation: 'Conversation',
|
|
13
|
+
typing: '{name} is typing…',
|
|
14
|
+
botTyping: '{name} is writing…',
|
|
15
|
+
searchHelp: 'Search the help center',
|
|
16
|
+
poweredBy: 'Powered by opendesk',
|
|
17
|
+
back: 'Back to conversations',
|
|
18
|
+
emptyHint: "Ask us anything. We'll reply here, and by email if you leave one.",
|
|
19
|
+
notSent: 'Not sent.',
|
|
20
|
+
sending: 'Sending…',
|
|
21
|
+
uploading: 'Uploading…',
|
|
22
|
+
removeFile: 'Remove {name}',
|
|
23
|
+
viewImage: 'View {name}',
|
|
24
|
+
closeImage: 'Close image',
|
|
25
|
+
fileTypeRefused: "{name} can't be sent here. Try another file type.",
|
|
26
|
+
fileTooLarge: '{name} is larger than 25 MB.',
|
|
27
|
+
uploadFailed: "Couldn't upload {name}. Try again.",
|
|
28
|
+
placeholder: 'Write a message…',
|
|
29
|
+
attach: 'Attach files',
|
|
30
|
+
send: 'Send',
|
|
31
|
+
ai: 'AI',
|
|
32
|
+
talkToPerson: 'Talk to a person',
|
|
33
|
+
handoffFailed: "Couldn't reach the team. Try again.",
|
|
34
|
+
handedOff: 'A person will reply here',
|
|
35
|
+
handedOffHint: "You'll see their reply in this conversation, and by email if you left one.",
|
|
36
|
+
checking: 'Checking {tool}…',
|
|
37
|
+
checked: 'Checked {tool}',
|
|
38
|
+
toolFailed: "Couldn't check {tool}",
|
|
39
|
+
actionPending: 'Needs your confirmation',
|
|
40
|
+
confirm: 'Confirm',
|
|
41
|
+
cancel: 'Cancel',
|
|
42
|
+
actionConfirmed: 'Confirmed',
|
|
43
|
+
actionDeclined: 'Cancelled',
|
|
44
|
+
actionExpired: 'Expired',
|
|
45
|
+
actionFailed: "Didn't go through",
|
|
46
|
+
topicsTitle: 'What can we help with?',
|
|
47
|
+
topicsHint: 'Pick a topic so your message reaches the right people.',
|
|
48
|
+
placeholderTopic: 'Pick a topic to start',
|
|
49
|
+
changeTopic: 'Change topic',
|
|
50
|
+
offlineDefault: "We're away right now. Leave a message and we'll reply as soon as we're back.",
|
|
51
|
+
today: 'Today',
|
|
52
|
+
yesterday: 'Yesterday',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type ChatStrings = typeof defaultStrings;
|
|
56
|
+
|
|
57
|
+
export function format(template: string, values: Record<string, string | number> = {}): string {
|
|
58
|
+
return template.replace(/\{(\w+)\}/g, (match, key: string) => (key in values ? String(values[key]) : match));
|
|
59
|
+
}
|