@sparkstudio/realtime-ui 1.0.5 → 1.0.11
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/dist/index.cjs +218 -4
- package/dist/index.d.cts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +215 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
GuidUtil: () => GuidUtil,
|
|
25
25
|
Home: () => Home,
|
|
26
26
|
HomeView: () => HomeView,
|
|
27
|
+
RealtimeEngine: () => RealtimeEngine,
|
|
27
28
|
RoomInstance: () => RoomInstance,
|
|
28
29
|
RoomInstanceDTO: () => RoomInstanceDTO,
|
|
29
30
|
RoomInstanceViewDTO: () => RoomInstanceViewDTO,
|
|
@@ -35,7 +36,8 @@ __export(index_exports, {
|
|
|
35
36
|
UserStatusType: () => UserStatusType,
|
|
36
37
|
WebSocketClientRequestDTO: () => WebSocketClientRequestDTO,
|
|
37
38
|
WebSocketConnection: () => WebSocketConnection,
|
|
38
|
-
WebSocketConnectionDTO: () => WebSocketConnectionDTO
|
|
39
|
+
WebSocketConnectionDTO: () => WebSocketConnectionDTO,
|
|
40
|
+
useRealtime: () => useRealtime
|
|
39
41
|
});
|
|
40
42
|
module.exports = __toCommonJS(index_exports);
|
|
41
43
|
|
|
@@ -469,7 +471,104 @@ var UserStatusType = /* @__PURE__ */ ((UserStatusType2) => {
|
|
|
469
471
|
return UserStatusType2;
|
|
470
472
|
})(UserStatusType || {});
|
|
471
473
|
|
|
472
|
-
// src/
|
|
474
|
+
// src/engines/RealtimeEngine.ts
|
|
475
|
+
var RealtimeEngine = class {
|
|
476
|
+
constructor(url) {
|
|
477
|
+
this.url = url;
|
|
478
|
+
}
|
|
479
|
+
ws = null;
|
|
480
|
+
status = "disconnected";
|
|
481
|
+
listeners = {
|
|
482
|
+
status: /* @__PURE__ */ new Set(),
|
|
483
|
+
message: /* @__PURE__ */ new Set(),
|
|
484
|
+
open: /* @__PURE__ */ new Set(),
|
|
485
|
+
close: /* @__PURE__ */ new Set(),
|
|
486
|
+
error: /* @__PURE__ */ new Set()
|
|
487
|
+
};
|
|
488
|
+
getStatus() {
|
|
489
|
+
return this.status;
|
|
490
|
+
}
|
|
491
|
+
on(event, handler) {
|
|
492
|
+
const set = this.getListenerSet(event);
|
|
493
|
+
set.add(handler);
|
|
494
|
+
return () => set.delete(handler);
|
|
495
|
+
}
|
|
496
|
+
connect() {
|
|
497
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
this.setStatus("connecting");
|
|
501
|
+
const ws = new WebSocket(this.url);
|
|
502
|
+
this.ws = ws;
|
|
503
|
+
ws.onopen = (ev) => {
|
|
504
|
+
this.setStatus("connected");
|
|
505
|
+
this.emit("open", ev);
|
|
506
|
+
};
|
|
507
|
+
ws.onmessage = (ev) => {
|
|
508
|
+
this.emit("message", this.tryParse(ev.data));
|
|
509
|
+
};
|
|
510
|
+
ws.onerror = (ev) => {
|
|
511
|
+
this.setStatus("error");
|
|
512
|
+
this.emit("error", ev);
|
|
513
|
+
};
|
|
514
|
+
ws.onclose = (ev) => {
|
|
515
|
+
this.setStatus("disconnected");
|
|
516
|
+
this.emit("close", ev);
|
|
517
|
+
this.ws = null;
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
disconnect(code, reason) {
|
|
521
|
+
this.ws?.close(code, reason);
|
|
522
|
+
this.ws = null;
|
|
523
|
+
this.setStatus("disconnected");
|
|
524
|
+
}
|
|
525
|
+
send(payload) {
|
|
526
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
|
|
527
|
+
const message = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
528
|
+
this.ws.send(message);
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
getListenerSet(event) {
|
|
532
|
+
return this.listeners[event];
|
|
533
|
+
}
|
|
534
|
+
setStatus(next) {
|
|
535
|
+
if (this.status === next) return;
|
|
536
|
+
this.status = next;
|
|
537
|
+
this.emit("status", next);
|
|
538
|
+
}
|
|
539
|
+
emit(event, value) {
|
|
540
|
+
const set = this.getListenerSet(event);
|
|
541
|
+
set.forEach((fn) => fn(value));
|
|
542
|
+
}
|
|
543
|
+
tryParse(data) {
|
|
544
|
+
if (typeof data !== "string") return data;
|
|
545
|
+
try {
|
|
546
|
+
return JSON.parse(data);
|
|
547
|
+
} catch {
|
|
548
|
+
return data;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/hooks/useRealtime.ts
|
|
554
|
+
var import_react = require("react");
|
|
555
|
+
function useRealtime(url) {
|
|
556
|
+
const engine = (0, import_react.useMemo)(() => new RealtimeEngine(url), [url]);
|
|
557
|
+
const [status, setStatus] = (0, import_react.useState)(engine.getStatus());
|
|
558
|
+
const [lastMessage, setLastMessage] = (0, import_react.useState)(null);
|
|
559
|
+
(0, import_react.useEffect)(() => {
|
|
560
|
+
const offStatus = engine.on("status", setStatus);
|
|
561
|
+
const offMessage = engine.on("message", setLastMessage);
|
|
562
|
+
return () => {
|
|
563
|
+
offStatus();
|
|
564
|
+
offMessage();
|
|
565
|
+
};
|
|
566
|
+
}, [engine]);
|
|
567
|
+
return { engine, status, lastMessage };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/views/HomeContent.tsx
|
|
571
|
+
var import_react2 = require("react");
|
|
473
572
|
var import_authentication_ui = require("@sparkstudio/authentication-ui");
|
|
474
573
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
475
574
|
function HomeView() {
|
|
@@ -483,11 +582,124 @@ function HomeView() {
|
|
|
483
582
|
}
|
|
484
583
|
);
|
|
485
584
|
}
|
|
585
|
+
function isSuccessMessage(msg) {
|
|
586
|
+
if (typeof msg === "string") return msg.trim().toLowerCase() === "success";
|
|
587
|
+
if (msg && typeof msg === "object") {
|
|
588
|
+
const o = msg;
|
|
589
|
+
const status = typeof o.Status === "string" ? o.Status : typeof o.status === "string" ? o.status : void 0;
|
|
590
|
+
const message = typeof o.Message === "string" ? o.Message : typeof o.message === "string" ? o.message : void 0;
|
|
591
|
+
const success = typeof o.Success === "boolean" ? o.Success : typeof o.success === "boolean" ? o.success : void 0;
|
|
592
|
+
if (success === true) return true;
|
|
593
|
+
if (status?.trim().toLowerCase() === "success") return true;
|
|
594
|
+
if (message?.trim().toLowerCase() === "success") return true;
|
|
595
|
+
}
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
486
598
|
function HomeContent() {
|
|
487
599
|
const { user } = (0, import_authentication_ui.useUser)();
|
|
600
|
+
const wsUrl = (0, import_react2.useMemo)(() => "ws://localhost:8080", []);
|
|
601
|
+
const { engine, status } = useRealtime(wsUrl);
|
|
602
|
+
const [messages, setMessages] = (0, import_react2.useState)([]);
|
|
603
|
+
(0, import_react2.useEffect)(() => {
|
|
604
|
+
if (!user) {
|
|
605
|
+
engine.disconnect();
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
engine.connect();
|
|
609
|
+
return () => engine.disconnect();
|
|
610
|
+
}, [engine, user]);
|
|
611
|
+
(0, import_react2.useEffect)(() => {
|
|
612
|
+
const offOpen = engine.on("open", () => {
|
|
613
|
+
console.log("\u2705 WS open (socket connected)");
|
|
614
|
+
const token = localStorage.getItem("auth_token");
|
|
615
|
+
engine.send({
|
|
616
|
+
AccessToken: token,
|
|
617
|
+
Data: "{}",
|
|
618
|
+
Type: "InterComUserDTO",
|
|
619
|
+
ControllerName: "UserController",
|
|
620
|
+
MethodName: "Connected"
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
const offMessage = engine.on("message", (msg) => {
|
|
624
|
+
if (isSuccessMessage(msg)) return;
|
|
625
|
+
setMessages((prev) => [...prev, msg]);
|
|
626
|
+
});
|
|
627
|
+
const offClose = engine.on("close", (ev) => {
|
|
628
|
+
console.log("\u{1F6D1} WS close", { code: ev.code, reason: ev.reason });
|
|
629
|
+
});
|
|
630
|
+
const offError = engine.on("error", () => {
|
|
631
|
+
console.log("\u274C WS error");
|
|
632
|
+
});
|
|
633
|
+
const offStatus = engine.on("status", (s) => {
|
|
634
|
+
console.log("\u2139\uFE0F WS status:", s);
|
|
635
|
+
});
|
|
636
|
+
return () => {
|
|
637
|
+
offOpen();
|
|
638
|
+
offMessage();
|
|
639
|
+
offClose();
|
|
640
|
+
offError();
|
|
641
|
+
offStatus();
|
|
642
|
+
};
|
|
643
|
+
}, [engine]);
|
|
488
644
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
489
645
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
490
|
-
user && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
646
|
+
user && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
648
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "WebSocket:" }),
|
|
649
|
+
" ",
|
|
650
|
+
status
|
|
651
|
+
] }),
|
|
652
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
653
|
+
"button",
|
|
654
|
+
{
|
|
655
|
+
type: "button",
|
|
656
|
+
disabled: status !== "connected",
|
|
657
|
+
onClick: () => {
|
|
658
|
+
const token = localStorage.getItem("auth_token");
|
|
659
|
+
engine.send({
|
|
660
|
+
AccessToken: token,
|
|
661
|
+
Data: JSON.stringify({
|
|
662
|
+
Data: "Hello World",
|
|
663
|
+
UserIds: [user.Id]
|
|
664
|
+
}),
|
|
665
|
+
Type: "UserDataDTO",
|
|
666
|
+
ControllerName: "RoomInstanceController",
|
|
667
|
+
MethodName: "SendToUsers"
|
|
668
|
+
});
|
|
669
|
+
},
|
|
670
|
+
children: "Send message"
|
|
671
|
+
}
|
|
672
|
+
),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Messages:" }),
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
676
|
+
"div",
|
|
677
|
+
{
|
|
678
|
+
style: {
|
|
679
|
+
maxHeight: 250,
|
|
680
|
+
overflowY: "auto",
|
|
681
|
+
border: "1px solid #ccc",
|
|
682
|
+
padding: 8,
|
|
683
|
+
fontFamily: "monospace",
|
|
684
|
+
fontSize: 12,
|
|
685
|
+
whiteSpace: "pre-wrap"
|
|
686
|
+
},
|
|
687
|
+
children: messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "(no messages yet)" }) : messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
688
|
+
"div",
|
|
689
|
+
{
|
|
690
|
+
style: {
|
|
691
|
+
marginBottom: 10,
|
|
692
|
+
paddingBottom: 10,
|
|
693
|
+
borderBottom: "1px dashed #ddd"
|
|
694
|
+
},
|
|
695
|
+
children: typeof msg === "string" ? msg : JSON.stringify(msg, null, 2)
|
|
696
|
+
},
|
|
697
|
+
i
|
|
698
|
+
))
|
|
699
|
+
}
|
|
700
|
+
)
|
|
701
|
+
] })
|
|
702
|
+
] })
|
|
491
703
|
] });
|
|
492
704
|
}
|
|
493
705
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -496,6 +708,7 @@ function HomeContent() {
|
|
|
496
708
|
GuidUtil,
|
|
497
709
|
Home,
|
|
498
710
|
HomeView,
|
|
711
|
+
RealtimeEngine,
|
|
499
712
|
RoomInstance,
|
|
500
713
|
RoomInstanceDTO,
|
|
501
714
|
RoomInstanceViewDTO,
|
|
@@ -507,5 +720,6 @@ function HomeContent() {
|
|
|
507
720
|
UserStatusType,
|
|
508
721
|
WebSocketClientRequestDTO,
|
|
509
722
|
WebSocketConnection,
|
|
510
|
-
WebSocketConnectionDTO
|
|
723
|
+
WebSocketConnectionDTO,
|
|
724
|
+
useRealtime
|
|
511
725
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -210,6 +210,39 @@ declare class SparkStudioRealtimeSDK {
|
|
|
210
210
|
constructor(baseUrl: string);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
type RealtimeStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
214
|
+
type RealtimeEventMap = {
|
|
215
|
+
status: RealtimeStatus;
|
|
216
|
+
message: unknown;
|
|
217
|
+
open: Event;
|
|
218
|
+
close: CloseEvent;
|
|
219
|
+
error: Event;
|
|
220
|
+
};
|
|
221
|
+
type Unsubscribe = () => void;
|
|
222
|
+
type Handler<K extends keyof RealtimeEventMap> = (v: RealtimeEventMap[K]) => void;
|
|
223
|
+
declare class RealtimeEngine {
|
|
224
|
+
private readonly url;
|
|
225
|
+
private ws;
|
|
226
|
+
private status;
|
|
227
|
+
private readonly listeners;
|
|
228
|
+
constructor(url: string);
|
|
229
|
+
getStatus(): RealtimeStatus;
|
|
230
|
+
on<K extends keyof RealtimeEventMap>(event: K, handler: Handler<K>): Unsubscribe;
|
|
231
|
+
connect(): void;
|
|
232
|
+
disconnect(code?: number, reason?: string): void;
|
|
233
|
+
send(payload: unknown): boolean;
|
|
234
|
+
private getListenerSet;
|
|
235
|
+
private setStatus;
|
|
236
|
+
private emit;
|
|
237
|
+
private tryParse;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare function useRealtime(url: string): {
|
|
241
|
+
engine: RealtimeEngine;
|
|
242
|
+
status: RealtimeStatus;
|
|
243
|
+
lastMessage: unknown;
|
|
244
|
+
};
|
|
245
|
+
|
|
213
246
|
declare function HomeView(): react_jsx_runtime.JSX.Element;
|
|
214
247
|
|
|
215
|
-
export { type Guid, GuidResultDTO, GuidUtil, Home, HomeView, type IGuidResultDTO, type IRoomInstanceDTO, type IRoomInstanceViewDTO, type IStringResultDTO, type IUniqueViewDTO, type IUserSettingDTO, type IWebSocketClientRequestDTO, type IWebSocketConnectionDTO, RoomInstance, RoomInstanceDTO, RoomInstanceViewDTO, SparkStudioRealtimeSDK, StringResultDTO, UniqueViewDTO, UserSetting, UserSettingDTO, UserStatusType, WebSocketClientRequestDTO, WebSocketConnection, WebSocketConnectionDTO };
|
|
248
|
+
export { type Guid, GuidResultDTO, GuidUtil, Home, HomeView, type IGuidResultDTO, type IRoomInstanceDTO, type IRoomInstanceViewDTO, type IStringResultDTO, type IUniqueViewDTO, type IUserSettingDTO, type IWebSocketClientRequestDTO, type IWebSocketConnectionDTO, RealtimeEngine, type RealtimeEventMap, type RealtimeStatus, RoomInstance, RoomInstanceDTO, RoomInstanceViewDTO, SparkStudioRealtimeSDK, StringResultDTO, UniqueViewDTO, UserSetting, UserSettingDTO, UserStatusType, WebSocketClientRequestDTO, WebSocketConnection, WebSocketConnectionDTO, useRealtime };
|
package/dist/index.d.ts
CHANGED
|
@@ -210,6 +210,39 @@ declare class SparkStudioRealtimeSDK {
|
|
|
210
210
|
constructor(baseUrl: string);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
type RealtimeStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
214
|
+
type RealtimeEventMap = {
|
|
215
|
+
status: RealtimeStatus;
|
|
216
|
+
message: unknown;
|
|
217
|
+
open: Event;
|
|
218
|
+
close: CloseEvent;
|
|
219
|
+
error: Event;
|
|
220
|
+
};
|
|
221
|
+
type Unsubscribe = () => void;
|
|
222
|
+
type Handler<K extends keyof RealtimeEventMap> = (v: RealtimeEventMap[K]) => void;
|
|
223
|
+
declare class RealtimeEngine {
|
|
224
|
+
private readonly url;
|
|
225
|
+
private ws;
|
|
226
|
+
private status;
|
|
227
|
+
private readonly listeners;
|
|
228
|
+
constructor(url: string);
|
|
229
|
+
getStatus(): RealtimeStatus;
|
|
230
|
+
on<K extends keyof RealtimeEventMap>(event: K, handler: Handler<K>): Unsubscribe;
|
|
231
|
+
connect(): void;
|
|
232
|
+
disconnect(code?: number, reason?: string): void;
|
|
233
|
+
send(payload: unknown): boolean;
|
|
234
|
+
private getListenerSet;
|
|
235
|
+
private setStatus;
|
|
236
|
+
private emit;
|
|
237
|
+
private tryParse;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare function useRealtime(url: string): {
|
|
241
|
+
engine: RealtimeEngine;
|
|
242
|
+
status: RealtimeStatus;
|
|
243
|
+
lastMessage: unknown;
|
|
244
|
+
};
|
|
245
|
+
|
|
213
246
|
declare function HomeView(): react_jsx_runtime.JSX.Element;
|
|
214
247
|
|
|
215
|
-
export { type Guid, GuidResultDTO, GuidUtil, Home, HomeView, type IGuidResultDTO, type IRoomInstanceDTO, type IRoomInstanceViewDTO, type IStringResultDTO, type IUniqueViewDTO, type IUserSettingDTO, type IWebSocketClientRequestDTO, type IWebSocketConnectionDTO, RoomInstance, RoomInstanceDTO, RoomInstanceViewDTO, SparkStudioRealtimeSDK, StringResultDTO, UniqueViewDTO, UserSetting, UserSettingDTO, UserStatusType, WebSocketClientRequestDTO, WebSocketConnection, WebSocketConnectionDTO };
|
|
248
|
+
export { type Guid, GuidResultDTO, GuidUtil, Home, HomeView, type IGuidResultDTO, type IRoomInstanceDTO, type IRoomInstanceViewDTO, type IStringResultDTO, type IUniqueViewDTO, type IUserSettingDTO, type IWebSocketClientRequestDTO, type IWebSocketConnectionDTO, RealtimeEngine, type RealtimeEventMap, type RealtimeStatus, RoomInstance, RoomInstanceDTO, RoomInstanceViewDTO, SparkStudioRealtimeSDK, StringResultDTO, UniqueViewDTO, UserSetting, UserSettingDTO, UserStatusType, WebSocketClientRequestDTO, WebSocketConnection, WebSocketConnectionDTO, useRealtime };
|
package/dist/index.js
CHANGED
|
@@ -428,7 +428,104 @@ var UserStatusType = /* @__PURE__ */ ((UserStatusType2) => {
|
|
|
428
428
|
return UserStatusType2;
|
|
429
429
|
})(UserStatusType || {});
|
|
430
430
|
|
|
431
|
-
// src/
|
|
431
|
+
// src/engines/RealtimeEngine.ts
|
|
432
|
+
var RealtimeEngine = class {
|
|
433
|
+
constructor(url) {
|
|
434
|
+
this.url = url;
|
|
435
|
+
}
|
|
436
|
+
ws = null;
|
|
437
|
+
status = "disconnected";
|
|
438
|
+
listeners = {
|
|
439
|
+
status: /* @__PURE__ */ new Set(),
|
|
440
|
+
message: /* @__PURE__ */ new Set(),
|
|
441
|
+
open: /* @__PURE__ */ new Set(),
|
|
442
|
+
close: /* @__PURE__ */ new Set(),
|
|
443
|
+
error: /* @__PURE__ */ new Set()
|
|
444
|
+
};
|
|
445
|
+
getStatus() {
|
|
446
|
+
return this.status;
|
|
447
|
+
}
|
|
448
|
+
on(event, handler) {
|
|
449
|
+
const set = this.getListenerSet(event);
|
|
450
|
+
set.add(handler);
|
|
451
|
+
return () => set.delete(handler);
|
|
452
|
+
}
|
|
453
|
+
connect() {
|
|
454
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
this.setStatus("connecting");
|
|
458
|
+
const ws = new WebSocket(this.url);
|
|
459
|
+
this.ws = ws;
|
|
460
|
+
ws.onopen = (ev) => {
|
|
461
|
+
this.setStatus("connected");
|
|
462
|
+
this.emit("open", ev);
|
|
463
|
+
};
|
|
464
|
+
ws.onmessage = (ev) => {
|
|
465
|
+
this.emit("message", this.tryParse(ev.data));
|
|
466
|
+
};
|
|
467
|
+
ws.onerror = (ev) => {
|
|
468
|
+
this.setStatus("error");
|
|
469
|
+
this.emit("error", ev);
|
|
470
|
+
};
|
|
471
|
+
ws.onclose = (ev) => {
|
|
472
|
+
this.setStatus("disconnected");
|
|
473
|
+
this.emit("close", ev);
|
|
474
|
+
this.ws = null;
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
disconnect(code, reason) {
|
|
478
|
+
this.ws?.close(code, reason);
|
|
479
|
+
this.ws = null;
|
|
480
|
+
this.setStatus("disconnected");
|
|
481
|
+
}
|
|
482
|
+
send(payload) {
|
|
483
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
|
|
484
|
+
const message = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
485
|
+
this.ws.send(message);
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
getListenerSet(event) {
|
|
489
|
+
return this.listeners[event];
|
|
490
|
+
}
|
|
491
|
+
setStatus(next) {
|
|
492
|
+
if (this.status === next) return;
|
|
493
|
+
this.status = next;
|
|
494
|
+
this.emit("status", next);
|
|
495
|
+
}
|
|
496
|
+
emit(event, value) {
|
|
497
|
+
const set = this.getListenerSet(event);
|
|
498
|
+
set.forEach((fn) => fn(value));
|
|
499
|
+
}
|
|
500
|
+
tryParse(data) {
|
|
501
|
+
if (typeof data !== "string") return data;
|
|
502
|
+
try {
|
|
503
|
+
return JSON.parse(data);
|
|
504
|
+
} catch {
|
|
505
|
+
return data;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/hooks/useRealtime.ts
|
|
511
|
+
import { useEffect, useMemo, useState } from "react";
|
|
512
|
+
function useRealtime(url) {
|
|
513
|
+
const engine = useMemo(() => new RealtimeEngine(url), [url]);
|
|
514
|
+
const [status, setStatus] = useState(engine.getStatus());
|
|
515
|
+
const [lastMessage, setLastMessage] = useState(null);
|
|
516
|
+
useEffect(() => {
|
|
517
|
+
const offStatus = engine.on("status", setStatus);
|
|
518
|
+
const offMessage = engine.on("message", setLastMessage);
|
|
519
|
+
return () => {
|
|
520
|
+
offStatus();
|
|
521
|
+
offMessage();
|
|
522
|
+
};
|
|
523
|
+
}, [engine]);
|
|
524
|
+
return { engine, status, lastMessage };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// src/views/HomeContent.tsx
|
|
528
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
432
529
|
import {
|
|
433
530
|
AppSettings,
|
|
434
531
|
AuthenticatorProvider,
|
|
@@ -447,11 +544,124 @@ function HomeView() {
|
|
|
447
544
|
}
|
|
448
545
|
);
|
|
449
546
|
}
|
|
547
|
+
function isSuccessMessage(msg) {
|
|
548
|
+
if (typeof msg === "string") return msg.trim().toLowerCase() === "success";
|
|
549
|
+
if (msg && typeof msg === "object") {
|
|
550
|
+
const o = msg;
|
|
551
|
+
const status = typeof o.Status === "string" ? o.Status : typeof o.status === "string" ? o.status : void 0;
|
|
552
|
+
const message = typeof o.Message === "string" ? o.Message : typeof o.message === "string" ? o.message : void 0;
|
|
553
|
+
const success = typeof o.Success === "boolean" ? o.Success : typeof o.success === "boolean" ? o.success : void 0;
|
|
554
|
+
if (success === true) return true;
|
|
555
|
+
if (status?.trim().toLowerCase() === "success") return true;
|
|
556
|
+
if (message?.trim().toLowerCase() === "success") return true;
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
450
560
|
function HomeContent() {
|
|
451
561
|
const { user } = useUser();
|
|
562
|
+
const wsUrl = useMemo2(() => "ws://localhost:8080", []);
|
|
563
|
+
const { engine, status } = useRealtime(wsUrl);
|
|
564
|
+
const [messages, setMessages] = useState2([]);
|
|
565
|
+
useEffect2(() => {
|
|
566
|
+
if (!user) {
|
|
567
|
+
engine.disconnect();
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
engine.connect();
|
|
571
|
+
return () => engine.disconnect();
|
|
572
|
+
}, [engine, user]);
|
|
573
|
+
useEffect2(() => {
|
|
574
|
+
const offOpen = engine.on("open", () => {
|
|
575
|
+
console.log("\u2705 WS open (socket connected)");
|
|
576
|
+
const token = localStorage.getItem("auth_token");
|
|
577
|
+
engine.send({
|
|
578
|
+
AccessToken: token,
|
|
579
|
+
Data: "{}",
|
|
580
|
+
Type: "InterComUserDTO",
|
|
581
|
+
ControllerName: "UserController",
|
|
582
|
+
MethodName: "Connected"
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
const offMessage = engine.on("message", (msg) => {
|
|
586
|
+
if (isSuccessMessage(msg)) return;
|
|
587
|
+
setMessages((prev) => [...prev, msg]);
|
|
588
|
+
});
|
|
589
|
+
const offClose = engine.on("close", (ev) => {
|
|
590
|
+
console.log("\u{1F6D1} WS close", { code: ev.code, reason: ev.reason });
|
|
591
|
+
});
|
|
592
|
+
const offError = engine.on("error", () => {
|
|
593
|
+
console.log("\u274C WS error");
|
|
594
|
+
});
|
|
595
|
+
const offStatus = engine.on("status", (s) => {
|
|
596
|
+
console.log("\u2139\uFE0F WS status:", s);
|
|
597
|
+
});
|
|
598
|
+
return () => {
|
|
599
|
+
offOpen();
|
|
600
|
+
offMessage();
|
|
601
|
+
offClose();
|
|
602
|
+
offError();
|
|
603
|
+
offStatus();
|
|
604
|
+
};
|
|
605
|
+
}, [engine]);
|
|
452
606
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
453
607
|
/* @__PURE__ */ jsx(UserInfoCard, {}),
|
|
454
|
-
user && /* @__PURE__ */
|
|
608
|
+
user && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 12 }, children: [
|
|
609
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
610
|
+
/* @__PURE__ */ jsx("strong", { children: "WebSocket:" }),
|
|
611
|
+
" ",
|
|
612
|
+
status
|
|
613
|
+
] }),
|
|
614
|
+
/* @__PURE__ */ jsx(
|
|
615
|
+
"button",
|
|
616
|
+
{
|
|
617
|
+
type: "button",
|
|
618
|
+
disabled: status !== "connected",
|
|
619
|
+
onClick: () => {
|
|
620
|
+
const token = localStorage.getItem("auth_token");
|
|
621
|
+
engine.send({
|
|
622
|
+
AccessToken: token,
|
|
623
|
+
Data: JSON.stringify({
|
|
624
|
+
Data: "Hello World",
|
|
625
|
+
UserIds: [user.Id]
|
|
626
|
+
}),
|
|
627
|
+
Type: "UserDataDTO",
|
|
628
|
+
ControllerName: "RoomInstanceController",
|
|
629
|
+
MethodName: "SendToUsers"
|
|
630
|
+
});
|
|
631
|
+
},
|
|
632
|
+
children: "Send message"
|
|
633
|
+
}
|
|
634
|
+
),
|
|
635
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
636
|
+
/* @__PURE__ */ jsx("strong", { children: "Messages:" }),
|
|
637
|
+
/* @__PURE__ */ jsx(
|
|
638
|
+
"div",
|
|
639
|
+
{
|
|
640
|
+
style: {
|
|
641
|
+
maxHeight: 250,
|
|
642
|
+
overflowY: "auto",
|
|
643
|
+
border: "1px solid #ccc",
|
|
644
|
+
padding: 8,
|
|
645
|
+
fontFamily: "monospace",
|
|
646
|
+
fontSize: 12,
|
|
647
|
+
whiteSpace: "pre-wrap"
|
|
648
|
+
},
|
|
649
|
+
children: messages.length === 0 ? /* @__PURE__ */ jsx("div", { children: "(no messages yet)" }) : messages.map((msg, i) => /* @__PURE__ */ jsx(
|
|
650
|
+
"div",
|
|
651
|
+
{
|
|
652
|
+
style: {
|
|
653
|
+
marginBottom: 10,
|
|
654
|
+
paddingBottom: 10,
|
|
655
|
+
borderBottom: "1px dashed #ddd"
|
|
656
|
+
},
|
|
657
|
+
children: typeof msg === "string" ? msg : JSON.stringify(msg, null, 2)
|
|
658
|
+
},
|
|
659
|
+
i
|
|
660
|
+
))
|
|
661
|
+
}
|
|
662
|
+
)
|
|
663
|
+
] })
|
|
664
|
+
] })
|
|
455
665
|
] });
|
|
456
666
|
}
|
|
457
667
|
export {
|
|
@@ -459,6 +669,7 @@ export {
|
|
|
459
669
|
GuidUtil,
|
|
460
670
|
Home,
|
|
461
671
|
HomeView,
|
|
672
|
+
RealtimeEngine,
|
|
462
673
|
RoomInstance,
|
|
463
674
|
RoomInstanceDTO,
|
|
464
675
|
RoomInstanceViewDTO,
|
|
@@ -470,5 +681,6 @@ export {
|
|
|
470
681
|
UserStatusType,
|
|
471
682
|
WebSocketClientRequestDTO,
|
|
472
683
|
WebSocketConnection,
|
|
473
|
-
WebSocketConnectionDTO
|
|
684
|
+
WebSocketConnectionDTO,
|
|
685
|
+
useRealtime
|
|
474
686
|
};
|