@realtimexsco/live-chat 1.4.12 → 1.4.13

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 CHANGED
@@ -67,4 +67,12 @@ DMs · groups · Socket.IO · reactions · replies · forward · edit/delete ·
67
67
 
68
68
  ## License
69
69
 
70
- [MIT](https://www.npmjs.com/package/@realtimexsco/live-chat#license) © RealtimeX
70
+ The MIT License (MIT)
71
+
72
+ Copyright (c) 2026 RealtimeX
73
+
74
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
75
+
76
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
77
+
78
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/dist/index.cjs CHANGED
@@ -1082,16 +1082,27 @@ var init_connection_actions = __esm({
1082
1082
  setActiveConversationId: (conversationId) => {
1083
1083
  set((state) => {
1084
1084
  const nextId = conversationId ?? null;
1085
- if (String(state.activeConversationId ?? "") === String(nextId ?? "")) {
1085
+ const prevId = state.activeConversationId ?? null;
1086
+ if (String(prevId ?? "") === String(nextId ?? "")) {
1086
1087
  return state;
1087
1088
  }
1089
+ const nextErrors = { ...state.conversationErrors };
1090
+ if (prevId) {
1091
+ delete nextErrors[String(prevId)];
1092
+ }
1088
1093
  if (!nextId) {
1089
- return { activeConversationId: null };
1094
+ return {
1095
+ activeConversationId: null,
1096
+ globalAppError: null,
1097
+ conversationErrors: nextErrors
1098
+ };
1090
1099
  }
1091
1100
  const target = state.conversations.find((c) => String(c._id) === String(nextId));
1092
1101
  const unreadAlreadyZero = !normalizeUnreadCount(target?.unreadCount, state.loggedUserDetails?._id);
1093
1102
  return {
1094
1103
  activeConversationId: nextId,
1104
+ globalAppError: null,
1105
+ conversationErrors: nextErrors,
1095
1106
  conversations: unreadAlreadyZero ? state.conversations : state.conversations.map(
1096
1107
  (c) => String(c._id) === String(nextId) ? { ...c, unreadCount: 0 } : c
1097
1108
  )
@@ -1132,25 +1143,32 @@ var init_connection_actions = __esm({
1132
1143
  type = "rate_limit";
1133
1144
  return;
1134
1145
  }
1146
+ if (conversationId) {
1147
+ set((state) => ({
1148
+ conversationErrors: {
1149
+ ...state.conversationErrors,
1150
+ [conversationId]: { message, type }
1151
+ },
1152
+ globalAppError: null
1153
+ }));
1154
+ return;
1155
+ }
1135
1156
  set({ globalAppError: { message, type } });
1136
1157
  },
1137
1158
  clearAppError: (conversationId) => {
1138
1159
  set((state) => {
1139
- const targetId = conversationId ?? state.activeConversationId;
1140
- if (targetId) {
1160
+ if (conversationId) {
1141
1161
  const nextErrors = { ...state.conversationErrors };
1142
1162
  const nextComposer = { ...state.composerDisabledByConversation };
1143
- delete nextErrors[targetId];
1144
- delete nextComposer[targetId];
1163
+ delete nextErrors[conversationId];
1164
+ delete nextComposer[conversationId];
1145
1165
  return {
1146
1166
  conversationErrors: nextErrors,
1147
1167
  composerDisabledByConversation: nextComposer
1148
1168
  };
1149
1169
  }
1150
1170
  return {
1151
- globalAppError: null,
1152
- conversationErrors: {},
1153
- composerDisabledByConversation: {}
1171
+ globalAppError: null
1154
1172
  };
1155
1173
  });
1156
1174
  },
@@ -1698,7 +1716,7 @@ var init_uploads_api = __esm({
1698
1716
  });
1699
1717
 
1700
1718
  // src/types/settings.types.ts
1701
- exports.DEFAULT_EFFECTIVE_SETTINGS = void 0; exports.ADMIN_PERMISSION_DENIED_MESSAGE = void 0; exports.CALLS_NOT_CONFIGURED_MESSAGE = void 0;
1719
+ exports.DEFAULT_EFFECTIVE_SETTINGS = void 0; exports.ADMIN_PERMISSION_DENIED_MESSAGE = void 0; exports.CALLS_NOT_ALLOWED_BY_PLATFORM_MESSAGE = void 0; exports.CALLS_NOT_CONFIGURED_MESSAGE = void 0;
1702
1720
  var init_settings_types = __esm({
1703
1721
  "src/types/settings.types.ts"() {
1704
1722
  exports.DEFAULT_EFFECTIVE_SETTINGS = {
@@ -1707,6 +1725,7 @@ var init_settings_types = __esm({
1707
1725
  audioCallEnabled: true,
1708
1726
  blockUsersEnabled: true,
1709
1727
  calls: {
1728
+ allowedByPlatform: true,
1710
1729
  enabled: true,
1711
1730
  configured: true
1712
1731
  },
@@ -1739,6 +1758,7 @@ var init_settings_types = __esm({
1739
1758
  groupCreationEnabled: true
1740
1759
  };
1741
1760
  exports.ADMIN_PERMISSION_DENIED_MESSAGE = "This feature is disabled by your administrator.";
1761
+ exports.CALLS_NOT_ALLOWED_BY_PLATFORM_MESSAGE = "Calls are not allowed on this platform. Please contact your administrator.";
1742
1762
  exports.CALLS_NOT_CONFIGURED_MESSAGE = "Calls are not configured. Please contact your administrator.";
1743
1763
  }
1744
1764
  });
@@ -1786,6 +1806,10 @@ function normalizeCalls(value, fallback) {
1786
1806
  if (!value || typeof value !== "object") return { ...fallback };
1787
1807
  const raw = value;
1788
1808
  return {
1809
+ allowedByPlatform: asBoolean(
1810
+ raw.allowedByPlatform,
1811
+ fallback.allowedByPlatform
1812
+ ),
1789
1813
  enabled: asBoolean(raw.enabled, fallback.enabled),
1790
1814
  configured: asBoolean(raw.configured, fallback.configured)
1791
1815
  };
@@ -4743,6 +4767,13 @@ init_settings_api();
4743
4767
  init_settings_types();
4744
4768
  var ChatEffectiveSettingsContext = React3.createContext(null);
4745
4769
  function buildCallGate(settings, featureEnabled) {
4770
+ if (!settings.calls.allowedByPlatform) {
4771
+ return {
4772
+ visible: true,
4773
+ enabled: false,
4774
+ disabledReason: exports.CALLS_NOT_ALLOWED_BY_PLATFORM_MESSAGE
4775
+ };
4776
+ }
4746
4777
  if (!settings.calls.configured) {
4747
4778
  return {
4748
4779
  visible: true,
@@ -7358,7 +7389,8 @@ var PushNotificationToggle = ({
7358
7389
  checked: adminBlocked || notConfigured ? false : isOn,
7359
7390
  disabled: isDisabled,
7360
7391
  onCheckedChange: (checked) => void handleChange(checked),
7361
- "aria-label": `Toggle ${label}`
7392
+ "aria-label": `Toggle ${label}`,
7393
+ className: "data-[state=checked]:bg-(--chat-theme) data-[state=unchecked]:bg-(--chat-border)"
7362
7394
  }
7363
7395
  )
7364
7396
  }
@@ -8961,7 +8993,7 @@ var HeaderSearchPopover = ({
8961
8993
  ] });
8962
8994
  };
8963
8995
  var HeaderSearchPopover_default = HeaderSearchPopover;
8964
- var actionBtnClass = "size-8 rounded-lg text-(--chat-muted) transition-colors hover:bg-(--chat-surface) hover:text-(--chat-text) disabled:pointer-events-none disabled:opacity-40";
8996
+ var actionBtnClass = "size-8 rounded-lg text-(--chat-muted) transition-colors hover:bg-(--chat-surface) hover:text-(--chat-text) disabled:opacity-40";
8965
8997
  var DefaultHeaderCallActions = ({
8966
8998
  activeChannel,
8967
8999
  conversationId,
@@ -14981,7 +15013,15 @@ function isWithinMessageEditWindow(createdAt, timers, isGroup) {
14981
15013
  function isWithinMessageDeleteWindow(createdAt, timers, isGroup) {
14982
15014
  return isWithinWindow(createdAt, getDeleteWindowMs(timers, isGroup));
14983
15015
  }
15016
+ function allowsAllMimeTypes(allowedMimeTypes) {
15017
+ return allowedMimeTypes.some((pattern) => {
15018
+ const normalized = pattern.trim().toLowerCase();
15019
+ return normalized === "*" || normalized === "*/*";
15020
+ });
15021
+ }
14984
15022
  function matchesAllowedMimeType(fileType, allowedMimeTypes) {
15023
+ if (!allowedMimeTypes.length) return false;
15024
+ if (allowsAllMimeTypes(allowedMimeTypes)) return true;
14985
15025
  const mime = (fileType || "application/octet-stream").toLowerCase();
14986
15026
  return allowedMimeTypes.some((pattern) => {
14987
15027
  const normalized = pattern.trim().toLowerCase();
@@ -14994,6 +15034,7 @@ function matchesAllowedMimeType(fileType, allowedMimeTypes) {
14994
15034
  });
14995
15035
  }
14996
15036
  function buildAcceptFromMimeTypes(allowedMimeTypes) {
15037
+ if (allowsAllMimeTypes(allowedMimeTypes)) return "";
14997
15038
  return allowedMimeTypes.map((type) => type.trim()).filter(Boolean).join(",");
14998
15039
  }
14999
15040
  function formatFileSizeMB(sizeBytes) {
@@ -18608,6 +18649,7 @@ var ChatPanelAlerts = () => {
18608
18649
  const globalAppError = exports.useChatStore((s) => s.globalAppError);
18609
18650
  const conversationErrors = exports.useChatStore((s) => s.conversationErrors);
18610
18651
  const clearAppError = exports.useChatStore((s) => s.clearAppError);
18652
+ const setRateLimitResetAt = exports.useChatStore((s) => s.setRateLimitResetAt);
18611
18653
  const rateLimitResetAt2 = exports.useChatStore((s) => s.rateLimitResetAt);
18612
18654
  const [now, setNow] = React3.useState(() => Date.now());
18613
18655
  const conversationAppError = activeConversationId ? conversationErrors[activeConversationId] ?? null : null;
@@ -18644,7 +18686,7 @@ var ChatPanelAlerts = () => {
18644
18686
  onDismiss: () => clearAppError(activeConversationId)
18645
18687
  });
18646
18688
  }
18647
- if (globalAppError?.message) {
18689
+ if (!activeConversationId && globalAppError?.message) {
18648
18690
  push({
18649
18691
  id: `global-app-error-${globalAppError.type}`,
18650
18692
  type: "error",
@@ -18659,7 +18701,8 @@ var ChatPanelAlerts = () => {
18659
18701
  id: "rate-limit",
18660
18702
  type: "warning",
18661
18703
  message: `Too many requests. Please wait ${seconds}s before trying again.`,
18662
- icon: lucideReact.AlertCircle
18704
+ icon: lucideReact.AlertCircle,
18705
+ onDismiss: () => setRateLimitResetAt(0)
18663
18706
  });
18664
18707
  }
18665
18708
  if (info?.trim()) {
@@ -18682,7 +18725,8 @@ var ChatPanelAlerts = () => {
18682
18725
  onDismissError,
18683
18726
  onDismissInfo,
18684
18727
  info,
18685
- clearAppError
18728
+ clearAppError,
18729
+ setRateLimitResetAt
18686
18730
  ]);
18687
18731
  if (alerts.length === 0) return null;
18688
18732
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 space-y-2 border-b border-(--chat-border) bg-(--chat-surface) px-4 py-2.5", children: alerts.map((alert) => {
@@ -18698,16 +18742,20 @@ var ChatPanelAlerts = () => {
18698
18742
  children: [
18699
18743
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-3.5 shrink-0", "aria-hidden": true }),
18700
18744
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 leading-relaxed", children: alert.message }),
18701
- alert.onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
18745
+ alert.onDismiss ? /* @__PURE__ */ jsxRuntime.jsx(
18702
18746
  "button",
18703
18747
  {
18704
18748
  type: "button",
18705
- onClick: alert.onDismiss,
18749
+ onClick: (event) => {
18750
+ event.preventDefault();
18751
+ event.stopPropagation();
18752
+ alert.onDismiss?.();
18753
+ },
18706
18754
  className: "shrink-0 rounded-md p-0.5 opacity-70 transition-opacity hover:opacity-100",
18707
18755
  "aria-label": "Dismiss",
18708
18756
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3.5" })
18709
18757
  }
18710
- )
18758
+ ) : null
18711
18759
  ]
18712
18760
  },
18713
18761
  alert.id