@realtimexsco/live-chat 1.4.14 → 1.4.15
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/README.md +5 -3
- package/dist/index.cjs +112 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +112 -67
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +9 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
6
6
|
[](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
7
|
-
[](
|
|
7
|
+
[](#license)
|
|
8
8
|
|
|
9
9
|
| | |
|
|
10
10
|
|---|---|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
| **Styles** | `@realtimexsco/live-chat/styles.css` |
|
|
13
13
|
| **Requires** | React 17+, Tailwind CSS **v4**, Zustand 4+ |
|
|
14
14
|
| **Docs** | [Package documentation](https://realtimex.softwareco.com/documentations/package) |
|
|
15
|
-
| **License** | [MIT](
|
|
15
|
+
| **License** | [MIT](#license) |
|
|
16
16
|
|
|
17
17
|
Socket.IO, REST client, Zustand store, Radix UI, emoji picker, and chat UI are **bundled**. Your app only installs peer dependencies.
|
|
18
18
|
|
|
@@ -61,10 +61,12 @@ DMs · groups · Socket.IO · reactions · replies · forward · edit/delete ·
|
|
|
61
61
|
|-------|------|
|
|
62
62
|
| Full setup & docs | [https://realtimex.softwareco.com/documentations/package](https://realtimex.softwareco.com/documentations/package) |
|
|
63
63
|
| npm package | [npmjs.com/package/@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat) |
|
|
64
|
-
| License | [MIT](
|
|
64
|
+
| License | [MIT](#license) |
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
+
<a id="license"></a>
|
|
69
|
+
|
|
68
70
|
## License
|
|
69
71
|
|
|
70
72
|
The MIT License (MIT)
|
package/dist/index.cjs
CHANGED
|
@@ -12922,9 +12922,91 @@ init_useStore();
|
|
|
12922
12922
|
|
|
12923
12923
|
// src/hooks/useGroupInfo.ts
|
|
12924
12924
|
init_useStore();
|
|
12925
|
+
|
|
12926
|
+
// src/lib/effective-settings.helpers.ts
|
|
12927
|
+
function minutesToMs(minutes) {
|
|
12928
|
+
return Math.max(0, minutes) * 60 * 1e3;
|
|
12929
|
+
}
|
|
12930
|
+
function getEditWindowMs(timers, isGroup) {
|
|
12931
|
+
return minutesToMs(isGroup ? timers.groupEditMinutes : timers.dmEditMinutes);
|
|
12932
|
+
}
|
|
12933
|
+
function getDeleteWindowMs(timers, isGroup) {
|
|
12934
|
+
return minutesToMs(
|
|
12935
|
+
isGroup ? timers.groupDeleteMinutes : timers.dmDeleteMinutes
|
|
12936
|
+
);
|
|
12937
|
+
}
|
|
12938
|
+
function isWithinWindow(createdAt, windowMs) {
|
|
12939
|
+
if (!createdAt) return false;
|
|
12940
|
+
const date = new Date(createdAt);
|
|
12941
|
+
if (isNaN(date.getTime())) return false;
|
|
12942
|
+
const diff = Date.now() - date.getTime();
|
|
12943
|
+
return diff >= 0 && diff < windowMs;
|
|
12944
|
+
}
|
|
12945
|
+
function isWithinMessageEditWindow(createdAt, timers, isGroup) {
|
|
12946
|
+
return isWithinWindow(createdAt, getEditWindowMs(timers, isGroup));
|
|
12947
|
+
}
|
|
12948
|
+
function isWithinMessageDeleteWindow(createdAt, timers, isGroup) {
|
|
12949
|
+
return isWithinWindow(createdAt, getDeleteWindowMs(timers, isGroup));
|
|
12950
|
+
}
|
|
12951
|
+
function allowsAllMimeTypes(allowedMimeTypes) {
|
|
12952
|
+
return allowedMimeTypes.some((pattern) => {
|
|
12953
|
+
const normalized = pattern.trim().toLowerCase();
|
|
12954
|
+
return normalized === "*" || normalized === "*/*";
|
|
12955
|
+
});
|
|
12956
|
+
}
|
|
12957
|
+
function matchesAllowedMimeType(fileType, allowedMimeTypes) {
|
|
12958
|
+
if (!allowedMimeTypes.length) return false;
|
|
12959
|
+
if (allowsAllMimeTypes(allowedMimeTypes)) return true;
|
|
12960
|
+
const mime = (fileType || "application/octet-stream").toLowerCase();
|
|
12961
|
+
return allowedMimeTypes.some((pattern) => {
|
|
12962
|
+
const normalized = pattern.trim().toLowerCase();
|
|
12963
|
+
if (!normalized) return false;
|
|
12964
|
+
if (normalized.endsWith("/*")) {
|
|
12965
|
+
const prefix = normalized.slice(0, -1);
|
|
12966
|
+
return mime.startsWith(prefix);
|
|
12967
|
+
}
|
|
12968
|
+
return mime === normalized;
|
|
12969
|
+
});
|
|
12970
|
+
}
|
|
12971
|
+
function buildAcceptFromMimeTypes(allowedMimeTypes) {
|
|
12972
|
+
if (allowsAllMimeTypes(allowedMimeTypes)) return "";
|
|
12973
|
+
return allowedMimeTypes.map((type) => type.trim()).filter(Boolean).join(",");
|
|
12974
|
+
}
|
|
12975
|
+
function formatFileSizeMB(sizeBytes) {
|
|
12976
|
+
return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
12977
|
+
}
|
|
12978
|
+
var GROUP_PERMISSION_KEYS = [
|
|
12979
|
+
"onlyAdminCanSendMessage",
|
|
12980
|
+
"onlyAdminCanEditInfo",
|
|
12981
|
+
"senderCanEditMessage",
|
|
12982
|
+
"allowMemberAdd",
|
|
12983
|
+
"allowMemberRemove",
|
|
12984
|
+
"moderationEnabled"
|
|
12985
|
+
];
|
|
12986
|
+
function resolveGroupPermissions(conversationPermissions, defaults, locked = []) {
|
|
12987
|
+
const next = { ...defaults };
|
|
12988
|
+
if (conversationPermissions) {
|
|
12989
|
+
for (const key of GROUP_PERMISSION_KEYS) {
|
|
12990
|
+
const value = conversationPermissions[key];
|
|
12991
|
+
if (typeof value === "boolean") {
|
|
12992
|
+
next[key] = value;
|
|
12993
|
+
}
|
|
12994
|
+
}
|
|
12995
|
+
}
|
|
12996
|
+
for (const key of locked) {
|
|
12997
|
+
if (GROUP_PERMISSION_KEYS.includes(String(key))) {
|
|
12998
|
+
const typedKey = key;
|
|
12999
|
+
next[typedKey] = defaults[typedKey];
|
|
13000
|
+
}
|
|
13001
|
+
}
|
|
13002
|
+
return next;
|
|
13003
|
+
}
|
|
13004
|
+
|
|
13005
|
+
// src/hooks/useGroupInfo.ts
|
|
12925
13006
|
var useGroupInfo = (conversationId) => {
|
|
12926
13007
|
const conversationInfos = exports.useChatStore((s) => s.conversationInfos);
|
|
12927
13008
|
const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
|
|
13009
|
+
const { settings } = useEffectiveSettingsOptional();
|
|
12928
13010
|
const info = conversationId ? conversationInfos[conversationId] : null;
|
|
12929
13011
|
const isAdmin = React3.useMemo(() => {
|
|
12930
13012
|
if (!info || !loggedUserDetails) return false;
|
|
@@ -12940,7 +13022,14 @@ var useGroupInfo = (conversationId) => {
|
|
|
12940
13022
|
}
|
|
12941
13023
|
return String(ownerId) === String(loggedUserDetails._id);
|
|
12942
13024
|
}, [info, loggedUserDetails]);
|
|
12943
|
-
const permissions =
|
|
13025
|
+
const permissions = React3.useMemo(() => {
|
|
13026
|
+
if (!info) return null;
|
|
13027
|
+
return resolveGroupPermissions(
|
|
13028
|
+
info.groupPermissions,
|
|
13029
|
+
settings.groupPolicy.defaults,
|
|
13030
|
+
settings.groupPolicy.locked
|
|
13031
|
+
);
|
|
13032
|
+
}, [info, settings.groupPolicy.defaults, settings.groupPolicy.locked]);
|
|
12944
13033
|
const userPermissions = React3.useMemo(() => {
|
|
12945
13034
|
return {
|
|
12946
13035
|
canSendMessage: isOwner || isAdmin || !permissions?.onlyAdminCanSendMessage,
|
|
@@ -12948,7 +13037,6 @@ var useGroupInfo = (conversationId) => {
|
|
|
12948
13037
|
canAddMember: isOwner || !!permissions?.allowMemberAdd,
|
|
12949
13038
|
canRemoveMember: isOwner || !!permissions?.allowMemberRemove,
|
|
12950
13039
|
canEditMessage: isOwner || isAdmin || !!permissions?.senderCanEditMessage
|
|
12951
|
-
// You can add more derived permissions here as needed
|
|
12952
13040
|
};
|
|
12953
13041
|
}, [isOwner, isAdmin, permissions]);
|
|
12954
13042
|
return {
|
|
@@ -13375,14 +13463,20 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13375
13463
|
const [permissions, setPermissions] = React3.useState(null);
|
|
13376
13464
|
const [isSaving, setIsSaving] = React3.useState(false);
|
|
13377
13465
|
React3.useEffect(() => {
|
|
13378
|
-
if (
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13466
|
+
if (!isOpen) return;
|
|
13467
|
+
setPermissions(
|
|
13468
|
+
resolveGroupPermissions(
|
|
13469
|
+
conversationInfo?.groupPermissions,
|
|
13470
|
+
settings.groupPolicy.defaults,
|
|
13471
|
+
settings.groupPolicy.locked
|
|
13472
|
+
)
|
|
13473
|
+
);
|
|
13474
|
+
}, [
|
|
13475
|
+
isOpen,
|
|
13476
|
+
conversationInfo?.groupPermissions,
|
|
13477
|
+
settings.groupPolicy.defaults,
|
|
13478
|
+
settings.groupPolicy.locked
|
|
13479
|
+
]);
|
|
13386
13480
|
if (!conversationId || !permissions) return null;
|
|
13387
13481
|
const handleToggle = (key) => {
|
|
13388
13482
|
if (isGroupPermissionLocked(key)) return;
|
|
@@ -13392,7 +13486,12 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13392
13486
|
if (!conversationId || !permissions) return;
|
|
13393
13487
|
setIsSaving(true);
|
|
13394
13488
|
try {
|
|
13395
|
-
|
|
13489
|
+
const payload = resolveGroupPermissions(
|
|
13490
|
+
permissions,
|
|
13491
|
+
settings.groupPolicy.defaults,
|
|
13492
|
+
settings.groupPolicy.locked
|
|
13493
|
+
);
|
|
13494
|
+
await updateGroupPermissions(conversationId, payload);
|
|
13396
13495
|
onOpenChange(false);
|
|
13397
13496
|
} finally {
|
|
13398
13497
|
setIsSaving(false);
|
|
@@ -13419,6 +13518,7 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13419
13518
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1 px-0.5 text-[10px] font-semibold uppercase tracking-wide text-(--chat-muted)", children: "Permissions" }),
|
|
13420
13519
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-(--chat-border) px-3", children: permissionItems.map((item, index) => {
|
|
13421
13520
|
const locked = isGroupPermissionLocked(item.key);
|
|
13521
|
+
const checked = item.inverted ? !permissions[item.key] : permissions[item.key];
|
|
13422
13522
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13423
13523
|
"div",
|
|
13424
13524
|
{
|
|
@@ -13443,7 +13543,7 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13443
13543
|
size: "sm",
|
|
13444
13544
|
className: "mt-0.5 shrink-0 data-[state=checked]:bg-(--chat-theme) data-[state=unchecked]:bg-(--chat-border)",
|
|
13445
13545
|
disabled: locked,
|
|
13446
|
-
checked
|
|
13546
|
+
checked,
|
|
13447
13547
|
onCheckedChange: () => handleToggle(item.key)
|
|
13448
13548
|
}
|
|
13449
13549
|
)
|
|
@@ -14987,61 +15087,6 @@ function resolveForwardItems(selectedKeys, messages) {
|
|
|
14987
15087
|
|
|
14988
15088
|
// src/chat/lib/chat-edit-window.ts
|
|
14989
15089
|
init_settings_types();
|
|
14990
|
-
|
|
14991
|
-
// src/lib/effective-settings.helpers.ts
|
|
14992
|
-
function minutesToMs(minutes) {
|
|
14993
|
-
return Math.max(0, minutes) * 60 * 1e3;
|
|
14994
|
-
}
|
|
14995
|
-
function getEditWindowMs(timers, isGroup) {
|
|
14996
|
-
return minutesToMs(isGroup ? timers.groupEditMinutes : timers.dmEditMinutes);
|
|
14997
|
-
}
|
|
14998
|
-
function getDeleteWindowMs(timers, isGroup) {
|
|
14999
|
-
return minutesToMs(
|
|
15000
|
-
isGroup ? timers.groupDeleteMinutes : timers.dmDeleteMinutes
|
|
15001
|
-
);
|
|
15002
|
-
}
|
|
15003
|
-
function isWithinWindow(createdAt, windowMs) {
|
|
15004
|
-
if (!createdAt) return false;
|
|
15005
|
-
const date = new Date(createdAt);
|
|
15006
|
-
if (isNaN(date.getTime())) return false;
|
|
15007
|
-
const diff = Date.now() - date.getTime();
|
|
15008
|
-
return diff >= 0 && diff < windowMs;
|
|
15009
|
-
}
|
|
15010
|
-
function isWithinMessageEditWindow(createdAt, timers, isGroup) {
|
|
15011
|
-
return isWithinWindow(createdAt, getEditWindowMs(timers, isGroup));
|
|
15012
|
-
}
|
|
15013
|
-
function isWithinMessageDeleteWindow(createdAt, timers, isGroup) {
|
|
15014
|
-
return isWithinWindow(createdAt, getDeleteWindowMs(timers, isGroup));
|
|
15015
|
-
}
|
|
15016
|
-
function allowsAllMimeTypes(allowedMimeTypes) {
|
|
15017
|
-
return allowedMimeTypes.some((pattern) => {
|
|
15018
|
-
const normalized = pattern.trim().toLowerCase();
|
|
15019
|
-
return normalized === "*" || normalized === "*/*";
|
|
15020
|
-
});
|
|
15021
|
-
}
|
|
15022
|
-
function matchesAllowedMimeType(fileType, allowedMimeTypes) {
|
|
15023
|
-
if (!allowedMimeTypes.length) return false;
|
|
15024
|
-
if (allowsAllMimeTypes(allowedMimeTypes)) return true;
|
|
15025
|
-
const mime = (fileType || "application/octet-stream").toLowerCase();
|
|
15026
|
-
return allowedMimeTypes.some((pattern) => {
|
|
15027
|
-
const normalized = pattern.trim().toLowerCase();
|
|
15028
|
-
if (!normalized) return false;
|
|
15029
|
-
if (normalized.endsWith("/*")) {
|
|
15030
|
-
const prefix = normalized.slice(0, -1);
|
|
15031
|
-
return mime.startsWith(prefix);
|
|
15032
|
-
}
|
|
15033
|
-
return mime === normalized;
|
|
15034
|
-
});
|
|
15035
|
-
}
|
|
15036
|
-
function buildAcceptFromMimeTypes(allowedMimeTypes) {
|
|
15037
|
-
if (allowsAllMimeTypes(allowedMimeTypes)) return "";
|
|
15038
|
-
return allowedMimeTypes.map((type) => type.trim()).filter(Boolean).join(",");
|
|
15039
|
-
}
|
|
15040
|
-
function formatFileSizeMB(sizeBytes) {
|
|
15041
|
-
return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
15042
|
-
}
|
|
15043
|
-
|
|
15044
|
-
// src/chat/lib/chat-edit-window.ts
|
|
15045
15090
|
var FALLBACK_TIMERS = exports.DEFAULT_EFFECTIVE_SETTINGS.messageTimers;
|
|
15046
15091
|
function isWithinMessageEditWindow2(createdAt, isGroup, timers = FALLBACK_TIMERS) {
|
|
15047
15092
|
return isWithinMessageEditWindow(createdAt, timers, isGroup);
|