vg-x07df 1.8.1 → 1.8.2
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/channel/index.cjs +353 -58
- package/dist/channel/index.cjs.map +1 -1
- package/dist/channel/index.d.cts +73 -4
- package/dist/channel/index.d.ts +73 -4
- package/dist/channel/index.mjs +351 -59
- package/dist/channel/index.mjs.map +1 -1
- package/dist/index.cjs +159 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +160 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{types-CWNlFnPU.d.cts → types-DbGItZmC.d.cts} +24 -1
- package/dist/{types-CWNlFnPU.d.ts → types-DbGItZmC.d.ts} +24 -1
- package/package.json +1 -1
package/dist/channel/index.cjs
CHANGED
|
@@ -468,37 +468,29 @@ var ChatService = class {
|
|
|
468
468
|
this.room.registerTextStreamHandler(
|
|
469
469
|
"chat:v1",
|
|
470
470
|
async (reader, participantInfo) => {
|
|
471
|
-
console.log("Got here: chat:v1", participantInfo.identity);
|
|
472
471
|
try {
|
|
473
472
|
const text = await reader.readAll();
|
|
474
|
-
console.log("Got here: chat:v1", text);
|
|
473
|
+
console.log("Got here: chat:v1", text, participantInfo.identity);
|
|
475
474
|
this.handleIncomingMessage(text);
|
|
476
475
|
} catch (error) {
|
|
477
476
|
logger.error("Error reading text stream", error);
|
|
478
477
|
}
|
|
479
478
|
}
|
|
480
479
|
);
|
|
481
|
-
this.room.registerByteStreamHandler(
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
Topic: ${info.topic}
|
|
494
|
-
Timestamp: ${info.timestamp}
|
|
495
|
-
ID: ${info.id}
|
|
496
|
-
Size: ${info.size}`
|
|
497
|
-
);
|
|
498
|
-
} catch (error) {
|
|
499
|
-
logger.error("Error reading file stream", error);
|
|
480
|
+
this.room.registerByteStreamHandler(
|
|
481
|
+
"chat:v1",
|
|
482
|
+
async (reader, participantInfo) => {
|
|
483
|
+
try {
|
|
484
|
+
const info = reader.info;
|
|
485
|
+
const filename = info.name;
|
|
486
|
+
const parts = (await reader.readAll()).map((chunk) => chunk.slice());
|
|
487
|
+
const fileBlob = new Blob(parts, { type: info.mimeType });
|
|
488
|
+
this.handleIncomingFile(fileBlob, filename, info.mimeType);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
logger.error("Error reading file stream", error);
|
|
491
|
+
}
|
|
500
492
|
}
|
|
501
|
-
|
|
493
|
+
);
|
|
502
494
|
this.isSubscribed = true;
|
|
503
495
|
}
|
|
504
496
|
unsubscribe() {
|
|
@@ -517,7 +509,6 @@ var ChatService = class {
|
|
|
517
509
|
return;
|
|
518
510
|
}
|
|
519
511
|
if (content.length === 0 && typeof file !== "undefined") {
|
|
520
|
-
console.log("file present", typeof file);
|
|
521
512
|
content = file.name;
|
|
522
513
|
}
|
|
523
514
|
const validation = validateContent(content);
|
|
@@ -566,7 +557,6 @@ var ChatService = class {
|
|
|
566
557
|
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
567
558
|
topic: "chat:v1"
|
|
568
559
|
});
|
|
569
|
-
console.log("Sent message", entry, envelope);
|
|
570
560
|
if (file) {
|
|
571
561
|
await this.room.localParticipant.sendFile(file, {
|
|
572
562
|
mimeType: file.type,
|
|
@@ -881,13 +871,310 @@ function useChat() {
|
|
|
881
871
|
};
|
|
882
872
|
}
|
|
883
873
|
var defaultState2 = {
|
|
874
|
+
raisedHands: /* @__PURE__ */ new Map(),
|
|
875
|
+
nextOrder: 1
|
|
876
|
+
};
|
|
877
|
+
var useRaiseHandStore = zustand.create()(
|
|
878
|
+
immer.immer((set, get) => ({
|
|
879
|
+
...defaultState2,
|
|
880
|
+
raiseHand: (participantId) => set((state) => {
|
|
881
|
+
if (!state.raisedHands.has(participantId)) {
|
|
882
|
+
state.raisedHands.set(participantId, {
|
|
883
|
+
ts: Date.now(),
|
|
884
|
+
order: state.nextOrder++
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
}),
|
|
888
|
+
lowerHand: (participantId) => set((state) => {
|
|
889
|
+
state.raisedHands.delete(participantId);
|
|
890
|
+
}),
|
|
891
|
+
lowerAllHands: () => set((state) => {
|
|
892
|
+
state.raisedHands.clear();
|
|
893
|
+
state.nextOrder = 1;
|
|
894
|
+
}),
|
|
895
|
+
isHandRaised: (participantId) => get().raisedHands.has(participantId),
|
|
896
|
+
getRaisedHandOrder: (participantId) => {
|
|
897
|
+
const hand = get().raisedHands.get(participantId);
|
|
898
|
+
return hand?.order ?? null;
|
|
899
|
+
},
|
|
900
|
+
upsertParticipantInfo: (id, info) => {
|
|
901
|
+
},
|
|
902
|
+
clear: () => set(() => ({
|
|
903
|
+
raisedHands: /* @__PURE__ */ new Map(),
|
|
904
|
+
nextOrder: 1
|
|
905
|
+
}))
|
|
906
|
+
}))
|
|
907
|
+
);
|
|
908
|
+
function applyIncomingRaiseHand(envelope) {
|
|
909
|
+
const { raiseHand, lowerHand, lowerAllHands, upsertParticipantInfo } = useRaiseHandStore.getState();
|
|
910
|
+
if (envelope.sender.info) {
|
|
911
|
+
upsertParticipantInfo(envelope.sender.id, envelope.sender.info);
|
|
912
|
+
}
|
|
913
|
+
switch (envelope.payload.action) {
|
|
914
|
+
case "raise": {
|
|
915
|
+
raiseHand(envelope.sender.id);
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
case "lower": {
|
|
919
|
+
const targetId = envelope.payload.targetId || envelope.sender.id;
|
|
920
|
+
lowerHand(targetId);
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
case "lower-all": {
|
|
924
|
+
lowerAllHands();
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/channel/raiseHand/service.ts
|
|
931
|
+
var logger2 = createLogger("raise-hand");
|
|
932
|
+
var RaiseHandService = class {
|
|
933
|
+
constructor(room) {
|
|
934
|
+
this.isSubscribed = false;
|
|
935
|
+
this.room = room;
|
|
936
|
+
}
|
|
937
|
+
isRoomReady() {
|
|
938
|
+
if (!this.room) {
|
|
939
|
+
logger2.warn("Room not initialized");
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
if (this.room.state !== livekitClient.ConnectionState.Connected) {
|
|
943
|
+
logger2.warn("Room not connected", { state: this.room.state });
|
|
944
|
+
return false;
|
|
945
|
+
}
|
|
946
|
+
if (!this.room.localParticipant) {
|
|
947
|
+
logger2.warn("Local participant not available");
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
952
|
+
subscribe() {
|
|
953
|
+
if (this.isSubscribed) return;
|
|
954
|
+
this.room.registerTextStreamHandler("raise-hand:v1", async (reader) => {
|
|
955
|
+
try {
|
|
956
|
+
const text = await reader.readAll();
|
|
957
|
+
this.handleIncoming(text);
|
|
958
|
+
} catch (err) {
|
|
959
|
+
logger2.error("Error reading raise-hand stream", err);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
this.isSubscribed = true;
|
|
963
|
+
logger2.info("RaiseHandService subscribed");
|
|
964
|
+
}
|
|
965
|
+
unsubscribe() {
|
|
966
|
+
this.isSubscribed = false;
|
|
967
|
+
logger2.info("RaiseHandService unsubscribed");
|
|
968
|
+
}
|
|
969
|
+
getLocalParticipantId() {
|
|
970
|
+
return this.room.localParticipant.identity;
|
|
971
|
+
}
|
|
972
|
+
async raiseHand() {
|
|
973
|
+
if (!this.isRoomReady()) {
|
|
974
|
+
useRtcStore.getState().addError({
|
|
975
|
+
code: "RAISE_HAND_ROOM_NOT_READY",
|
|
976
|
+
message: "Cannot raise hand: room not connected",
|
|
977
|
+
timestamp: Date.now()
|
|
978
|
+
});
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const senderInfo = this.getSenderInfo();
|
|
982
|
+
useRaiseHandStore.getState().raiseHand(senderInfo.id);
|
|
983
|
+
try {
|
|
984
|
+
const envelope = {
|
|
985
|
+
v: 1,
|
|
986
|
+
kind: "raise-hand",
|
|
987
|
+
roomId: this.room.name,
|
|
988
|
+
ts: Date.now(),
|
|
989
|
+
sender: senderInfo,
|
|
990
|
+
payload: {
|
|
991
|
+
action: "raise"
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
995
|
+
topic: "raise-hand:v1"
|
|
996
|
+
});
|
|
997
|
+
logger2.debug("Hand raised");
|
|
998
|
+
} catch (error) {
|
|
999
|
+
logger2.error("Failed to raise hand", error);
|
|
1000
|
+
useRtcStore.getState().addError({
|
|
1001
|
+
code: "RAISE_HAND_SEND_FAILED",
|
|
1002
|
+
message: error instanceof Error ? error.message : "Failed to raise hand",
|
|
1003
|
+
timestamp: Date.now()
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async lowerHand(targetId) {
|
|
1008
|
+
if (!this.isRoomReady()) {
|
|
1009
|
+
useRtcStore.getState().addError({
|
|
1010
|
+
code: "LOWER_HAND_ROOM_NOT_READY",
|
|
1011
|
+
message: "Cannot lower hand: room not connected",
|
|
1012
|
+
timestamp: Date.now()
|
|
1013
|
+
});
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const senderInfo = this.getSenderInfo();
|
|
1017
|
+
const participantId = targetId || senderInfo.id;
|
|
1018
|
+
useRaiseHandStore.getState().lowerHand(participantId);
|
|
1019
|
+
try {
|
|
1020
|
+
const envelope = {
|
|
1021
|
+
v: 1,
|
|
1022
|
+
kind: "raise-hand",
|
|
1023
|
+
roomId: this.room.name,
|
|
1024
|
+
ts: Date.now(),
|
|
1025
|
+
sender: senderInfo,
|
|
1026
|
+
payload: {
|
|
1027
|
+
action: "lower",
|
|
1028
|
+
targetId: participantId
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
1032
|
+
topic: "raise-hand:v1"
|
|
1033
|
+
});
|
|
1034
|
+
logger2.debug("Hand lowered", { targetId: participantId });
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
logger2.error("Failed to lower hand", error);
|
|
1037
|
+
useRtcStore.getState().addError({
|
|
1038
|
+
code: "LOWER_HAND_SEND_FAILED",
|
|
1039
|
+
message: error instanceof Error ? error.message : "Failed to lower hand",
|
|
1040
|
+
timestamp: Date.now()
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
async lowerAllHands() {
|
|
1045
|
+
if (!this.isRoomReady()) {
|
|
1046
|
+
useRtcStore.getState().addError({
|
|
1047
|
+
code: "LOWER_ALL_HANDS_ROOM_NOT_READY",
|
|
1048
|
+
message: "Cannot lower all hands: room not connected",
|
|
1049
|
+
timestamp: Date.now()
|
|
1050
|
+
});
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const senderInfo = this.getSenderInfo();
|
|
1054
|
+
useRaiseHandStore.getState().lowerAllHands();
|
|
1055
|
+
try {
|
|
1056
|
+
const envelope = {
|
|
1057
|
+
v: 1,
|
|
1058
|
+
kind: "raise-hand",
|
|
1059
|
+
roomId: this.room.name,
|
|
1060
|
+
ts: Date.now(),
|
|
1061
|
+
sender: senderInfo,
|
|
1062
|
+
payload: {
|
|
1063
|
+
action: "lower-all"
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
1067
|
+
topic: "raise-hand:v1"
|
|
1068
|
+
});
|
|
1069
|
+
logger2.debug("All hands lowered");
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
logger2.error("Failed to lower all hands", error);
|
|
1072
|
+
useRtcStore.getState().addError({
|
|
1073
|
+
code: "LOWER_ALL_HANDS_SEND_FAILED",
|
|
1074
|
+
message: error instanceof Error ? error.message : "Failed to lower all hands",
|
|
1075
|
+
timestamp: Date.now()
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
handleIncoming(text) {
|
|
1080
|
+
try {
|
|
1081
|
+
const parsed = JSON.parse(text);
|
|
1082
|
+
if (!this.isValidEnvelope(parsed)) {
|
|
1083
|
+
logger2.warn("Invalid raise-hand envelope received", parsed);
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
if (parsed.sender.id === this.getLocalParticipantId()) {
|
|
1087
|
+
logger2.debug("Ignoring self-echo raise-hand message");
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
applyIncomingRaiseHand(parsed);
|
|
1091
|
+
logger2.debug("Raise-hand message received", {
|
|
1092
|
+
action: parsed.payload.action
|
|
1093
|
+
});
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
logger2.error("Error parsing incoming raise-hand message", error);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
isValidEnvelope(e) {
|
|
1099
|
+
return e && e.v === 1 && e.kind === "raise-hand" && typeof e.roomId === "string" && e.roomId === this.room.name && typeof e.ts === "number" && e.ts > 0 && typeof e.sender?.id === "string" && typeof e.payload?.action === "string" && ["raise", "lower", "lower-all"].includes(e.payload.action);
|
|
1100
|
+
}
|
|
1101
|
+
getSenderInfo() {
|
|
1102
|
+
const localParticipant = this.room.localParticipant;
|
|
1103
|
+
const sender = {
|
|
1104
|
+
id: localParticipant.identity
|
|
1105
|
+
};
|
|
1106
|
+
if (localParticipant.metadata) {
|
|
1107
|
+
try {
|
|
1108
|
+
sender.info = JSON.parse(
|
|
1109
|
+
localParticipant.metadata
|
|
1110
|
+
);
|
|
1111
|
+
} catch {
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return sender;
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
var logger3 = createLogger("raise-hand:hook");
|
|
1118
|
+
function useRaiseHand() {
|
|
1119
|
+
const service = useFeatureService("raise-hand");
|
|
1120
|
+
const raisedHands = useRaiseHandStore((state) => state.raisedHands);
|
|
1121
|
+
const isHandRaised = useRaiseHandStore((state) => state.isHandRaised);
|
|
1122
|
+
const getRaisedHandOrder = useRaiseHandStore(
|
|
1123
|
+
(state) => state.getRaisedHandOrder
|
|
1124
|
+
);
|
|
1125
|
+
const raiseHand = react.useCallback(async () => {
|
|
1126
|
+
if (!service) {
|
|
1127
|
+
logger3.error("Cannot raise hand: service not ready");
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
return service.raiseHand();
|
|
1131
|
+
}, [service]);
|
|
1132
|
+
const lowerHand = react.useCallback(
|
|
1133
|
+
async (participantId) => {
|
|
1134
|
+
if (!service) {
|
|
1135
|
+
logger3.error("Cannot lower hand: service not ready");
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
return service.lowerHand(participantId);
|
|
1139
|
+
},
|
|
1140
|
+
[service]
|
|
1141
|
+
);
|
|
1142
|
+
const lowerAllHands = react.useCallback(async () => {
|
|
1143
|
+
if (!service) {
|
|
1144
|
+
logger3.error("Cannot lower all hands: service not ready");
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
return service.lowerAllHands();
|
|
1148
|
+
}, [service]);
|
|
1149
|
+
const getRaisedHands = react.useCallback(() => {
|
|
1150
|
+
const hands = [];
|
|
1151
|
+
for (const [participantId, hand] of raisedHands.entries()) {
|
|
1152
|
+
hands.push({
|
|
1153
|
+
participantId,
|
|
1154
|
+
order: hand.order,
|
|
1155
|
+
ts: hand.ts
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
return hands.sort((a, b) => a.order - b.order);
|
|
1159
|
+
}, [raisedHands]);
|
|
1160
|
+
return {
|
|
1161
|
+
raiseHand,
|
|
1162
|
+
lowerHand,
|
|
1163
|
+
lowerAllHands,
|
|
1164
|
+
isHandRaised,
|
|
1165
|
+
getRaisedHandOrder,
|
|
1166
|
+
getRaisedHands,
|
|
1167
|
+
isReady: !!service
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
var defaultState3 = {
|
|
884
1171
|
reactions: /* @__PURE__ */ new Map(),
|
|
885
1172
|
participantCache: {},
|
|
886
1173
|
ttlMs: 4e3
|
|
887
1174
|
};
|
|
888
1175
|
var useReactionsStore = zustand.create()(
|
|
889
1176
|
immer.immer((set) => ({
|
|
890
|
-
...
|
|
1177
|
+
...defaultState3,
|
|
891
1178
|
setReaction: (participantId, reaction) => set((state) => {
|
|
892
1179
|
state.reactions.set(participantId, reaction);
|
|
893
1180
|
}),
|
|
@@ -908,7 +1195,7 @@ var useReactionsStore = zustand.create()(
|
|
|
908
1195
|
clear: () => set(() => ({
|
|
909
1196
|
reactions: /* @__PURE__ */ new Map(),
|
|
910
1197
|
participantCache: {},
|
|
911
|
-
ttlMs:
|
|
1198
|
+
ttlMs: defaultState3.ttlMs
|
|
912
1199
|
}))
|
|
913
1200
|
}))
|
|
914
1201
|
);
|
|
@@ -940,7 +1227,7 @@ function generateNonce() {
|
|
|
940
1227
|
}
|
|
941
1228
|
|
|
942
1229
|
// src/channel/reactions/service.ts
|
|
943
|
-
var
|
|
1230
|
+
var logger5 = createLogger("reactions");
|
|
944
1231
|
var ReactionsService = class {
|
|
945
1232
|
constructor(room) {
|
|
946
1233
|
this.isSubscribed = false;
|
|
@@ -951,15 +1238,15 @@ var ReactionsService = class {
|
|
|
951
1238
|
}
|
|
952
1239
|
isRoomReady() {
|
|
953
1240
|
if (!this.room) {
|
|
954
|
-
|
|
1241
|
+
logger5.warn("Room not initialized");
|
|
955
1242
|
return false;
|
|
956
1243
|
}
|
|
957
1244
|
if (this.room.state !== livekitClient.ConnectionState.Connected) {
|
|
958
|
-
|
|
1245
|
+
logger5.warn("Room not connected", { state: this.room.state });
|
|
959
1246
|
return false;
|
|
960
1247
|
}
|
|
961
1248
|
if (!this.room.localParticipant) {
|
|
962
|
-
|
|
1249
|
+
logger5.warn("Local participant not available");
|
|
963
1250
|
return false;
|
|
964
1251
|
}
|
|
965
1252
|
return true;
|
|
@@ -977,14 +1264,14 @@ var ReactionsService = class {
|
|
|
977
1264
|
const text = await reader.readAll();
|
|
978
1265
|
this.handleIncoming(text);
|
|
979
1266
|
} catch (err) {
|
|
980
|
-
|
|
1267
|
+
logger5.error("Error reading reactions stream", err);
|
|
981
1268
|
}
|
|
982
1269
|
});
|
|
983
1270
|
this.pruneInterval = setInterval(() => {
|
|
984
1271
|
useReactionsStore.getState().pruneExpired();
|
|
985
1272
|
}, 1e3);
|
|
986
1273
|
this.isSubscribed = true;
|
|
987
|
-
|
|
1274
|
+
logger5.info("ReactionsService subscribed");
|
|
988
1275
|
}
|
|
989
1276
|
unsubscribe() {
|
|
990
1277
|
this.isSubscribed = false;
|
|
@@ -992,7 +1279,7 @@ var ReactionsService = class {
|
|
|
992
1279
|
clearInterval(this.pruneInterval);
|
|
993
1280
|
this.pruneInterval = void 0;
|
|
994
1281
|
}
|
|
995
|
-
|
|
1282
|
+
logger5.info("ReactionsService unsubscribed");
|
|
996
1283
|
}
|
|
997
1284
|
getLocalParticipantId() {
|
|
998
1285
|
return this.room.localParticipant.identity;
|
|
@@ -1016,7 +1303,7 @@ var ReactionsService = class {
|
|
|
1016
1303
|
return;
|
|
1017
1304
|
}
|
|
1018
1305
|
if (!this.canSendNow()) {
|
|
1019
|
-
|
|
1306
|
+
logger5.debug("Rate limited, skipping send");
|
|
1020
1307
|
return;
|
|
1021
1308
|
}
|
|
1022
1309
|
const senderInfo = this.getSenderInfo();
|
|
@@ -1042,9 +1329,9 @@ var ReactionsService = class {
|
|
|
1042
1329
|
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
1043
1330
|
topic: "reactions:v1"
|
|
1044
1331
|
});
|
|
1045
|
-
|
|
1332
|
+
logger5.debug("Reaction sent", { emoji });
|
|
1046
1333
|
} catch (error) {
|
|
1047
|
-
|
|
1334
|
+
logger5.error("Failed to send reaction", error);
|
|
1048
1335
|
useRtcStore.getState().addError({
|
|
1049
1336
|
code: "REACTIONS_SEND_FAILED",
|
|
1050
1337
|
message: error instanceof Error ? error.message : "Failed to send reaction",
|
|
@@ -1057,16 +1344,16 @@ var ReactionsService = class {
|
|
|
1057
1344
|
try {
|
|
1058
1345
|
const parsed = JSON.parse(text);
|
|
1059
1346
|
if (!this.isValidEnvelope(parsed)) {
|
|
1060
|
-
|
|
1347
|
+
logger5.warn("Invalid reaction envelope received", parsed);
|
|
1061
1348
|
return;
|
|
1062
1349
|
}
|
|
1063
1350
|
if (parsed.sender.id === this.getLocalParticipantId()) {
|
|
1064
|
-
|
|
1351
|
+
logger5.debug("Ignoring self-echo reaction");
|
|
1065
1352
|
return;
|
|
1066
1353
|
}
|
|
1067
1354
|
const lastTs = this.lastRemoteTs.get(parsed.sender.id) ?? Number.NEGATIVE_INFINITY;
|
|
1068
1355
|
if (parsed.ts < lastTs) {
|
|
1069
|
-
|
|
1356
|
+
logger5.debug("Ignoring out-of-order reaction", {
|
|
1070
1357
|
sender: parsed.sender.id,
|
|
1071
1358
|
ts: parsed.ts,
|
|
1072
1359
|
lastTs
|
|
@@ -1075,9 +1362,9 @@ var ReactionsService = class {
|
|
|
1075
1362
|
}
|
|
1076
1363
|
this.lastRemoteTs.set(parsed.sender.id, parsed.ts);
|
|
1077
1364
|
applyIncomingReaction(parsed);
|
|
1078
|
-
|
|
1365
|
+
logger5.debug("Reaction received", { emoji: parsed.payload.emoji });
|
|
1079
1366
|
} catch (error) {
|
|
1080
|
-
|
|
1367
|
+
logger5.error("Error parsing incoming reaction", error);
|
|
1081
1368
|
}
|
|
1082
1369
|
}
|
|
1083
1370
|
isValidEnvelope(e) {
|
|
@@ -1099,7 +1386,7 @@ var ReactionsService = class {
|
|
|
1099
1386
|
return sender;
|
|
1100
1387
|
}
|
|
1101
1388
|
};
|
|
1102
|
-
var
|
|
1389
|
+
var logger6 = createLogger("reactions:hook");
|
|
1103
1390
|
function useReactions() {
|
|
1104
1391
|
const service = useFeatureService("reactions");
|
|
1105
1392
|
const reactions = useReactionsStore((state) => state.reactions);
|
|
@@ -1118,7 +1405,7 @@ function useReactions() {
|
|
|
1118
1405
|
const sendReaction = react.useCallback(
|
|
1119
1406
|
async (emoji) => {
|
|
1120
1407
|
if (!service) {
|
|
1121
|
-
|
|
1408
|
+
logger6.error("Cannot send reaction: service not ready");
|
|
1122
1409
|
return;
|
|
1123
1410
|
}
|
|
1124
1411
|
return service.sendReaction(emoji);
|
|
@@ -1148,10 +1435,15 @@ var FEATURES = {
|
|
|
1148
1435
|
name: "reactions",
|
|
1149
1436
|
createService: (room) => new ReactionsService(room),
|
|
1150
1437
|
cleanupStore: () => useReactionsStore.getState().clear()
|
|
1438
|
+
},
|
|
1439
|
+
"raise-hand": {
|
|
1440
|
+
name: "raise-hand",
|
|
1441
|
+
createService: (room) => new RaiseHandService(room),
|
|
1442
|
+
cleanupStore: () => useRaiseHandStore.getState().clear()
|
|
1151
1443
|
}
|
|
1152
1444
|
};
|
|
1153
|
-
var DEFAULT_FEATURES = ["chat", "reactions"];
|
|
1154
|
-
var
|
|
1445
|
+
var DEFAULT_FEATURES = ["chat", "reactions", "raise-hand"];
|
|
1446
|
+
var logger7 = createLogger("channels:provider");
|
|
1155
1447
|
function DataChannelProvider({
|
|
1156
1448
|
room,
|
|
1157
1449
|
features = DEFAULT_FEATURES,
|
|
@@ -1161,52 +1453,52 @@ function DataChannelProvider({
|
|
|
1161
1453
|
const [isReady, setIsReady] = react.useState(false);
|
|
1162
1454
|
react.useEffect(() => {
|
|
1163
1455
|
if (!room) {
|
|
1164
|
-
|
|
1456
|
+
logger7.warn("DataChannelProvider mounted without room");
|
|
1165
1457
|
return;
|
|
1166
1458
|
}
|
|
1167
|
-
|
|
1459
|
+
logger7.debug("Initializing features", { features });
|
|
1168
1460
|
for (const featureName of features) {
|
|
1169
1461
|
const feature = FEATURES[featureName];
|
|
1170
1462
|
if (!feature) {
|
|
1171
|
-
|
|
1463
|
+
logger7.warn(`Feature "${featureName}" not found in registry`);
|
|
1172
1464
|
continue;
|
|
1173
1465
|
}
|
|
1174
1466
|
try {
|
|
1175
|
-
|
|
1467
|
+
logger7.debug(`Initializing feature: ${featureName}`);
|
|
1176
1468
|
const service = feature.createService(room);
|
|
1177
1469
|
service.subscribe();
|
|
1178
1470
|
services.current.set(featureName, service);
|
|
1179
|
-
|
|
1471
|
+
logger7.info(`Feature "${featureName}" initialized`);
|
|
1180
1472
|
} catch (error) {
|
|
1181
|
-
|
|
1473
|
+
logger7.error(`Failed to initialize feature "${featureName}"`, error);
|
|
1182
1474
|
}
|
|
1183
1475
|
}
|
|
1184
1476
|
setIsReady(true);
|
|
1185
|
-
|
|
1477
|
+
logger7.info("All features initialized");
|
|
1186
1478
|
return () => {
|
|
1187
|
-
|
|
1479
|
+
logger7.debug("Cleaning up features");
|
|
1188
1480
|
services.current.forEach((service, name) => {
|
|
1189
1481
|
try {
|
|
1190
|
-
|
|
1482
|
+
logger7.debug(`Unsubscribing feature: ${name}`);
|
|
1191
1483
|
service.unsubscribe();
|
|
1192
1484
|
} catch (error) {
|
|
1193
|
-
|
|
1485
|
+
logger7.error(`Failed to unsubscribe feature "${name}"`, error);
|
|
1194
1486
|
}
|
|
1195
1487
|
});
|
|
1196
1488
|
for (const featureName of features) {
|
|
1197
1489
|
const feature = FEATURES[featureName];
|
|
1198
1490
|
if (feature?.cleanupStore) {
|
|
1199
1491
|
try {
|
|
1200
|
-
|
|
1492
|
+
logger7.debug(`Cleaning store for feature: ${featureName}`);
|
|
1201
1493
|
feature.cleanupStore();
|
|
1202
1494
|
} catch (error) {
|
|
1203
|
-
|
|
1495
|
+
logger7.error(`Failed to cleanup store for "${featureName}"`, error);
|
|
1204
1496
|
}
|
|
1205
1497
|
}
|
|
1206
1498
|
}
|
|
1207
1499
|
services.current.clear();
|
|
1208
1500
|
setIsReady(false);
|
|
1209
|
-
|
|
1501
|
+
logger7.info("Features cleanup complete");
|
|
1210
1502
|
};
|
|
1211
1503
|
}, [room, features]);
|
|
1212
1504
|
const contextValue = react.useMemo(
|
|
@@ -1221,6 +1513,7 @@ function DataChannelProvider({
|
|
|
1221
1513
|
|
|
1222
1514
|
exports.ChatService = ChatService;
|
|
1223
1515
|
exports.DataChannelProvider = DataChannelProvider;
|
|
1516
|
+
exports.RaiseHandService = RaiseHandService;
|
|
1224
1517
|
exports.ReactionsService = ReactionsService;
|
|
1225
1518
|
exports.applyIncomingReaction = applyIncomingReaction;
|
|
1226
1519
|
exports.compareEntries = compareEntries;
|
|
@@ -1232,6 +1525,8 @@ exports.useChat = useChat;
|
|
|
1232
1525
|
exports.useChatStore = useChatStore;
|
|
1233
1526
|
exports.useDataChannelContext = useDataChannelContext;
|
|
1234
1527
|
exports.useFeatureService = useFeatureService;
|
|
1528
|
+
exports.useRaiseHand = useRaiseHand;
|
|
1529
|
+
exports.useRaiseHandStore = useRaiseHandStore;
|
|
1235
1530
|
exports.useReactions = useReactions;
|
|
1236
1531
|
exports.useReactionsStore = useReactionsStore;
|
|
1237
1532
|
exports.validateContent = validateContent;
|