@phonghq/go-chat 1.0.10 → 1.0.13

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 (56) 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 +1 -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/composable/useCallHelper.d.ts +6 -2
  14. package/dist/composable/useInitData.d.ts +2 -4
  15. package/dist/go-chat.es.js +24960 -23685
  16. package/dist/go-chat.umd.js +42 -16
  17. package/dist/plugins/websocket.d.ts +12 -2
  18. package/dist/router/index.d.ts +2 -0
  19. package/dist/style.css +1 -1
  20. package/dist/test/assets/icons/IconAiCheck.vue.js +28 -0
  21. package/dist/test/assets/icons/call/IconClose.vue.js +26 -0
  22. package/dist/test/assets/icons/call/IconSoundDownload.vue.js +50 -0
  23. package/dist/test/chat/App.vue.js +145 -90
  24. package/dist/test/chat/page/customer-detail/CustomerDetail.vue.js +6 -5
  25. package/dist/test/chat/page/home/ChatList.vue.js +30 -9
  26. package/dist/test/chat/page/home/ChatMessage.vue.js +17 -9
  27. package/dist/test/chat/page/home/Home.vue.js +3 -3
  28. package/dist/test/chat/page/home/NewCustomer.vue.js +0 -12
  29. package/dist/test/components/chat/ScrollEvent/ScrollEvent.vue.js +7 -1
  30. package/dist/test/components/chat/call/Calling.vue.js +198 -76
  31. package/dist/test/components/chat/common/input/InputSearch.vue.js +2 -2
  32. package/dist/test/components/chat/select/SelectBase.vue.js +98 -0
  33. package/dist/test/components/common/drawer/DrawerBaseCustom.vue.js +0 -1
  34. package/dist/test/composable/data.json +32 -0
  35. package/dist/test/composable/useCallHelper.js +143 -33
  36. package/dist/test/composable/useDigibot.js +1 -1
  37. package/dist/test/composable/useInitData.js +17 -12
  38. package/dist/test/constant/color.js +1 -1
  39. package/dist/test/plugins/axios.js +2 -1
  40. package/dist/test/plugins/mqtt.js +11 -5
  41. package/dist/test/plugins/websocket.js +84 -4
  42. package/dist/test/router/index.js +39 -0
  43. package/dist/test/utils/chat/auth.js +10 -2
  44. package/dist/test/utils/chat/call.js +32 -5
  45. package/dist/test/utils/chat/phone-string.js +4 -0
  46. package/dist/test/utils/chat/user.js +7 -2
  47. package/dist/test/views/NotFound.vue.js +47 -0
  48. package/dist/test/views/TenantPhone.vue.js +270 -0
  49. package/dist/types/chat/global.d.ts +4 -0
  50. package/dist/utils/chat/auth.d.ts +5 -1
  51. package/dist/utils/chat/call.d.ts +5 -2
  52. package/dist/utils/chat/phone-string.d.ts +1 -0
  53. package/dist/utils/chat/user.d.ts +4 -0
  54. package/dist/views/NotFound.vue.d.ts +2 -0
  55. package/dist/views/TenantPhone.vue.d.ts +2 -0
  56. package/package.json +1 -1
@@ -1,14 +1,18 @@
1
- import { plivoCall, plivoEndCall } from '../utils/chat/call';
1
+ import { callClient, plivoCall, plivoEndCall } from '../utils/chat/call';
2
+ import { dataProfile } from '../utils/chat/auth';
2
3
  import { ref } from 'vue';
