@phonghq/go-chat 1.0.11 → 1.0.14

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 (65) hide show
  1. package/dist/assets/icons/IconAiCheck.vue.d.ts +2 -0
  2. package/dist/assets/icons/call/IconClose.vue.d.ts +2 -0
  3. package/dist/assets/icons/call/IconSoundDownload.vue.d.ts +2 -0
  4. package/dist/chat/App.vue.d.ts +7 -2
  5. package/dist/chat/page/customer-detail/CustomerDetail.vue.d.ts +1 -1
  6. package/dist/chat/page/home/ChatList.vue.d.ts +29 -1
  7. package/dist/chat/page/home/ChatMessage.vue.d.ts +2 -1
  8. package/dist/chat/page/home/Home.vue.d.ts +1 -1
  9. package/dist/components/chat/ScrollEvent/ScrollEvent.vue.d.ts +1 -0
  10. package/dist/components/chat/call/Calling.vue.d.ts +8 -2
  11. package/dist/components/chat/common/input/InputSearch.vue.d.ts +1 -1
  12. package/dist/components/chat/select/SelectBase.vue.d.ts +22 -0
  13. package/dist/components/common/drawer/DrawerBase.vue.d.ts +1 -1
  14. package/dist/components/common/modal/ModalBase.vue.d.ts +1 -1
  15. package/dist/composable/useCallHelper.d.ts +7 -2
  16. package/dist/composable/useInitData.d.ts +2 -4
  17. package/dist/composable/usePlivo.d.ts +9 -0
  18. package/dist/go-chat.es.js +14676 -12322
  19. package/dist/go-chat.umd.js +44 -14
  20. package/dist/plugins/websocket.d.ts +12 -2
  21. package/dist/router/index.d.ts +2 -0
  22. package/dist/style.css +1 -1
  23. package/dist/test/assets/icons/IconAiCheck.vue.js +28 -0
  24. package/dist/test/assets/icons/call/IconClose.vue.js +26 -0
  25. package/dist/test/assets/icons/call/IconMic.vue.js +9 -9
  26. package/dist/test/assets/icons/call/IconSoundDownload.vue.js +50 -0
  27. package/dist/test/chat/App.vue.js +144 -90
  28. package/dist/test/chat/page/customer-detail/CustomerDetail.vue.js +6 -5
  29. package/dist/test/chat/page/home/ChatList.vue.js +30 -9
  30. package/dist/test/chat/page/home/ChatMessage.vue.js +23 -12
  31. package/dist/test/chat/page/home/Home.vue.js +4 -3
  32. package/dist/test/chat/page/home/NewCustomer.vue.js +0 -12
  33. package/dist/test/components/chat/ScrollEvent/ScrollEvent.vue.js +7 -1
  34. package/dist/test/components/chat/call/Calling.vue.js +277 -111
  35. package/dist/test/components/chat/common/input/InputSearch.vue.js +2 -2
  36. package/dist/test/components/chat/select/SelectBase.vue.js +98 -0
  37. package/dist/test/components/common/drawer/DrawerBaseCustom.vue.js +0 -1
  38. package/dist/test/composable/data.json +32 -0
  39. package/dist/test/composable/useCallHelper.js +146 -33
  40. package/dist/test/composable/useDigibot.js +1 -1
  41. package/dist/test/composable/useInitData.js +17 -12
  42. package/dist/test/composable/usePlivo.js +138 -0
  43. package/dist/test/constant/color.js +1 -1
  44. package/dist/test/plugins/axios.js +2 -1
  45. package/dist/test/plugins/mqtt.js +11 -8
  46. package/dist/test/plugins/websocket.js +108 -19
  47. package/dist/test/router/index.js +39 -0
  48. package/dist/test/types/call.js +10 -1
  49. package/dist/test/utils/chat/auth.js +10 -2
  50. package/dist/test/utils/chat/call.js +48 -8
  51. package/dist/test/utils/chat/phone-string.js +4 -0
  52. package/dist/test/utils/chat/user.js +7 -2
  53. package/dist/test/views/NotFound.vue.js +47 -0
  54. package/dist/test/views/TenantPhone.vue.js +270 -0
  55. package/dist/types/call.d.ts +9 -0
  56. package/dist/types/chat/global.d.ts +4 -0
  57. package/dist/utils/chat/auth.d.ts +5 -1
  58. package/dist/utils/chat/call.d.ts +6 -2
  59. package/dist/utils/chat/phone-string.d.ts +1 -0
  60. package/dist/utils/chat/user.d.ts +4 -0
  61. package/dist/views/NotFound.vue.d.ts +2 -0
  62. package/dist/views/TenantPhone.vue.d.ts +2 -0
  63. package/package.json +2 -1
  64. package/dist/composable/TestSound.d.ts +0 -64
  65. package/dist/test/composable/TestSound.js +0 -196
