@chat21/chat21-ionic 3.4.27-rc21 → 3.4.27-rc23

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.
@@ -0,0 +1,232 @@
1
+ /// websocket.worker.ts
2
+
3
+ interface WSMessage {
4
+ action?: string;
5
+ event?: { topic: string; method: string };
6
+ payload?: any;
7
+ }
8
+
9
+ interface Subscription {
10
+ topic: string;
11
+ label?: string;
12
+ onCreate?: (msg: any) => void;
13
+ onUpdate?: (msg: any) => void;
14
+ onData?: (msg: any) => void;
15
+ }
16
+
17
+ let ws: WebSocket | null = null;
18
+ let url = '';
19
+ let reconnectInterval = 5000;
20
+ let subscriptions: Subscription[] = [];
21
+ let pendingMessages: any[] = [];
22
+
23
+
24
+ let pingMsg = { action: "heartbeat", payload: { message: { text: "ping" } } }
25
+ let pongMsg = { action: "heartbeat", payload: { message: { text: "pong" } } }
26
+ const pongTimeout = 10000;
27
+ const pingTimeout = 15000;
28
+ let pingTimeoutId;
29
+ let pongTimeoutId;
30
+
31
+ let reconnectAttempts = 0;
32
+ const maxReconnectDelay = 30000;
33
+ let manuallyClosed = false;
34
+ let pageHidden = false;
35
+
36
+ onmessage = (e: MessageEvent) => {
37
+ const { action, data } = e.data;
38
+
39
+ switch (action) {
40
+ case 'init':
41
+ url = data.url;
42
+ connect();
43
+ break;
44
+ case 'subscribe':
45
+ const topic = data.topic || data.label;
46
+ subscriptions.push({ topic });
47
+ if (ws && ws.readyState === WebSocket.OPEN) {
48
+ ws.send(JSON.stringify({
49
+ action: 'subscribe',
50
+ payload: {
51
+ topic: topic,
52
+ message: undefined,
53
+ method: undefined
54
+ }
55
+ }));
56
+ }
57
+ break;
58
+ case 'unsubscribe':
59
+ subscriptions = subscriptions.filter(s => s.topic !== data.topic);
60
+ if (ws && ws.readyState === WebSocket.OPEN) {
61
+ // Unsubscribe
62
+ ws.send(JSON.stringify({
63
+ action: 'unsubscribe',
64
+ payload: {
65
+ topic: data.topic,
66
+ message: undefined,
67
+ method: undefined
68
+ }
69
+ }));
70
+ }
71
+ break;
72
+ case 'visibility': // ✅ Nuovo
73
+ pageHidden = data.hidden;
74
+ break;
75
+ case 'send':
76
+ sendMessage(data.message);
77
+ break;
78
+ case 'close': // ✅ Nuovo
79
+ closeConnection();
80
+ break;
81
+ }
82
+ };
83
+
84
+ function connect() {
85
+ ws = new WebSocket(url);
86
+ ws.onopen = () => {
87
+ console.log('[Worker] WebSocket connected',subscriptions);
88
+ // Risottiamo pending subscriptions
89
+ subscriptions.forEach(s => {
90
+ ws!.send(JSON.stringify({
91
+ action: 'subscribe',
92
+ payload: {
93
+ topic: s.topic,
94
+ message: undefined,
95
+ method: undefined
96
+ }
97
+ }));
98
+ }
99
+ );
100
+ // Invia messaggi in coda
101
+ pendingMessages.forEach(msg => ws!.send(JSON.stringify(msg)));
102
+ pendingMessages = [];
103
+
104
+ // Inizializza heartbeat
105
+ heartCheck();
106
+ reconnectAttempts = 0;
107
+ };
108
+
109
+ ws.onmessage = (event) => {
110
+ try {
111
+ const msg: WSMessage = JSON.parse(event.data);
112
+ handleMessage(msg);
113
+ } catch (err) {
114
+ console.error('[Worker] Invalid message', err);
115
+ }
116
+ };
117
+
118
+ ws.onclose = () => {
119
+ heartReset();
120
+ if(!manuallyClosed){
121
+ reconnectAttempts++;
122
+ const delay = Math.min(reconnectAttempts * 1000, maxReconnectDelay);
123
+ console.log('[Worker] WebSocket disconnected, retry in', delay, 'ms');
124
+ setTimeout(connect, delay);
125
+ }
126
+ };
127
+
128
+ ws.onerror = (err) => {
129
+ console.error('[Worker] WebSocket error', err);
130
+ ws?.close();
131
+ };
132
+ }
133
+
134
+ function handleMessage(msg: WSMessage) {
135
+ console.log('[Worker] Received message:', msg);
136
+ // --- GESTIONE PING/PONG ---
137
+ if (msg.action === 'heartbeat' && msg.payload?.message?.text === 'ping') {
138
+ console.log('[Worker] Received ping, sending pong');
139
+ if (ws && ws.readyState === WebSocket.OPEN) {
140
+ ws.send(JSON.stringify(pongMsg));
141
+ }
142
+ return; // Non processare ulteriormente il ping
143
+ }
144
+
145
+ // Solo formato "publish"
146
+ if (msg.action !== "publish") return;
147
+
148
+ const topic = msg.payload?.topic;
149
+ const method = msg.payload?.method;
150
+ const payload = msg.payload?.message;
151
+
152
+ if (!topic) return;
153
+
154
+ // Notifica solo le subscription che matchano
155
+ subscriptions.forEach(sub => {
156
+ if (sub.topic === topic) {
157
+ postMessage({ topic, method, payload, msg }, undefined);
158
+ }
159
+ });
160
+ }
161
+
162
+ function sendMessage(message: any) {
163
+ if (ws && ws.readyState === WebSocket.OPEN) {
164
+ ws.send(JSON.stringify(message));
165
+ } else {
166
+ pendingMessages.push(message);
167
+ }
168
+ }
169
+
170
+
171
+ // -----------------------------------------------------------------------------------------------------
172
+ // @ HeartCheck
173
+ // -----------------------------------------------------------------------------------------------------
174
+ function heartCheck() {
175
+ heartReset();
176
+ heartStart();
177
+ }
178
+
179
+ // -----------------------------------------------------------------------------------------------------
180
+ // @ HeartStart
181
+ // -----------------------------------------------------------------------------------------------------
182
+ function heartStart() {
183
+ // this.getRemainingTime();
184
+
185
+ // usa intervallo adattivo se tab è in background (Chrome throtla i timer)
186
+ const adaptivePing = pageHidden ? pingTimeout * 3 : pingTimeout;
187
+
188
+ // // pianifica invio ping
189
+ pingTimeoutId = setTimeout(() => {
190
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
191
+ console.log("[WEBSOCKET-JS] - HEART-START - SENDING PING ");
192
+
193
+ // Qui viene inviato un battito cardiaco. Dopo averlo ricevuto, viene restituito un messaggio di battito cardiaco.
194
+ // onmessage Ottieni il battito cardiaco restituito per indicare che la connessione è normale
195
+ ws.send(JSON.stringify(pingMsg));
196
+
197
+ // Se non viene ripristinato dopo un determinato periodo di tempo, il backend viene attivamente disconnesso
198
+ pongTimeoutId = setTimeout(() => {
199
+ console.log("[WEBSOCKET-JS] - HEART-START - PONG-TIMEOUT-ID - CLOSE WS ");
200
+ // se onclose Si esibirà reconnect,Eseguiamo ws.close() Bene, se lo esegui direttamente reconnect Si innescherà onclose Causa riconnessione due volte
201
+ ws.close();
202
+ }, pongTimeout) as unknown as number;
203
+ }, adaptivePing);
204
+
205
+ }
206
+
207
+ // -----------------------------------------------------------------------------------------------------
208
+ // @ heartReset
209
+ // -----------------------------------------------------------------------------------------------------
210
+ function heartReset() {
211
+ if (pongTimeoutId !== undefined) {
212
+ clearTimeout(pongTimeoutId);
213
+ pongTimeoutId = undefined;
214
+ }
215
+
216
+ if (pingTimeoutId !== undefined) {
217
+ clearTimeout(pingTimeoutId);
218
+ pingTimeoutId = undefined;
219
+ }
220
+ }
221
+
222
+ function closeConnection() {
223
+ if (ws) {
224
+ ws.close();
225
+ ws = null;
226
+ }
227
+ heartReset();
228
+ pendingMessages = [];
229
+ subscriptions = [];
230
+ manuallyClosed = true;
231
+ console.log('[Worker] WebSocket closed manually');
232
+ }
@@ -1010,6 +1010,7 @@ class Chat21Client {
1010
1010
  this.start( () => {
1011
1011
  // callback();
1012
1012
  });
1013
+ this.initKeepAliveWorker()
1013
1014
  }