3
- import { socketSend } from '../plugins/websocket';
4
+ import { WebSocketClient } from '../plugins/websocket';
5
+ import dataJson from './data.json';
4
6
  export function useCallHelper() {
5
- function _getAudioContext() {
7
+ function _getAudioContext(sampleRate) {
6
8
  return new (window.AudioContext || window.webkitAudioContext)({
7
- sampleRate: SAMPLE_RATE
9
+ sampleRate: sampleRate ?? PLIVO_SAMPLE_RATE
8
10
  });
9
11
  }
10
- const SAMPLE_RATE = 8000;
11
- let uuid = '';
12
+ const PLIVO_SAMPLE_RATE = 8000;
13
+ const SAMPLE_RATE = 48000;
14
+ const CHUNK_LENGTH = 160;
15
+ const uuid = ref('');
12
16
  let audioCtxListen = _getAudioContext();
13
17
  let audioCtxCall = _getAudioContext();
14
18
  let prebuffer = [];
@@ -17,7 +21,10 @@ export function useCallHelper() {
17
21
  let sourceCall;
18
22
  let loopTimer = null;
19
23
  let running = false;
24
+ let loopId = null;
20
25
  const userRemoter = ref(null);
26
+ let websocket = null;
27
+ let stream;
21
28
  function _int16ToFloat32(int16Array) {
22
29
  const float32 = new Float32Array(int16Array.length);
23
30
  for (let i = 0; i < int16Array.length; i++) {
@@ -114,34 +121,54 @@ export function useCallHelper() {
114
121
  }
115
122
  return int16Array;
116
123
  }
124
+ function _resample8kTo48k(float8k) {
125
+ const ratio = 48000 / 8000;
126
+ const float48k = new Float32Array(float8k.length * ratio);
127
+ for (let i = 0; i < float48k.length; i++) {
128
+ float48k[i] = float8k[Math.floor(i / ratio)] || 0;
129
+ }
130
+ return float48k;
131
+ }
117
132
  const call = async (user) => {
118
133
  userRemoter.value = user;
119
- uuid = '';
134
+ uuid.value = '';
120
135
  const res = await plivoCall(user);
121
- console.log(res);
122
- uuid = res?.call?.requestUuid;
136
+ uuid.value = res?.call?.requestUuid;
123
137
  };
124
- const end = async (link) => {
138
+ const end = async () => {
125
139
  processorCall?.disconnect?.();
126
140
  sourceCall?.disconnect?.(processorCall);
127
141
  stopQueue();
128
- console.log(uuid);
142
+ websocket?.disconnect();
143
+ stream?.getTracks?.()?.forEach((track) => track?.stop?.());
129
144
  if (!uuid)
130
145
  return;
131
- await plivoEndCall(uuid);
146
+ await plivoEndCall(uuid.value);
132
147
  return;
133
148
  };
134
149
  const startPeerConnection = async () => {
135
150
  try {
136
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
151
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true });
137
152
  sourceCall = audioCtxCall.createMediaStreamSource(stream);
138
- processorCall: ScriptProcessorNode = audioCtxCall.createScriptProcessor(256, 1, 1);
153
+ processorCall = audioCtxCall.createScriptProcessor(256, 1, 1);
139
154
  processorCall.onaudioprocess = (e) => {
140
155
  const input = e.inputBuffer.getChannelData(0);
141
- for (let i = 0; i < input.length; i += 160) {
142
- const slice = input.slice(i, i + 160);
156
+ for (let i = 0; i < input.length; i += CHUNK_LENGTH) {
157
+ const slice = input.slice(i, i + CHUNK_LENGTH);
143
158
  const binaryChunk = _float32ToMuLaw8(slice);
144
- socketSend(binaryChunk);
159
+ // socketSend(binaryChunk)
160
+ // const data = {
161
+ // event: 'testPlaySound',
162
+ // media: {
163
+ // payload: binaryChunk
164
+ // }
165
+ // }
166
+ // console.log(data)
167
+ // addQueueListen(binaryChunk)
168
+ // socketSend(binaryChunk)
169
+ // websocket.binaryType = "arraybuffer";
170
+ // console.log(binaryChunk)
171
+ websocket.sendDefault(binaryChunk);
145
172
  }
146
173
  };
147
174
  sourceCall.connect(processorCall);
@@ -165,54 +192,133 @@ export function useCallHelper() {
165
192
  try {
166
193
  const chunk = prebuffer.shift();
167
194
  if (chunk) {
168
- const audioBuffer = audioCtxListen.createBuffer(1, chunk.length, 8000);
195
+ const float48k = _resample8kTo48k(chunk);
196
+ const audioBuffer = audioCtxListen.createBuffer(1, chunk.length, PLIVO_SAMPLE_RATE);
169
197
  audioBuffer.getChannelData(0).set(chunk);
170
198
  const source = audioCtxListen.createBufferSource();
171
199
  source.buffer = audioBuffer;
172
200
  source.connect(audioCtxListen.destination);
201
+ // console.log(
202
+ // 'Scheduling chunk',
203
+ // 'nextPlayTime:', nextPlayTime,
204
+ // 'currentTime:', audioCtxListen.currentTime
205
+ // );
206
+ if (nextPlayTime < audioCtxListen.currentTime + (CHUNK_LENGTH / PLIVO_SAMPLE_RATE)) {
207
+ nextPlayTime = audioCtxListen.currentTime + (CHUNK_LENGTH / PLIVO_SAMPLE_RATE);
208
+ }
173
209
  source.start(nextPlayTime);
174
- nextPlayTime = audioCtxListen.currentTime + chunk.length / SAMPLE_RATE;
210
+ // console.log(
211
+ // 'Started chunk',
212
+ // 'sourceStartTime:', nextPlayTime,
213
+ // 'audioCtxCurrentTime:', audioCtxListen.currentTime
214
+ // );
215
+ // nextPlayTime += audioBuffer.duration
216
+ nextPlayTime += audioBuffer.duration;
175
217
  }
176
218
  }
177
219
  catch (e) {
178
220
  console.log(e);
179
221
  }
180
222
  }
223
+ let startTime = 0;
181
224
  const playQueueLoop = () => {
182
225
  if (!running)
183
226
  return;
184
227
  while (prebuffer.length > 0 && nextPlayTime < audioCtxListen.currentTime + 0.1) {
185
228
  scheduleNext();
186
229
  }
187
- loopTimer = setTimeout(playQueueLoop, 10);
230
+ loopId = requestAnimationFrame(playQueueLoop);
188
231
  };
232
+ async function processSpeakerQueue() {
233
+ try {
234
+ // console.log(prebuffer)
235
+ if (prebuffer.length > 0) {
236
+ const chunk = prebuffer.shift();
237
+ if (chunk) {
238
+ // console.log(chunk.length)
239
+ const audioBuffer = audioCtxListen.createBuffer(1, chunk.length, 8000);
240
+ audioBuffer.getChannelData(0).set(chunk);
241
+ const source = audioCtxListen.createBufferSource();
242
+ source.buffer = audioBuffer;
243
+ source.connect(audioCtxListen.destination);
244
+ if (nextPlayTime < audioCtxListen.currentTime + audioBuffer.duration) {
245
+ nextPlayTime = audioCtxListen.currentTime + audioBuffer.duration;
246
+ }
247
+ source.start(nextPlayTime);
248
+ // console.log(`🎧 Played samples (${audioBuffer.duration.toFixed(3)}s)`);
249
+ nextPlayTime += audioBuffer.duration;
250
+ }
251
+ }
252
+ }
253
+ catch (e) {
254
+ console.log(e);
255
+ }
256
+ requestAnimationFrame(processSpeakerQueue);
257
+ }
189
258
  function stopQueue() {
190
259
  running = false;
191
260
  if (loopTimer)
192
261
  clearTimeout(loopTimer);
262
+ if (loopId !== null) {
263
+ cancelAnimationFrame(loopId);
264
+ loopId = null;
265
+ }
193
266
  prebuffer.length = 0;
194
267
  }
195
268
  const handleMedia = async (message) => {
196
269
  addQueueListen(message);
197
270
  };
198
- const startIncomingCall = async () => {
199
- if (!audioCtxCall) {
200
- audioCtxCall = _getAudioContext();
271
+ const handleMqttMessage = async (message) => {
272
+ handleMedia(message);
273
+ };
274
+ const startIncomingCall = async (uuid_request) => {
275
+ try {
276
+ if (uuid_request && uuid_request != uuid.value) {
277
+ throw new Error('This call isn’t yours');
278
+ }
279
+ websocket = new WebSocketClient(uuid_request, handleMqttMessage);
280
+ await websocket?.init();
281
+ if (!audioCtxCall) {
282
+ audioCtxCall = _getAudioContext();
283
+ }
284
+ if (audioCtxCall.state === 'suspended')
285
+ await audioCtxCall.resume();
286
+ await startPeerConnection();
287
+ if (running)
288
+ return;
289
+ running = true;
290
+ if (!audioCtxListen) {
291
+ audioCtxListen = _getAudioContext();
292
+ }
293
+ if (audioCtxListen.state === 'suspended')
294
+ await audioCtxListen.resume();
295
+ nextPlayTime = audioCtxListen.currentTime;
296
+ playQueueLoop();
297
+ // processSpeakerQueue()
298
+ }
299
+ catch (e) {
300
+ end();
301
+ throw new Error(e);
302
+ }
303
+ };
304
+ const testPlay = () => {
305
+ for (let i = 0; i < dataJson.length; i++) {
306
+ addQueueListen(dataJson[i]);
201
307
  }
202
- if (audioCtxCall.state === 'suspended')
203
- await audioCtxCall.resume();
204
- await startPeerConnection();
205
308
  if (running)
206
309
  return;
207
310
  running = true;
208
- if (!audioCtxListen) {
209
- audioCtxListen = _getAudioContext();
210
- }
211
- if (audioCtxListen.state === 'suspended')
212
- await audioCtxListen.resume();
213
- nextPlayTime = audioCtxListen.currentTime;
214
311
  playQueueLoop();
215
312
  };
313
+ const handleCallAnswer = (data) => {
314
+ uuid.value = data?.data?.call_uuid ?? '';
315
+ };
316
+ const callAnswer = async (uuid_request) => {
317
+ if (uuid_request && uuid_request != uuid.value) {
318
+ throw new Error('This call isn’t yours');
319
+ }
320
+ await callClient({ call_uuid: uuid.value, clientId: dataProfile.value?.tenant_id ?? '' });
321
+ };
216
322
  return {
217
323
  call,
218
324
  end,
@@ -221,6 +327,10 @@ export function useCallHelper() {
221
327
  sendOfferOk,
222
328
  handleMedia,
223
329
  userRemoter,
224
- startIncomingCall
330
+ testPlay,
331
+ handleCallAnswer,
332
+ callAnswer,
333
+ startIncomingCall,
334
+ uuid
225
335
  };
226
336
  }
@@ -2,7 +2,7 @@
2
2
  export const digibotData = {
3
3
  id: -98,
4
4
  receiver_id: 101,
5
- username: 'digibot',
5
+ username: 'Gocheckin AI',
6
6
  customer_phone: '',
7
7
  avatar: '',
8
8
  last_message: '',
@@ -5,7 +5,8 @@ import { routerPush } from '../utils/chat/chat-router';
5
5
  import { PAGE } from '../constant/general';
6
6
  import { subscribeToTopic, unsubscribeFromTopic } from '../plugins/mqtt';
7
7
  import { TOPIC_DETAIL_CALL } from '../constant/mqtt';
8
- import { getWebSocket } from '../plugins/websocket';
8
+ import { getWebSocket, initWebSocket } from '../plugins/websocket';
9
+ import router from '../router';
9
10
  //PINIA
10
11
  export const isRouterReady = ref(false);
11
12
  export function useInitData() {
@@ -13,7 +14,7 @@ export function useInitData() {
13
14
  const initPage = async (data) => {
14
15
  try {
15
16
  isRouterReady.value = false;
16
- const sdk_res = await sdkInit({ id: data.id, token: data.token, domain: data.domain });
17
+ const sdk_res = await sdkInit(data.props);
17
18
  if (sdk_res.tenant_error) {
18
19
  const api_link_res = await loginApiLink();
19
20
  // if (api_link_res.client_error) {
@@ -22,13 +23,7 @@ export function useInitData() {
22
23
  // }
23
24
  }
24
25
  // connectMqtt()
25
- await initData();
26
- if (data.response == 'mobile') {
27
- routerPush(PAGE.CHAT_LIST);
28
- }
29
- else {
30
- routerPush(PAGE.HOME);
31
- }
26
+ await initData(data.props, data.response);
32
27
  }
33
28
  catch (error) {
34
29
  console.log(error);
@@ -37,11 +32,21 @@ export function useInitData() {
37
32
  isRouterReady.value = true;
38
33
  }
39
34
  };
40
- const initData = async () => {
41
- await getProfile();
42
- getWebSocket();
35
+ const initData = async (props, response) => {
36
+ const res = await getProfile();
37
+ await getWebSocket();
38
+ initWebSocket();
43
39
  unsubscribeFromTopic(TOPIC_DETAIL_CALL + dataProfile.value?.id);
44
40
  subscribeToTopic(TOPIC_DETAIL_CALL + dataProfile.value?.id);
41
+ if ((!res?.phone && res.tenant_id && !props.isLib)) {
42
+ router.push({ name: 'tenant-phone' });
43
+ }
44
+ else if (response == 'mobile') {
45
+ routerPush(PAGE.CHAT_LIST);
46
+ }
47
+ else {
48
+ routerPush(PAGE.HOME);
49
+ }
45
50
  };
46
51
  const loginApiLink = async () => {
47
52
  try {
@@ -27,7 +27,7 @@ export function defineRootColor() {
27
27
  root.style?.setProperty('--chat-color-primary', Color.Primary);
28
28
  // root.style?.setProperty('--chat-color-primary_hover', Color.Primary_Hover)
29
29
  // root.style?.setProperty('--chat-color-primary_rgb', Color.Primary_RGB)
30
- // root.style?.setProperty('--chat-color-error', Color.Error)
30
+ root.style?.setProperty('--chat-color-error', Color.Error);
31
31
  // root.style?.setProperty('--chat-color-error_hover', Color.Error_Hover)
32
32
  root.style?.setProperty('--chat-color-success', Color.Success);
33
33
  // root.style?.setProperty('--chat-color-success-bg', Color.Success_Bg)
@@ -1,6 +1,7 @@
1
1
  import axios from 'axios';
2
2
  const baseURL = 'https://go-chat.dev01.dtsmart.dev/';
3
- // const baseURL = 'http://192.168.1.152:8085/'
3
+ // const baseURL = 'https://go-chat-test.dev01.dtsmart.dev'
4
+ // const baseURL = '192.168.1.162:3000'
4
5
  const instance = axios.create({
5
6
  baseURL,
6
7
  timeout: 20000,
@@ -4,9 +4,11 @@ const mqttOptions = { qos: 1, retain: false };
4
4
  let mqttClient = null;
5
5
  const subscribedTopics = new Set();
6
6
  let dataCallBack = [];
7
+ let reconnectCount = 0;
8
+ const MAX_RECONNECT = 5;
7
9
  export const connectMqtt = () => {
8
10
  return new Promise((resolve, reject) => {
9
- console.log(mqttClient);
11
+ reconnectCount = 0;
10
12
  if (mqttClient && mqttClient?.connected) {
11
13
  console.log('MQTT already connected');
12
14
  return resolve();
@@ -49,11 +51,17 @@ export const connectMqtt = () => {
49
51
  });
50
52
  resolve();
51
53
  });
54
+ mqttClient?.on('reconnect', () => {
55
+ reconnectCount++;
56
+ if (reconnectCount >= MAX_RECONNECT) {
57
+ mqttClient?.end(true); // true = force close
58
+ mqttClient = null;
59
+ resolve();
60
+ }
61
+ });
52
62
  mqttClient?.on('close', () => {
53
- console.log('MQTT Disconnected');
54
63
  });
55
64
  mqttClient?.on('error', (err) => {
56
- console.error('MQTT Error:', err);
57
65
  reject(err);
58
66
  });
59
67
  });
@@ -61,7 +69,6 @@ export const connectMqtt = () => {
61
69
  export const disconnectMqtt = () => {
62
70
  if (mqttClient) {
63
71
  mqttClient?.end(false, {}, () => {
64
- console.log('MQTT Disconnected');
65
72
  mqttClient = null;
66
73
  });
67
74
  }
@@ -104,7 +111,6 @@ export const publishMessage = (topic, payload) => {
104
111
  const message = typeof payload !== 'string' ? JSON.stringify(payload) : payload;
105
112
  mqttClient?.publish(topic, message, { qos: 1, retain: false }, (err) => {
106
113
  if (!err) {
107
- // console.log(`Published message to ${topic}`)
108
114
  }
109
115
  else {
110
116
  console.error(`Failed to publish message to ${topic}`, err);
@@ -1,9 +1,19 @@
1
1
  import { tryParseJson } from '../utils/json';
2
+ import { dataProfile } from '../utils/chat/auth';
2
3
  let socket = null;
3
4
  let dataCallBack = [];
4
- export function initWebSocket(server) {
5
- socket = new WebSocket('wss://web-socket.dev01.dtsmart.dev/web-stream');
6
- socket.binaryType = "arraybuffer";
5
+ let callId = '';
6
+ const BASE_URL = 'wss://web-socket.dev01.dtsmart.dev/web-stream';
7
+ // const BASE_URL = 'wss://web-socket-test.dev01.dtsmart.dev/web-stream'
8
+ const BARE_URL_INBOUND = 'https://web-socket.dev01.dtsmart.dev';
9
+ export function initWebSocket() {
10
+ if (socket && socket.readyState === 1) {
11
+ console.log("WebSocket đã kết nối, bỏ qua connect.");
12
+ return;
13
+ }
14
+ // socket = new WebSocket('wss://' + callId)
15
+ socket = new WebSocket(BASE_URL);
16
+ socket.binaryType = 'arraybuffer';
7
17
  socket.onopen = (event) => {
8
18
  console.log('Connected!');
9
19
  };
@@ -35,9 +45,10 @@ export const getWebSocket = async () => {
35
45
  const response = await fetch(url, {
36
46
  method: 'POST',
37
47
  body: JSON.stringify({
38
- clientId: 'test1'
48
+ clientId: dataProfile.value?.tenant_id
39
49
  }),
40
50
  headers: {
51
+ "Content-Type": "application/json",
41
52
  Authorization: 'Bearer -Qixt-ZztiBA5Dxl-EIWG7f2TDhK-ZCj'
42
53
  }
43
54
  });
@@ -45,6 +56,8 @@ export const getWebSocket = async () => {
45
56
  throw new Error(`Response status: ${response.status}`);
46
57
  }
47
58
  const result = await response.json();
59
+ callId = result?.wsUrl;
60
+ return result;
48
61
  }
49
62
  catch (error) {
50
63
  console.error(error.message);
@@ -60,3 +73,70 @@ export const removeHandleWebSK = (key) => {
60
73
  dataCallBack.splice(index, 1);
61
74
  }
62
75
  };
76
+ export class WebSocketClient {
77
+ socket = null;
78
+ dataCallbacks = null;
79
+ server;
80
+ constructor(server, callBack) {
81
+ this.server = server;
82
+ this.dataCallbacks = callBack;
83
+ }
84
+ async init() {
85
+ return new Promise((resolve, reject) => {
86
+ if (!this.server)
87
+ reject(new Error("Missing uuid!"));
88
+ this.socket = new WebSocket(BASE_URL + '?call_uuid=' + this.server);
89
+ console.log(BASE_URL + '?call_uuid=' + this.server);
90
+ this.socket.binaryType = "arraybuffer";
91
+ const timeout = setTimeout(() => {
92
+ reject(new Error("⏰ WebSocket connection timeout"));
93
+ }, 5000); // 5s timeout
94
+ this.socket.onopen = () => {
95
+ clearTimeout(timeout);
96
+ resolve(); // Kết nối thành công
97
+ };
98
+ this.socket.onmessage = (event) => {
99
+ const data = tryParseJson(event.data);
100
+ console.log(data);
101
+ this.dataCallbacks?.(data);
102
+ };
103
+ this.socket.onclose = (event) => {
104
+ };
105
+ this.socket.onerror = (event) => {
106
+ clearTimeout(timeout);
107
+ reject(new Error("WebSocket connection error"));
108
+ };
109
+ });
110
+ }
111
+ send(data) {
112
+ try {
113
+ const message = typeof data === 'string' ? data : JSON.stringify(data);
114
+ this.socket?.send(message);
115
+ }
116
+ catch (e) {
117
+ console.error('Send error:', e);
118
+ }
119
+ }
120
+ sendDefault(data) {
121
+ try {
122
+ this.socket?.send(data);
123
+ }
124
+ catch (e) {
125
+ console.error('Send error:', e);
126
+ }
127
+ }
128
+ disconnect() {
129
+ if (this.socket) {
130
+ this.socket.onopen = null;
131
+ this.socket.onmessage = null;
132
+ this.socket.onclose = null;
133
+ this.socket.onerror = null;
134
+ if (this.socket.readyState === WebSocket.OPEN) {
135
+ this.socket.close(1000, "Manual disconnect");
136
+ console.log("🔌 WebSocket closed manually");
137
+ }
138
+ this.socket = null;
139
+ }
140
+ this.dataCallbacks = null;
141
+ }
142
+ }
@@ -0,0 +1,39 @@
1
+ import { createRouter, createWebHistory } from "vue-router";
2
+ import Index from "../chat/App.vue";
3
+ import NotFound from "../views/NotFound.vue";
4
+ import TenantPhone from "../views/TenantPhone.vue";
5
+ const router = createRouter({
6
+ history: createWebHistory(import.meta.env.BASE_URL),
7
+ routes: [
8
+ {
9
+ path: '/',
10
+ alias: '/index.html',
11
+ name: "home",
12
+ component: Index,
13
+ },
14
+ {
15
+ path: '/tenant',
16
+ alias: '/index.html',
17
+ name: "client-home",
18
+ component: Index,
19
+ },
20
+ {
21
+ path: '/tenant-phone',
22
+ alias: '/index.html',
23
+ name: "tenant-phone",
24
+ component: TenantPhone,
25
+ },
26
+ {
27
+ path: '/:pathMatch(.*)*',
28
+ alias: '/index.html',
29
+ name: "not-found",
30
+ component: NotFound,
31
+ },
32
+ ],
33
+ });
34
+ // router.beforeEach(async (to, from): Promise<any> => {
35
+ // if (to.name != "home") {
36
+ // return { name: "home" };
37
+ // }
38
+ // });
39
+ export default router;
@@ -31,21 +31,29 @@ export const loginLink = async (params) => {
31
31
  };
32
32
  export const getProfile = async () => {
33
33
  const res = await axios.get('/api/v1/message/user/me');
34
- localStorage.setItem('chat_user', JSON.stringify(res?.data));
34
+ localStorage.setItem('chat_user', JSON.stringify(res));
35
35
  dataProfile.value = res;
36
36
  return res;
37
37
  };
38
+ export const submitTenantPhone = async (body) => {
39
+ const res = await axios.post('/api/v1/message/tenant/update-phone', body);
40
+ dataLoginLink.value = res;
41
+ console.log(res);
42
+ return res;
43
+ };
38
44
  export const logout = async () => {
39
45
  dataLogin = { id: '', token: '', domain: '' };
40
46
  dataProfile.value = null;
41
47
  localStorage.removeItem('chat_accessToken');
42
48
  localStorage.removeItem('chat_domain');
43
49
  localStorage.removeItem('chat_id');
44
- window.location.href = '/login';
45
50
  try {
46
51
  if (gapMiniAppSdk?.getInstance()?.getBridge()) {
47
52
  gapMiniAppSdk?.getInstance()?.closeApp();
48
53
  }
54
+ else {
55
+ window.location.href = '/login';
56
+ }
49
57
  }
50
58
  catch (error) { }
51
59
  };
@@ -1,6 +1,7 @@
1
1
  import axios from '../../plugins/axios';
2
2
  import { dataProfile } from '../../utils/chat/auth';
3
3
  const BARE_WEBSOCKET_URL = 'https://web-socket.dev01.dtsmart.dev';
4
+ // const BARE_WEBSOCKET_URL = 'https://web-socket-test.dev01.dtsmart.dev'
4
5
  // const BARE_WEBSOCKET_URL = 'http://192.168.1.173:3000'
5
6
  export const getIceService = async () => {
6
7
  const res = await axios.post('/api/v1/message/call/ice-servers', {
@@ -16,12 +17,12 @@ export const getIceService = async () => {
16
17
  // })
17
18
  // return res
18
19
  // }
19
- export const callClient = async (body) => {
20
- const url = BARE_WEBSOCKET_URL + '/call/answer';
20
+ export const callClient = async (data) => {
21
+ const url = BARE_WEBSOCKET_URL + '/ws/answer';
21
22
  try {
22
23
  const response = await fetch(url, {
23
24
  method: 'POST',
24
- body: JSON.stringify(body),
25
+ body: JSON.stringify(data),
25
26
  headers: {
26
27
  ['Content-Type']: 'application/json'
27
28
  }
@@ -64,8 +65,10 @@ export const plivoCall = async (user) => {
64
65
  const response = await fetch(url, {
65
66
  method: 'POST',
66
67
  body: JSON.stringify({
68
+ // from: '18668259612' || dataProfile.value?.phone,
67
69
  from: '18668259612' || dataProfile.value?.phone,
68
- to: user?.phone || ''
70
+ to: user?.phone || '',
71
+ // clientId: dataProfile.value?.tenant_id
69
72
  }),
70
73
  headers: {
71
74
  ['Content-Type']: 'application/json'
@@ -80,7 +83,7 @@ export const plivoCall = async (user) => {
80
83
  return result;
81
84
  };
82
85
  export const plivoEndCall = async (uuid) => {
83
- const url = BARE_WEBSOCKET_URL + '/plivo/end-call';
86
+ const url = BARE_WEBSOCKET_URL + '/ws/end-call';
84
87
  const response = await fetch(url, {
85
88
  method: 'POST',
86
89
  body: JSON.stringify({
@@ -96,3 +99,27 @@ export const plivoEndCall = async (uuid) => {
96
99
  const result = await response.json();
97
100
  console.log(result);
98
101
  };
102
+ export const downloadRecord = async (url_pub) => {
103
+ const url = BARE_WEBSOCKET_URL + '/ws/download-record';
104
+ const response = await fetch(url, {
105
+ method: 'POST',
106
+ body: JSON.stringify({
107
+ url: url_pub
108
+ }),
109
+ headers: {
110
+ ['Content-Type']: 'application/json'
111
+ }
112
+ });
113
+ if (!response.ok) {
114
+ throw new Error(`Response status: ${response.status}`);
115
+ }
116
+ const blob = await response.blob();
117
+ const url_blob = window.URL.createObjectURL(blob);
118
+ const a = document.createElement("a");
119
+ a.href = url_blob;
120
+ a.download = "recording.mp3";
121
+ a.click();
122
+ a.remove();
123
+ // const result = await response.json()
124
+ console.log(a);
125
+ };
@@ -0,0 +1,4 @@
1
+ export const formatPhone10number = (phone, dial) => {
2
+ let result = phone.replace(/\D/g, '');
3
+ return result?.startsWith(dial) ? result.slice(dial.length) : result;
4
+ };