@may-db/core 0.1.0
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/Client.d.ts +20 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +163 -0
- package/dist/Client.js.map +1 -0
- package/dist/MayDb.d.ts +20 -0
- package/dist/MayDb.d.ts.map +1 -0
- package/dist/MayDb.js +162 -0
- package/dist/MayDb.js.map +1 -0
- package/dist/Room.d.ts +81 -0
- package/dist/Room.d.ts.map +1 -0
- package/dist/Room.js +382 -0
- package/dist/Room.js.map +1 -0
- package/dist/authStorage.d.ts +8 -0
- package/dist/authStorage.d.ts.map +1 -0
- package/dist/authStorage.js +29 -0
- package/dist/authStorage.js.map +1 -0
- package/dist/hooks/useAllowedRooms.d.ts +12 -0
- package/dist/hooks/useAllowedRooms.d.ts.map +1 -0
- package/dist/hooks/useAllowedRooms.js +29 -0
- package/dist/hooks/useAllowedRooms.js.map +1 -0
- package/dist/hooks/useAsync.d.ts +8 -0
- package/dist/hooks/useAsync.d.ts.map +1 -0
- package/dist/hooks/useAsync.js +29 -0
- package/dist/hooks/useAsync.js.map +1 -0
- package/dist/hooks/useAuth.d.ts +11 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +45 -0
- package/dist/hooks/useAuth.js.map +1 -0
- package/dist/hooks/usePresence.d.ts +30 -0
- package/dist/hooks/usePresence.d.ts.map +1 -0
- package/dist/hooks/usePresence.js +95 -0
- package/dist/hooks/usePresence.js.map +1 -0
- package/dist/hooks/useRoom.d.ts +11 -0
- package/dist/hooks/useRoom.d.ts.map +1 -0
- package/dist/hooks/useRoom.js +37 -0
- package/dist/hooks/useRoom.js.map +1 -0
- package/dist/hooks/useRoomMembers.d.ts +12 -0
- package/dist/hooks/useRoomMembers.d.ts.map +1 -0
- package/dist/hooks/useRoomMembers.js +37 -0
- package/dist/hooks/useRoomMembers.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/loginFlow.d.ts +4 -0
- package/dist/loginFlow.d.ts.map +1 -0
- package/dist/loginFlow.js +48 -0
- package/dist/loginFlow.js.map +1 -0
- package/dist/matrixId.d.ts +9 -0
- package/dist/matrixId.d.ts.map +1 -0
- package/dist/matrixId.js +14 -0
- package/dist/matrixId.js.map +1 -0
- package/dist/presence.d.ts +36 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +63 -0
- package/dist/presence.js.map +1 -0
- package/dist/singletonRoom.d.ts +3 -0
- package/dist/singletonRoom.d.ts.map +1 -0
- package/dist/singletonRoom.js +39 -0
- package/dist/singletonRoom.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/presenceColors.d.ts +3 -0
- package/dist/utils/presenceColors.d.ts.map +1 -0
- package/dist/utils/presenceColors.js +21 -0
- package/dist/utils/presenceColors.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import { getUserColor } from '../utils/presenceColors';
|
|
3
|
+
const PRESENCE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
4
|
+
const SYNC_POLL_INTERVAL_MS = 2500; // Poll every 2.5 seconds
|
|
5
|
+
export const PRESENCE_EVENT = "com.matrixsheets.presence";
|
|
6
|
+
/**
|
|
7
|
+
* Hook for managing presence via Matrix to-device messages.
|
|
8
|
+
* Broadcasts local focus changes to room members and receives their presence updates.
|
|
9
|
+
*/
|
|
10
|
+
export function usePresence({ room }) {
|
|
11
|
+
const roomId = room.roomId;
|
|
12
|
+
const localUserId = room.userId;
|
|
13
|
+
const localUserName = room.userDisplayName || localUserId;
|
|
14
|
+
const localColor = useMemo(() => getUserColor(localUserId), [localUserId]);
|
|
15
|
+
const [remotePresences, setRemotePresences] = useState([]);
|
|
16
|
+
// Track sync token for incremental sync
|
|
17
|
+
const syncTokenRef = useRef(null);
|
|
18
|
+
// Track current local focus to avoid duplicate sends
|
|
19
|
+
const localFocusRef = useRef(null);
|
|
20
|
+
const sendPresence = useCallback(async (focus) => {
|
|
21
|
+
const presence = {
|
|
22
|
+
roomId,
|
|
23
|
+
focus,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
user: {
|
|
26
|
+
id: localUserId,
|
|
27
|
+
name: localUserName,
|
|
28
|
+
color: localColor,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
await room.sendToDevice(PRESENCE_EVENT, presence);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
console.error('Failed to send presence:', err);
|
|
36
|
+
}
|
|
37
|
+
}, [room, roomId, localUserId, localUserName, localColor]);
|
|
38
|
+
const setLocalFocus = useCallback((focus) => {
|
|
39
|
+
if (localFocusRef.current === focus)
|
|
40
|
+
return;
|
|
41
|
+
localFocusRef.current = focus;
|
|
42
|
+
sendPresence(focus);
|
|
43
|
+
}, [sendPresence]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let stopped = false;
|
|
46
|
+
const poll = async () => {
|
|
47
|
+
try {
|
|
48
|
+
const { events, nextBatch } = await room.pollToDeviceEvents(syncTokenRef.current);
|
|
49
|
+
syncTokenRef.current = nextBatch;
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
for (const event of events) {
|
|
52
|
+
if (event.type !== PRESENCE_EVENT)
|
|
53
|
+
continue;
|
|
54
|
+
const content = event.content;
|
|
55
|
+
if (content.roomId !== roomId)
|
|
56
|
+
continue;
|
|
57
|
+
if (content.user.id === localUserId)
|
|
58
|
+
continue;
|
|
59
|
+
if (now - content.timestamp >= PRESENCE_TIMEOUT_MS)
|
|
60
|
+
continue;
|
|
61
|
+
const userId = content.user.id;
|
|
62
|
+
if (content.focus === null) {
|
|
63
|
+
setRemotePresences(prev => prev.filter(p => p.userId !== userId));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
setRemotePresences(prev => [
|
|
67
|
+
...prev.filter(p => p.userId !== userId),
|
|
68
|
+
{ userId, name: content.user.name, color: content.user.color, focus: content.focus },
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.error('Presence poll error:', err);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
poll();
|
|
78
|
+
const interval = setInterval(() => {
|
|
79
|
+
if (!stopped)
|
|
80
|
+
poll();
|
|
81
|
+
}, SYNC_POLL_INTERVAL_MS);
|
|
82
|
+
return () => {
|
|
83
|
+
stopped = true;
|
|
84
|
+
clearInterval(interval);
|
|
85
|
+
};
|
|
86
|
+
}, [room, roomId, localUserId]);
|
|
87
|
+
// Send null presence when unmounting (leaving room)
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
return () => {
|
|
90
|
+
sendPresence(null);
|
|
91
|
+
};
|
|
92
|
+
}, [sendPresence]);
|
|
93
|
+
return { remotePresences, setLocalFocus };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=usePresence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresence.js","sourceRoot":"","sources":["../../src/hooks/usePresence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Room } from "../Room";
|
|
2
|
+
import type { RoomStatus, RoomMetadata } from "../types";
|
|
3
|
+
export declare function useRoom(room: Room): {
|
|
4
|
+
room: Room;
|
|
5
|
+
status: RoomStatus;
|
|
6
|
+
isConnected: boolean;
|
|
7
|
+
canWrite: boolean;
|
|
8
|
+
metadata: RoomMetadata | null;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=useRoom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRoom.d.ts","sourceRoot":"","sources":["../../src/hooks/useRoom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI;;;;;;;EAqCjC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
export function useRoom(room) {
|
|
3
|
+
const [status, setStatus] = useState(room.status);
|
|
4
|
+
const [canWrite, setCanWrite] = useState(room.canWrite);
|
|
5
|
+
const [metadata, setMetadata] = useState(room.metadata);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const unsubscribe = room.subscribe({
|
|
9
|
+
onStatusChange: setStatus,
|
|
10
|
+
onCanWriteChange: setCanWrite,
|
|
11
|
+
onMetadataChange: setMetadata,
|
|
12
|
+
});
|
|
13
|
+
return unsubscribe;
|
|
14
|
+
}, [room]);
|
|
15
|
+
// Auto-connect effect (no cleanup - rooms are cached and stay connected)
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (room.isConnected)
|
|
18
|
+
return;
|
|
19
|
+
(async () => {
|
|
20
|
+
try {
|
|
21
|
+
await room.connect();
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
setError(err instanceof Error ? err : new Error("Failed to connect"));
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
}, [room]);
|
|
28
|
+
return {
|
|
29
|
+
room,
|
|
30
|
+
status,
|
|
31
|
+
isConnected: status === "connected",
|
|
32
|
+
canWrite,
|
|
33
|
+
metadata,
|
|
34
|
+
error,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=useRoom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRoom.js","sourceRoot":"","sources":["../../src/hooks/useRoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI5C,MAAM,UAAU,OAAO,CAAC,IAAU;IAChC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC;YACjC,cAAc,EAAE,SAAS;YACzB,gBAAgB,EAAE,WAAW;YAC7B,gBAAgB,EAAE,WAAW;SAC9B,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,yEAAyE;IACzE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAE7B,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;YACxE,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO;QACL,IAAI;QACJ,MAAM;QACN,WAAW,EAAE,MAAM,KAAK,WAAW;QACnC,QAAQ;QACR,QAAQ;QACR,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Room } from "../Room";
|
|
2
|
+
import type { RoomMember, AccessLevel } from "../types";
|
|
3
|
+
export declare function useRoomMembers(room: Room): {
|
|
4
|
+
members: RoomMember[];
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
refresh: () => Promise<void>;
|
|
8
|
+
invite: (userId: string, access: AccessLevel) => Promise<void>;
|
|
9
|
+
setAccess: (userId: string, access: AccessLevel) => Promise<void>;
|
|
10
|
+
kick: (userId: string) => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=useRoomMembers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRoomMembers.d.ts","sourceRoot":"","sources":["../../src/hooks/useRoomMembers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAExD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI;;;;;qBA0BO,MAAM,UAAU,WAAW;wBAQ3B,MAAM,UAAU,WAAW;mBAQ3B,MAAM;EAQxB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
export function useRoomMembers(room) {
|
|
3
|
+
const [members, setMembers] = useState([]);
|
|
4
|
+
const [loading, setLoading] = useState(true);
|
|
5
|
+
const [error, setError] = useState(null);
|
|
6
|
+
const refresh = useCallback(async () => {
|
|
7
|
+
setLoading(true);
|
|
8
|
+
setError(null);
|
|
9
|
+
try {
|
|
10
|
+
const memberList = await room.getMembers();
|
|
11
|
+
setMembers(memberList);
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
setError(err instanceof Error ? err : new Error("Failed to load members"));
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
setLoading(false);
|
|
18
|
+
}
|
|
19
|
+
}, [room]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
refresh();
|
|
22
|
+
}, [refresh]);
|
|
23
|
+
const invite = useCallback(async (userId, access) => {
|
|
24
|
+
await room.invite(userId, access);
|
|
25
|
+
await refresh();
|
|
26
|
+
}, [room, refresh]);
|
|
27
|
+
const setAccess = useCallback(async (userId, access) => {
|
|
28
|
+
await room.setAccess(userId, access);
|
|
29
|
+
await refresh();
|
|
30
|
+
}, [room, refresh]);
|
|
31
|
+
const kick = useCallback(async (userId) => {
|
|
32
|
+
await room.kick(userId);
|
|
33
|
+
await refresh();
|
|
34
|
+
}, [room, refresh]);
|
|
35
|
+
return { members, loading, error, refresh, invite, setAccess, kick };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=useRoomMembers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRoomMembers.js","sourceRoot":"","sources":["../../src/hooks/useRoomMembers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAIzD,MAAM,UAAU,cAAc,CAC5B,IAAU;IAEV,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3C,UAAU,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CACN,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CACjE,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,MAAc,EAAE,MAAmB,EAAE,EAAE;QAC5C,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAC3B,KAAK,EAAE,MAAc,EAAE,MAAmB,EAAE,EAAE;QAC5C,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,MAAM,IAAI,GAAG,WAAW,CACtB,KAAK,EAAE,MAAc,EAAE,EAAE;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { MayDb } from "./MayDb";
|
|
2
|
+
export { Room } from "./Room";
|
|
3
|
+
export { startLogin, completeLogin } from "./loginFlow";
|
|
4
|
+
export { getAuth, saveAuth, removeAuth, getHomeserver, saveHomeserver, removeHomeserver, } from "./authStorage";
|
|
5
|
+
export { getOrCreateSingletonRoomId } from "./singletonRoom";
|
|
6
|
+
export { isValidMatrixId, extractLocalpart } from "./matrixId";
|
|
7
|
+
export { PRESENCE_EVENT, PRESENCE_TIMEOUT_MS, applyPresenceUpdates, filterRelevantPresenceEvents, isPresenceContent, type PresenceContent, type PresenceUpdate, type RemotePresence, type ToDeviceEvent, } from "./presence";
|
|
8
|
+
export { getUserColor, PRESENCE_COLORS } from "./utils/presenceColors";
|
|
9
|
+
export * from "./types";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { MayDb } from "./MayDb";
|
|
2
|
+
export { Room } from "./Room";
|
|
3
|
+
export { startLogin, completeLogin } from "./loginFlow";
|
|
4
|
+
export { getAuth, saveAuth, removeAuth, getHomeserver, saveHomeserver, removeHomeserver, } from "./authStorage";
|
|
5
|
+
export { getOrCreateSingletonRoomId } from "./singletonRoom";
|
|
6
|
+
export { isValidMatrixId, extractLocalpart } from "./matrixId";
|
|
7
|
+
export { PRESENCE_EVENT, PRESENCE_TIMEOUT_MS, applyPresenceUpdates, filterRelevantPresenceEvents, isPresenceContent, } from "./presence";
|
|
8
|
+
export { getUserColor, PRESENCE_COLORS } from "./utils/presenceColors";
|
|
9
|
+
export * from "./types";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,GAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loginFlow.d.ts","sourceRoot":"","sources":["../src/loginFlow.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQxC,wBAAgB,UAAU,IAAI,IAAI,CASjC;AAgCD,wBAAsB,aAAa,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoB9D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { saveAuth, saveHomeserver, getHomeserver, removeHomeserver, } from "./authStorage";
|
|
2
|
+
export function startLogin() {
|
|
3
|
+
// For now, just login with matrix.org all the time. Later, findhomeserver.com will make this good.
|
|
4
|
+
const homeserver = "https://matrix.org";
|
|
5
|
+
saveHomeserver(homeserver);
|
|
6
|
+
const redirectUrl = encodeURIComponent(window.location.origin + window.location.pathname);
|
|
7
|
+
window.location.href = `${homeserver}/_matrix/client/v3/login/sso/redirect?redirectUrl=${redirectUrl}`;
|
|
8
|
+
}
|
|
9
|
+
async function exchangeLoginToken(homeserver, loginToken) {
|
|
10
|
+
const response = await fetch(`${homeserver}/_matrix/client/v3/login`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
},
|
|
15
|
+
body: JSON.stringify({
|
|
16
|
+
type: "m.login.token",
|
|
17
|
+
token: loginToken,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const error = await response.json();
|
|
22
|
+
throw new Error(error.error || "Login failed");
|
|
23
|
+
}
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
return {
|
|
26
|
+
homeserver,
|
|
27
|
+
accessToken: data.access_token,
|
|
28
|
+
userId: data.user_id,
|
|
29
|
+
deviceId: data.device_id,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function completeLogin() {
|
|
33
|
+
const params = new URLSearchParams(window.location.search);
|
|
34
|
+
const loginToken = params.get("loginToken");
|
|
35
|
+
if (!loginToken) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
window.history.replaceState({}, document.title, window.location.pathname);
|
|
39
|
+
const homeserver = getHomeserver();
|
|
40
|
+
if (!homeserver) {
|
|
41
|
+
throw new Error("No homeserver stored for SSO callback");
|
|
42
|
+
}
|
|
43
|
+
const auth = await exchangeLoginToken(homeserver, loginToken);
|
|
44
|
+
saveAuth(auth);
|
|
45
|
+
removeHomeserver();
|
|
46
|
+
return auth;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=loginFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../src/loginFlow.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,eAAe,CAAC;AASvB,MAAM,UAAU,UAAU;IACxB,mGAAmG;IACnG,MAAM,UAAU,GAAG,oBAAoB,CAAC;IACxC,cAAc,CAAC,UAAU,CAAC,CAAC;IAE3B,MAAM,WAAW,GAAG,kBAAkB,CACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAClD,CAAC;IACF,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,UAAU,qDAAqD,WAAW,EAAE,CAAC;AACzG,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,UAAkB,EAClB,UAAkB;IAElB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,0BAA0B,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,UAAU;SAClB,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,IAAI,GAAkB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAElD,OAAO;QACL,UAAU;QACV,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,MAAM,EAAE,IAAI,CAAC,OAAO;QACpB,QAAQ,EAAE,IAAI,CAAC,SAAS;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAE5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE1E,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC9D,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,gBAAgB,EAAE,CAAC;IAEnB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a Matrix user ID (e.g. @user:matrix.org).
|
|
3
|
+
*/
|
|
4
|
+
export declare function isValidMatrixId(id: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Extract the localpart from a Matrix user ID (@user:server.com -> user).
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractLocalpart(matrixId: string): string;
|
|
9
|
+
//# sourceMappingURL=matrixId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrixId.d.ts","sourceRoot":"","sources":["../src/matrixId.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
package/dist/matrixId.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a Matrix user ID (e.g. @user:matrix.org).
|
|
3
|
+
*/
|
|
4
|
+
export function isValidMatrixId(id) {
|
|
5
|
+
return /^@[^:]+:.+$/.test(id);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Extract the localpart from a Matrix user ID (@user:server.com -> user).
|
|
9
|
+
*/
|
|
10
|
+
export function extractLocalpart(matrixId) {
|
|
11
|
+
const match = matrixId.match(/^@([^:]+)/);
|
|
12
|
+
return match ? match[1] : matrixId;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=matrixId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrixId.js","sourceRoot":"","sources":["../src/matrixId.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const PRESENCE_EVENT = "com.matrixsheets.presence";
|
|
2
|
+
export declare const PRESENCE_TIMEOUT_MS: number;
|
|
3
|
+
export interface PresenceContent {
|
|
4
|
+
roomId: string;
|
|
5
|
+
focus: string | null;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
user: {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
color: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface PresenceUpdate {
|
|
14
|
+
userId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
color: string;
|
|
17
|
+
focus: string | null;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
export interface RemotePresence {
|
|
21
|
+
userId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
focus: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ToDeviceEvent {
|
|
27
|
+
type: string;
|
|
28
|
+
content: unknown;
|
|
29
|
+
}
|
|
30
|
+
export declare function isPresenceContent(value: unknown): value is PresenceContent;
|
|
31
|
+
export declare function applyPresenceUpdates(current: RemotePresence[], updates: PresenceUpdate[]): RemotePresence[];
|
|
32
|
+
export declare function filterRelevantPresenceEvents(events: ToDeviceEvent[], options: {
|
|
33
|
+
roomId: string;
|
|
34
|
+
localUserId: string | null;
|
|
35
|
+
}): PresenceUpdate[];
|
|
36
|
+
//# sourceMappingURL=presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,QAAgB,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAkB1E;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,EAAE,cAAc,EAAE,GACxB,cAAc,EAAE,CAsBlB;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GACA,cAAc,EAAE,CAwBlB"}
|
package/dist/presence.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const PRESENCE_EVENT = "com.matrixsheets.presence";
|
|
2
|
+
export const PRESENCE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
3
|
+
export function isPresenceContent(value) {
|
|
4
|
+
if (!value || typeof value !== "object")
|
|
5
|
+
return false;
|
|
6
|
+
const content = value;
|
|
7
|
+
const user = content.user && typeof content.user === "object"
|
|
8
|
+
? content.user
|
|
9
|
+
: null;
|
|
10
|
+
return (typeof content.roomId === "string" &&
|
|
11
|
+
(content.focus === null || typeof content.focus === "string") &&
|
|
12
|
+
typeof content.timestamp === "number" &&
|
|
13
|
+
!!user &&
|
|
14
|
+
typeof user.id === "string" &&
|
|
15
|
+
typeof user.name === "string" &&
|
|
16
|
+
typeof user.color === "string");
|
|
17
|
+
}
|
|
18
|
+
export function applyPresenceUpdates(current, updates) {
|
|
19
|
+
if (updates.length === 0)
|
|
20
|
+
return current;
|
|
21
|
+
const byUserId = new Map(current.map((presence) => [presence.userId, presence]));
|
|
22
|
+
for (const update of updates) {
|
|
23
|
+
if (update.focus === null) {
|
|
24
|
+
byUserId.delete(update.userId);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
byUserId.set(update.userId, {
|
|
28
|
+
userId: update.userId,
|
|
29
|
+
name: update.name,
|
|
30
|
+
color: update.color,
|
|
31
|
+
focus: update.focus,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return Array.from(byUserId.values());
|
|
35
|
+
}
|
|
36
|
+
export function filterRelevantPresenceEvents(events, options) {
|
|
37
|
+
const { roomId, localUserId } = options;
|
|
38
|
+
if (!localUserId)
|
|
39
|
+
return [];
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
const updates = [];
|
|
42
|
+
for (const event of events) {
|
|
43
|
+
if (event.type !== PRESENCE_EVENT)
|
|
44
|
+
continue;
|
|
45
|
+
if (!isPresenceContent(event.content))
|
|
46
|
+
continue;
|
|
47
|
+
if (event.content.roomId !== roomId)
|
|
48
|
+
continue;
|
|
49
|
+
if (event.content.user.id === localUserId)
|
|
50
|
+
continue;
|
|
51
|
+
if (now - event.content.timestamp >= PRESENCE_TIMEOUT_MS)
|
|
52
|
+
continue;
|
|
53
|
+
updates.push({
|
|
54
|
+
userId: event.content.user.id,
|
|
55
|
+
name: event.content.user.name,
|
|
56
|
+
color: event.content.user.color,
|
|
57
|
+
focus: event.content.focus,
|
|
58
|
+
timestamp: event.content.timestamp,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return updates;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,2BAA2B,CAAC;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAiCjD,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEtD,MAAM,OAAO,GAAG,KAAgC,CAAC;IACjD,MAAM,IAAI,GACR,OAAO,CAAC,IAAI,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAC9C,CAAC,CAAE,OAAO,CAAC,IAAgC;QAC3C,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO,CACL,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;QAClC,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC;QAC7D,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;QACrC,CAAC,CAAC,IAAI;QACN,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;QAC3B,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAC7B,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,OAAyB,EACzB,OAAyB;IAEzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAEzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CACvD,CAAC;IAEF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1B,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/B,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE;YAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,MAAuB,EACvB,OAGC;IAED,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IACxC,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAE5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,SAAS;QAC5C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,SAAS;QAChD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,SAAS;QAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,WAAW;YAAE,SAAS;QACpD,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,mBAAmB;YAAE,SAAS;QAEnE,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YAC7B,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI;YAC7B,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK;YAC/B,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;YAC1B,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS;SACnC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRoom.d.ts","sourceRoot":"","sources":["../src/singletonRoom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,KAAK,EACT,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export async function getOrCreateSingletonRoomId(db, accountDataKey, roomName) {
|
|
2
|
+
const { matrixClient, namespace } = db;
|
|
3
|
+
const userId = matrixClient.getUserId();
|
|
4
|
+
if (!userId)
|
|
5
|
+
throw new Error("Not logged in");
|
|
6
|
+
let roomId = null;
|
|
7
|
+
try {
|
|
8
|
+
const accountData = (await matrixClient.getAccountDataFromServer(namespace));
|
|
9
|
+
roomId = accountData?.[accountDataKey] ?? null;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// No account data yet
|
|
13
|
+
}
|
|
14
|
+
if (roomId) {
|
|
15
|
+
try {
|
|
16
|
+
if (!matrixClient.getRoom(roomId)) {
|
|
17
|
+
await matrixClient.joinRoom(roomId);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Room not accessible, create a new one
|
|
22
|
+
roomId = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!roomId) {
|
|
26
|
+
roomId = await db.createMatrixRoom(roomName);
|
|
27
|
+
let existingData = {};
|
|
28
|
+
try {
|
|
29
|
+
existingData =
|
|
30
|
+
(await matrixClient.getAccountDataFromServer(namespace)) || {};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// No existing data
|
|
34
|
+
}
|
|
35
|
+
await matrixClient.setAccountData(namespace, { ...existingData, [accountDataKey]: roomId });
|
|
36
|
+
}
|
|
37
|
+
return roomId;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=singletonRoom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRoom.js","sourceRoot":"","sources":["../src/singletonRoom.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,EAAS,EACT,cAAsB,EACtB,QAAgB;IAEhB,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IAE9C,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,CAAC,MAAM,YAAY,CAAC,wBAAwB,CAC9D,SAEI,CACL,CAAkC,CAAC;QACpC,MAAM,GAAG,WAAW,EAAE,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,MAAM,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;YACxC,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,MAAM,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAE7C,IAAI,YAAY,GAA2B,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,YAAY;gBACT,CAAC,MAAM,YAAY,CAAC,wBAAwB,CAC3C,SAEI,CACL,CAA4B,IAAI,EAAE,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB;QACrB,CAAC;QAED,MAAM,YAAY,CAAC,cAAc,CAC/B,SAEI,EACJ,EAAE,GAAG,YAAY,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAC9C,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type * as sdk from "matrix-js-sdk";
|
|
2
|
+
export interface AuthData {
|
|
3
|
+
homeserver: string;
|
|
4
|
+
accessToken: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
deviceId: string;
|
|
7
|
+
}
|
|
8
|
+
export type AccessLevel = "view" | "edit" | "owner";
|
|
9
|
+
export interface RoomMember {
|
|
10
|
+
userId: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
accessLevel: AccessLevel;
|
|
13
|
+
}
|
|
14
|
+
export interface InviteInfo {
|
|
15
|
+
roomId: string;
|
|
16
|
+
roomName: string;
|
|
17
|
+
inviter: string;
|
|
18
|
+
alias: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface InviteMonitorOptions {
|
|
21
|
+
shouldJoin?: (invite: InviteInfo) => boolean;
|
|
22
|
+
onJoined: (invite: InviteInfo) => void | Promise<void>;
|
|
23
|
+
pollIntervalMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export type RoomStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
26
|
+
export interface RoomMetadata {
|
|
27
|
+
title: string;
|
|
28
|
+
}
|
|
29
|
+
export type MatrixClient = sdk.MatrixClient;
|
|
30
|
+
/**
|
|
31
|
+
* Interface for what a Room needs from its host client.
|
|
32
|
+
* This allows Room to work with different client implementations.
|
|
33
|
+
*/
|
|
34
|
+
export interface RoomServices {
|
|
35
|
+
matrixClient: MatrixClient;
|
|
36
|
+
removeRoom(roomId: string): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IAC7C,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenceColors.d.ts","sourceRoot":"","sources":["../../src/utils/presenceColors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,UAW3B,CAAC;AAEF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOnD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const PRESENCE_COLORS = [
|
|
2
|
+
"#E91E63", // Pink
|
|
3
|
+
"#9C27B0", // Purple
|
|
4
|
+
"#3F51B5", // Indigo
|
|
5
|
+
"#2196F3", // Blue
|
|
6
|
+
"#00BCD4", // Cyan
|
|
7
|
+
"#009688", // Teal
|
|
8
|
+
"#4CAF50", // Green
|
|
9
|
+
"#FF9800", // Orange
|
|
10
|
+
"#795548", // Brown
|
|
11
|
+
"#607D8B", // Blue Grey
|
|
12
|
+
];
|
|
13
|
+
export function getUserColor(userId) {
|
|
14
|
+
let hash = 0;
|
|
15
|
+
for (let i = 0; i < userId.length; i++) {
|
|
16
|
+
hash = (hash << 5) - hash + userId.charCodeAt(i);
|
|
17
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
18
|
+
}
|
|
19
|
+
return PRESENCE_COLORS[Math.abs(hash) % PRESENCE_COLORS.length];
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=presenceColors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenceColors.js","sourceRoot":"","sources":["../../src/utils/presenceColors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,YAAY;CACxB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,4BAA4B;IAClD,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAClE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@may-db/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"matrix-crdt": "^0.2.1-alpha.1",
|
|
19
|
+
"matrix-js-sdk": "^40.2.0-rc.0",
|
|
20
|
+
"y-protocols": "^1.0.7",
|
|
21
|
+
"yjs": "^13.6.29"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"eslint": "^9.39.1",
|
|
25
|
+
"patch-package": "^8.0.1",
|
|
26
|
+
"typescript": "~5.9.3"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"lint": "eslint src",
|
|
31
|
+
"postinstall": "patch-package"
|
|
32
|
+
}
|
|
33
|
+
}
|