1014
1015
  this.client.publish(
1015
1016
  this.presence_topic,
@@ -1027,6 +1028,34 @@ class Chat21Client {
1027
1028
  console.error("Chat client error event", error);
1028
1029
  }
1029
1030
  );
1031
+
1032
+
1033
+ }
1034
+
1035
+
1036
+ initKeepAliveWorker(){
1037
+ this.worker= new Worker('assets/js/mqtt-keepalive-worker.js');
1038
+ console.log("Chat21Client - initKeepAliveWorker() - worker created", this.worker);
1039
+ // this.worker.postMessage({
1040
+ // action: 'start',
1041
+ // user_id: this.user_id,
1042
+ // client_id: this.client_id,
1043
+ // jwt: this.jwt,
1044
+ // endpoint: this.endpoint
1045
+ // });
1046
+ this.worker.onmessage = (event) => {
1047
+ if (event.data.action === 'ping') {
1048
+ if (this.client && this.client.connected) {
1049
+ // questo non causa refresh di niente
1050
+ this.client.publish(
1051
+ `apps/tilechat/users/${this.user_id}/keepalive`,
1052
+ JSON.stringify({ ts: Date.now() })
1053
+ );
1054
+ }
1055
+ }
1056
+ };
1057
+
1058
+ this.worker.postMessage({ action: 'ping' });
1030
1059
  }