@@ -2,3 +2,12 @@ export type IResCall = {
2
2
  from: string;
3
3
  to: string;
4
4
  };
5
+ export declare const enum PLIVO_CALL_STATUS {
6
+ CONNECTING = "Connecting...",
7
+ CALLING = "calling",
8
+ RINGING = "ringing",
9
+ CONNECT_FAILED = "failed",
10
+ CALL_START = "in-progress",
11
+ CALL_END = "completed",
12
+ NO_ANSWER = "no-answer"
13
+ }
@@ -1,3 +1,4 @@
1
+ import { ComputedRef } from 'vue';
1
2
  export type GoChatProps = {
2
3
  token?: string;
3
4
  id?: string;
@@ -5,4 +6,7 @@ export type GoChatProps = {
5
6
  response?: PAGE_RESPONSE;
6
7
  isLib?: boolean;
7
8
  };
9
+ export interface GoChatInstance {
10
+ unreadCount: ComputedRef<number>;
11
+ }
8
12
  export type PAGE_RESPONSE = 'mobile' | 'tablet';
@@ -40,6 +40,10 @@ export declare const dataLoginLink: import("vue").Ref<{
40
40
  export declare const setDataLogin: (id: string, token: string, domain: string) => void;
41
41
  export declare const loginTenant: (body: IBodyLoginTenant) => Promise<any>;
42
42
  export declare const loginLink: (params: IPramsLoginLink) => Promise<any>;
43
- export declare const getProfile: () => Promise<any>;
43
+ export declare const getProfile: () => Promise<IResProfile>;
44
+ export declare const submitTenantPhone: (body: {
45
+ phone: string;
46
+ tenant_id: string;
47
+ }) => Promise<any>;
44
48
  export declare const logout: () => Promise<void>;
45
49
  export {};
@@ -1,7 +1,11 @@
1
1
  import type { IResUser } from '../../types/message';
2
- import type { IResCall } from '../../types/call';
3
2
  export declare const getIceService: () => Promise<import("axios").AxiosResponse<any, any>>;
4
- export declare const callClient: (body: IResCall) => Promise<void>;
3
+ export declare const callClient: (data: {
4
+ call_uuid: string;
5
+ clientId: string;
6
+ }) => Promise<void>;
5
7
  export declare const callOutBound: (user: IResUser) => Promise<void>;
6
8
  export declare const plivoCall: (user: IResUser) => Promise<any>;
7
9
  export declare const plivoEndCall: (uuid: string) => Promise<void>;
10
+ export declare const downloadRecord: (url_pub: string) => Promise<void>;
11
+ export declare const getPlivoAccessToken: () => Promise<any>;
@@ -0,0 +1 @@
1
+ export declare const formatPhone10number: (phone: string, dial: string) => string;
@@ -187,6 +187,10 @@ export declare const userHistory: import("vue").Ref<{
187
187
  visit_count: number;
188
188
  } | null>;
189
189
  export declare const getUserHistory: (phone: string) => Promise<GetCustomerHistoryResponse>;
190
+ export declare const getUserDetailChat: (params: {
191
+ phone: string;
192
+ client_id: string;
193
+ }) => Promise<any>;
190
194
  export declare function getItemsByYear<T = any>(data: any, key?: string): {
191
195
  year: string;
192
196
  items: T[];
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phonghq/go-chat",
3
- "version": "1.0.11",
3
+ "version": "1.0.14",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist"
@@ -34,6 +34,7 @@
34
34
  "lucide-vue-next": "^0.536.0",
35
35
  "mqtt": "^4.3.7",
36
36
  "pinia": "^2.1.7",
37
+ "plivo-browser-sdk": "^2.2.21",
37
38
  "prettier": "3.3.3",
38
39
  "reka-ui": "^2.6.0",
39
40
  "tailwind-merge": "^3.3.1",
@@ -1,64 +0,0 @@
1
- export declare function useAudioStream(wsUrl: string): {
2
- ws: import("vue").Ref<{
3
- binaryType: BinaryType;
4
- readonly bufferedAmount: number;
5
- readonly extensions: string;
6
- onclose: ((this: WebSocket, ev: CloseEvent) => any) | null;
7
- onerror: ((this: WebSocket, ev: Event) => any) | null;
8
- onmessage: ((this: WebSocket, ev: MessageEvent<any>) => any) | null;
9
- onopen: ((this: WebSocket, ev: Event) => any) | null;
10
- readonly protocol: string;
11
- readonly readyState: number;
12
- readonly url: string;
13
- close: (code?: number | undefined, reason?: string | undefined) => void;
14
- send: (data: string | ArrayBufferView | Blob | ArrayBufferLike) => void;
15
- readonly CONNECTING: 0;
16
- readonly OPEN: 1;
17
- readonly CLOSING: 2;
18
- readonly CLOSED: 3;
19
- addEventListener: {
20
- <K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void;
21
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void;
22
- };
23
- removeEventListener: {
24
- <K_1 extends keyof WebSocketEventMap>(type: K_1, listener: (this: WebSocket, ev: WebSocketEventMap[K_1]) => any, options?: boolean | EventListenerOptions | undefined): void;
25
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions | undefined): void;
26
- };
27
- dispatchEvent: (event: Event) => boolean;
28
- } | null, WebSocket | {
29
- binaryType: BinaryType;
30
- readonly bufferedAmount: number;
31
- readonly extensions: string;
32
- onclose: ((this: WebSocket, ev: CloseEvent) => any) | null;
33
- onerror: ((this: WebSocket, ev: Event) => any) | null;
34
- onmessage: ((this: WebSocket, ev: MessageEvent<any>) => any) | null;
35
- onopen: ((this: WebSocket, ev: Event) => any) | null;
36
- readonly protocol: string;
37
- readonly readyState: number;
38
- readonly url: string;
39
- close: (code?: number | undefined, reason?: string | undefined) => void;
40
- send: (data: string | ArrayBufferView | Blob | ArrayBufferLike) => void;
41
- readonly CONNECTING: 0;
42
- readonly OPEN: 1;
43
- readonly CLOSING: 2;
44
- readonly CLOSED: 3;
45
- addEventListener: {
46
- <K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void;
47
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void;
48
- };
49
- removeEventListener: {
50
- <K_1 extends keyof WebSocketEventMap>(type: K_1, listener: (this: WebSocket, ev: WebSocketEventMap[K_1]) => any, options?: boolean | EventListenerOptions | undefined): void;
51
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions | undefined): void;
52
- };
53
- dispatchEvent: (event: Event) => boolean;
54
- } | null>;
55
- statusText: import("vue").Ref<string, string>;
56
- micLevel: import("vue").Ref<number, number>;
57
- recording: import("vue").Ref<boolean, boolean>;
58
- connect: () => void;
59
- disconnect: () => void;
60
- resumeAudio: () => void;
61
- enqueueSpeakerChunk: (arrayBuffer: ArrayBuffer) => Promise<void>;
62
- processSpeakerQueue: () => void;
63
- startRecording: () => Promise<void>;
64
- };
@@ -1,196 +0,0 @@
1
- import { ref, onBeforeUnmount } from 'vue';
2
- export function useAudioStream(wsUrl) {
3
- const SAMPLE_RATE = 24000;
4
- const CHUNK_SIZE = 480;
5
- const PREBUFFER_SEC = 0.4;
6
- let audioCtx;
7
- let processor;
8
- let input;
9
- let stream;
10
- const ws = ref(null);
11
- const statusText = ref('Tap to Speak with Vico');
12
- // Speaker queue
13
- let speakerQueue = [];
14
- let nextPlayTime = 0;
15
- // UI state
16
- const recording = ref(false);
17
- const micLevel = ref(0);
18
- // Status management
19
- const STATUS = {
20
- IDLE: 'Tap to Speak with Vico',
21
- CONNECTING: 'Connecting...',
22
- LISTENING: 'Listening...',
23
- SPEAKING: 'Speaking...'
24
- };
25
- let currentStatus = STATUS.IDLE;
26
- function setStatus(newStatus) {
27
- if (currentStatus !== newStatus) {
28
- currentStatus = newStatus;
29
- statusText.value = newStatus;
30
- }
31
- }
32
- // 🎤 Float32 → PCM16
33
- function floatTo16BitPCM(float32Array) {
34
- const buffer = new ArrayBuffer(float32Array.length * 2);
35
- const view = new DataView(buffer);
36
- for (let i = 0; i < float32Array.length; i++) {
37
- let s = Math.max(-1, Math.min(1, float32Array[i]));
38
- view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);
39
- }
40
- return buffer;
41
- }
42
- // 🔊 PCM16 → Float32
43
- function int16ToFloat32(int16Array) {
44
- const float32 = new Float32Array(int16Array.length);
45
- for (let i = 0; i < int16Array.length; i++) {
46
- float32[i] = int16Array[i] / 32768;
47
- }
48
- return float32;
49
- }
50
- // 📥 enqueue speaker chunk
51
- async function enqueueSpeakerChunk(arrayBuffer) {
52
- const int16View = new Int16Array(arrayBuffer);
53
- const float32Data = int16ToFloat32(int16View);
54
- speakerQueue.push(float32Data);
55
- }
56
- // 🔊 process queue
57
- function processSpeakerQueue() {
58
- try {
59
- if (speakerQueue.length > 0) {
60
- const chunk = speakerQueue.shift();
61
- if (chunk) {
62
- const audioBuffer = audioCtx.createBuffer(1, chunk.length, SAMPLE_RATE);
63
- audioBuffer.getChannelData(0).set(chunk);
64
- const source = audioCtx.createBufferSource();
65
- source.buffer = audioBuffer;
66
- source.connect(audioCtx.destination);
67
- if (nextPlayTime < audioCtx.currentTime + 0.05) {
68
- nextPlayTime = audioCtx.currentTime + PREBUFFER_SEC;
69
- }
70
- source.start();
71
- nextPlayTime += audioBuffer.duration;
72
- setStatus(STATUS.SPEAKING);
73
- }
74
- }
75
- else if (recording.value) {
76
- setStatus(STATUS.LISTENING);
77
- }
78
- }
79
- catch (e) {
80
- console.log(e);
81
- }
82
- requestAnimationFrame(processSpeakerQueue);
83
- }
84
- // 🎤 start mic
85
- async function startRecording() {
86
- audioCtx = new AudioContext({ sampleRate: SAMPLE_RATE });
87
- return;
88
- stream = await navigator.mediaDevices.getUserMedia({ audio: true });
89
- input = audioCtx.createMediaStreamSource(stream);
90
- processor = audioCtx.createScriptProcessor(1024, 1, 1);
91
- processor.onaudioprocess = (e) => {
92
- if (!ws.value || ws.value.readyState !== WebSocket.OPEN)
93
- return;
94
- const inputData = e.inputBuffer.getChannelData(0);
95
- // calculate mic level
96
- let sum = 0;
97
- for (let i = 0; i < inputData.length; i++)
98
- sum += inputData[i] ** 2;
99
- micLevel.value = Math.sqrt(sum / inputData.length);
100
- // chunking & send
101
- for (let i = 0; i < inputData.length; i += CHUNK_SIZE) {
102
- const slice = inputData.slice(i, i + CHUNK_SIZE);
103
- const binaryChunk = floatTo16BitPCM(slice);
104
- ws.value.send(binaryChunk);
105
- }
106
- };
107
- input.connect(processor);
108
- processor.connect(audioCtx.destination);
109
- recording.value = true;
110
- setStatus(STATUS.LISTENING);
111
- }
112
- // ⏹ stop mic
113
- function stopRecording() {
114
- recording.value = false;
115
- processor?.disconnect();
116
- input?.disconnect();
117
- stream?.getTracks().forEach((t) => t.stop());
118
- if (audioCtx && audioCtx.state !== 'closed') {
119
- audioCtx
120
- .close()
121
- .then(() => console.log('AudioContext closed successfully.'))
122
- .catch((err) => console.error('Error closing AudioContext:', err))
123
- .finally(() => (micLevel.value = 0));
124
- }
125
- setStatus(STATUS.IDLE);
126
- }
127
- const getAudioContext = () => {
128
- if (!audioCtx || audioCtx.state === 'closed') {
129
- audioCtx = new AudioContext({ sampleRate: SAMPLE_RATE });
130
- }
131
- return audioCtx;
132
- };
133
- const safeResumeAudio = () => {
134
- const ctx = getAudioContext();
135
- if (ctx.state === 'suspended') {
136
- ctx.resume().catch((err) => console.error('Error resuming AudioContext:', err));
137
- }
138
- };
139
- function connect() {
140
- if (ws.value && ws.value.readyState === WebSocket.OPEN)
141
- return;
142
- setStatus(STATUS.CONNECTING);
143
- ws.value = new WebSocket(wsUrl);
144
- ws.value.binaryType = 'arraybuffer';
145
- ws.value.onopen = () => {
146
- console.log('✅ WS connected');
147
- startRecording();
148
- processSpeakerQueue();
149
- };
150
- ws.value.onmessage = (event) => {
151
- if (event.data instanceof ArrayBuffer) {
152
- enqueueSpeakerChunk(event.data);
153
- return;
154
- }
155
- if (typeof event.data === 'string') {
156
- try {
157
- const msg = JSON.parse(event.data);
158
- if (msg.type === 'AudioStop' || msg.code === 'UserStartedSpeaking') {
159
- // speakerQueue.length = 0
160
- nextPlayTime = 0;
161
- setStatus(STATUS.LISTENING);
162
- return;
163
- }
164
- }
165
- catch (err) {
166
- console.warn('⚠️ Parse JSON error:', err, event.data);
167
- }
168
- }
169
- console.log('⚠️ Unknown WS message, closing...');
170
- disconnect();
171
- };
172
- ws.value.onclose = () => {
173
- console.log('❌ WS closed');
174
- stopRecording();
175
- };
176
- }
177
- function disconnect() {
178
- ws.value?.close();
179
- stopRecording();
180
- }
181
- onBeforeUnmount(() => {
182
- disconnect();
183
- });
184
- return {
185
- ws,
186
- statusText,
187
- micLevel,
188
- recording,
189
- connect,
190
- disconnect,
191
- resumeAudio: safeResumeAudio,
192
- enqueueSpeakerChunk,
193
- processSpeakerQueue,
194
- startRecording
195
- };
196
- }