1031
1060
 
1032
1061
  onDisconnect(callback){
@@ -1041,6 +1070,11 @@ class Chat21Client {
1041
1070
  this.connected = false
1042
1071
  if (this.log) {console.log("Chat client close event");}
1043
1072
  callback('close')
1073
+ if (this.worker) {
1074
+ this.worker.postMessage({ action: 'stop' });
1075
+ this.worker.terminate();
1076
+ this.worker = null;
1077
+ }
1044
1078
  }
1045
1079
  );
1046
1080
  this.client.on('offline',
@@ -0,0 +1,52 @@
1
+ importScripts('https://unpkg.com/mqtt/dist/mqtt.min.js');
2
+
3
+ let client = null;
4
+ let user_id = null;
5
+ let pingIntervalId = null;
6
+ self.onmessage = function(event) {
7
+ const data = event.data;
8
+
9
+ console.log('MQTT KEEPALIVE WORKER - received message ', data);
10
+ if (data.action === 'start') {
11
+ user_id = data.user_id;
12
+ const endpoint = data.endpoint;
13
+ const jwt = data.jwt;
14
+
15
+ const options = {
16
+ keepalive: 3, // basso keepalive
17
+ reconnectPeriod: 1000,
18
+ clientId: data.client_id,
19
+ username: 'JWT',
20
+ password: jwt,
21
+ rejectUnauthorized: false
22
+ };
23
+
24
+ client = mqtt.connect(endpoint, options);
25
+
26
+ client.on('connect', () => {
27
+ // start ping
28
+ pingIntervalId = setInterval(() => {
29
+ if (client && client.connected) {
30
+ console.log('MQTT KEEPALIVE WORKER - sending keepalive ping for user ', user_id);
31
+ client.publish(`apps/tilechat/users/${user_id}/keepalive`,
32
+ JSON.stringify({ ts: new Date().getTime() }));
33
+ }
34
+ }, 3000);
35
+ });
36
+
37
+ client.on('close', () => {
38
+ clearInterval(pingIntervalId);
39
+ pingIntervalId = null;
40
+ });
41
+ }else if (data.action === 'ping') {
42
+ if (self.timer) return;
43
+ self.timer = setInterval(() => {
44
+ postMessage({ action: 'ping' });
45
+ }, 3000);
46
+ }
47
+
48
+ if (data.action === 'stop') {
49
+ if (pingIntervalId) clearInterval(pingIntervalId);
50
+ if (client) client.end();
51
+ }
52
+ };
@@ -4,7 +4,7 @@
4
4
  <head>
5
5
  <meta charset="utf-8">
6
6
  <meta name="viewport" content="width=device-width">
7
- <title>replit</title>
7
+ <title>Iframe test</title>
8
8
  </head>
9
9
 
10
10
  <body style="height: 100%;">
@@ -20,7 +20,10 @@
20
20
  </div>
21
21
 
22
22
  <iframe id="myIFrame" frameBorder="0" width="100%" style="display: flex; height: 95vh" onload="onLoad()"
23
- src="http://localhost:8080/#/conversation-detail?tiledesk_supportMode=true&jwt=JWT eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZmQzNGU0MjQ5NjEwZTAwMzQ5ODUwNzgiLCJlbWFpbCI6ImdhYnJpZWxlLnBhbmljb0Bmcm9udGllcmUyMS5pdCIsImZpcnN0bmFtZSI6IkdhYnJpZWxlMiIsImxhc3RuYW1lIjoiUGFuaWNvIC1wcmUiLCJlbWFpbHZlcmlmaWVkIjp0cnVlLCJpYXQiOjE2OTgzMzYyMTMsImF1ZCI6Imh0dHBzOi8vdGlsZWRlc2suY29tIiwiaXNzIjoiaHR0cHM6Ly90aWxlZGVzay5jb20iLCJzdWIiOiJ1c2VyIiwianRpIjoiNjAzZGY3NDQtZWY0OS00Mjg2LWJlMWQtYWM5OGYyYTNkMTJiIn0.bof5QPQP262yCQfpeSjqyoYPkSCttg5NQFkcV8d634xzeq7wJBdhtiO7IJY6hS81a1YyXrAsxlK7RiNS-UZdYiTXR81UXurQd1fnE9jtaggB82tyNL8vmiZEOs6xh4TqO3-kkBzRxQIp74bY53fk_mu5RmNbAjIgUWEufaNDiv8"></iframe>
23
+ src="http://localhost:8080/#/conversation-detail?tiledesk_supportMode=true&jwt=JWT eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NWM1ZjM1OTlmYWYyZDA0Y2Q3ZGE1MjgiLCJlbWFpbCI6Imdpb3Zhbm5pQHRpbGVkZXNrLmNvbSIsImZpcnN0bmFtZSI6Ikdpb3Zhbm5pIiwibGFzdG5hbWUiOiJUcm9pc2kgQWRtIiwiZW1haWx2ZXJpZmllZCI6dHJ1ZSwiaWF0IjoxNzY0NjAyMTQxLCJhdWQiOiJodHRwczovL3RpbGVkZXNrLmNvbSIsImlzcyI6Imh0dHBzOi8vdGlsZWRlc2suY29tIiwic3ViIjoidXNlciIsImp0aSI6IjIwNzZjYmMxLTI0NzgtNGU4MC1iZjFmLTBiZTA3MTQ3MjQ3MSJ9.R60xZC5M2n-hc-mRIMpA9KaUFxnBQHA_FrLyL9cCiqxeCitfCfbfA3_EKXWbWg3rDztmOR7SVjEO8Gyw_hdhKyjpx-OpaDAILw0dSsuBHB0R4h0F4fN2J_JJ60UIItXxoAq1AI9ML2cRGoRNAtfl6PMjpHXdbpdBAGcBYoGqGv3xWPlM7wOdIhGasnWT932DiscF3I_ltcFkS3naebQQfsgztlbU-UF7EcFjDc-mMzMxqnE1MYKzyqUAR9J9zFmqiUlCsl_J0TNhAjqUw-0GHXpzLQJN9dutVyH4y_MZxt_p6qAIzNo9LpMi-a5bfvQvpJjSuc9POvmlmWFAmwoQGw"
24
+ allow="autoplay; fullscreen; geolocation; microphone; camera; display-capture; encrypted-media; clipboard-read; clipboard-write;"
25
+ loading="eager" importance="high">
26
+ </iframe>
24
27
 
25
28
  <script>
26
29
  // window.addEventListener('message', event => {