@realtimexsco/live-chat 1.4.10 → 1.4.12

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 CHANGED
@@ -6,7 +6,7 @@ var socket_ioClient = require('socket.io-client');
6
6
  var axios = require('axios');
7
7
  var zustand = require('zustand');
8
8
  var middleware = require('zustand/middleware');
9
- var React2 = require('react');
9
+ var React3 = require('react');
10
10
  var radixUi = require('radix-ui');
11
11
  var clsx = require('clsx');
12
12
  var tailwindMerge = require('tailwind-merge');
@@ -39,7 +39,7 @@ function _interopNamespace(e) {
39
39
  }
40
40
 
41
41
  var axios__default = /*#__PURE__*/_interopDefault(axios);
42
- var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
42
+ var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
43
43
  var EmojiPicker__default = /*#__PURE__*/_interopDefault(EmojiPicker);
44
44
  var toast__default = /*#__PURE__*/_interopDefault(toast);
45
45
 
@@ -1190,6 +1190,7 @@ function resolveChatApiKey(url) {
1190
1190
  }
1191
1191
  if (path.includes("/message/search-messages")) return "searchMessages";
1192
1192
  if (path.includes("/message/searched-message/context")) return "searchMessagesContext";
1193
+ if (path.includes("/settings/effective")) return "effectiveSettings";
1193
1194
  if (path.includes("/conversation/group/create")) return "createGroup";
1194
1195
  if (path.includes("/conversation/group/permission/update")) return "updateGroupPermissions";
1195
1196
  if (path.includes("/conversation/group/information/update")) return "updateGroupInfo";
@@ -1233,7 +1234,8 @@ var init_query_params = __esm({
1233
1234
  "pinnedMessages",
1234
1235
  "starredMessages",
1235
1236
  "searchMessages",
1236
- "searchMessagesContext"
1237
+ "searchMessagesContext",
1238
+ "effectiveSettings"
1237
1239
  ];
1238
1240
  exports.CHAT_API_KEYS = [
1239
1241
  "all",
@@ -1251,6 +1253,64 @@ var init_query_params = __esm({
1251
1253
  ];
1252
1254
  }
1253
1255
  });
1256
+
1257
+ // src/services/api/api-version.ts
1258
+ var DEFAULT_VERSIONS, apiVersions, normalizeApiVersion; exports.resolveApiRoot = void 0; exports.buildVersionedApiUrl = void 0; exports.setChatApiVersions = void 0; exports.getChatApiVersions = void 0; exports.setChatPushApiVersion = void 0; var syncDefaultApiVersionFromBaseUrl;
1259
+ var init_api_version = __esm({
1260
+ "src/services/api/api-version.ts"() {
1261
+ init_client();
1262
+ DEFAULT_VERSIONS = {
1263
+ v1: "v1",
1264
+ v2: "v2"
1265
+ };
1266
+ apiVersions = { ...DEFAULT_VERSIONS };
1267
+ normalizeApiVersion = (version) => {
1268
+ const raw = String(version || "v1").trim().replace(/^\/+|\/+$/g, "");
1269
+ if (!raw) return "v1";
1270
+ return /^v\d+$/i.test(raw) ? raw.toLowerCase() : `v${raw}`;
1271
+ };
1272
+ exports.resolveApiRoot = (baseUrl) => {
1273
+ const raw = String(
1274
+ baseUrl || exports.getChatBaseUrl() || apiClient.defaults.baseURL || ""
1275
+ ).trim().replace(/\/$/, "");
1276
+ if (!raw) return "";
1277
+ const withoutVersion = raw.replace(/\/v\d+$/i, "");
1278
+ return withoutVersion || raw;
1279
+ };
1280
+ exports.buildVersionedApiUrl = (path, options) => {
1281
+ const root = exports.resolveApiRoot(options?.baseUrl);
1282
+ const version = normalizeApiVersion(
1283
+ options?.version ?? (options?.service ? apiVersions[options.service] : apiVersions.v1)
1284
+ );
1285
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
1286
+ if (!root) {
1287
+ return `/${version}${normalizedPath}`;
1288
+ }
1289
+ return `${root}/${version}${normalizedPath}`;
1290
+ };
1291
+ exports.setChatApiVersions = (config) => {
1292
+ if (!config) return;
1293
+ if (config.v1) {
1294
+ apiVersions.v1 = normalizeApiVersion(config.v1);
1295
+ }
1296
+ if (config.v2) {
1297
+ apiVersions.v2 = normalizeApiVersion(config.v2);
1298
+ }
1299
+ };
1300
+ exports.getChatApiVersions = () => ({
1301
+ ...apiVersions
1302
+ });
1303
+ exports.setChatPushApiVersion = (version) => {
1304
+ apiVersions.v2 = normalizeApiVersion(version);
1305
+ };
1306
+ syncDefaultApiVersionFromBaseUrl = (baseUrl) => {
1307
+ const match = String(baseUrl || "").trim().match(/\/(v\d+)\/?$/i);
1308
+ if (match?.[1]) {
1309
+ apiVersions.v1 = normalizeApiVersion(match[1]);
1310
+ }
1311
+ };
1312
+ }
1313
+ });
1254
1314
  function resolveParamsForRequest(url, method) {
1255
1315
  const apiKey = resolveChatApiKey(url);
1256
1316
  const httpMethod = (method || "get").toLowerCase();
@@ -1267,7 +1327,9 @@ var chatClientId, chatAccessToken, chatBaseUrl, chatSocketUrl, chatQueryParams,
1267
1327
  var init_client = __esm({
1268
1328
  "src/services/api/client.ts"() {
1269
1329
  init_query_params();
1330
+ init_api_version();
1270
1331
  init_query_params();
1332
+ init_api_version();
1271
1333
  chatClientId = "";
1272
1334
  chatAccessToken = "";
1273
1335
  chatBaseUrl = "";
@@ -1285,6 +1347,7 @@ var init_client = __esm({
1285
1347
  exports.setChatBaseURL = (url) => {
1286
1348
  chatBaseUrl = url.trim();
1287
1349
  apiClient.defaults.baseURL = chatBaseUrl;
1350
+ syncDefaultApiVersionFromBaseUrl(chatBaseUrl);
1288
1351
  };
1289
1352
  exports.getChatBaseUrl = () => chatBaseUrl;
1290
1353
  exports.setChatSocketUrl = (url) => {
@@ -1388,6 +1451,9 @@ var init_api_constants = __esm({
1388
1451
  SEARCH_MESSAGES: "/message/search-messages",
1389
1452
  SEARCH_MESSAGES_CONTEXT: (messageId2) => `/message/searched-message/context?messageId=${messageId2}`,
1390
1453
  BLOCK_UNBLOCK_USER: "/user/block-unblock"
1454
+ },
1455
+ SETTINGS: {
1456
+ PERMISSIONS: "/settings/effective"
1391
1457
  }
1392
1458
  };
1393
1459
  }
@@ -1424,13 +1490,11 @@ var init_auth_api = __esm({
1424
1490
  });
1425
1491
 
1426
1492
  // src/constants/chat.constants.ts
1427
- var DEFAULT_MESSAGES_LIMIT, DEFAULT_CONVERSATIONS_LIMIT, DM_EDIT_WINDOW_MS, GROUP_EDIT_WINDOW_MS, MAX_ATTACHMENT_CAPTION_LENGTH, ATTACHMENT_FILENAME_TIMESTAMP_LENGTH;
1493
+ var DEFAULT_MESSAGES_LIMIT, DEFAULT_CONVERSATIONS_LIMIT, MAX_ATTACHMENT_CAPTION_LENGTH, ATTACHMENT_FILENAME_TIMESTAMP_LENGTH;
1428
1494
  var init_chat_constants = __esm({
1429
1495
  "src/constants/chat.constants.ts"() {
1430
1496
  DEFAULT_MESSAGES_LIMIT = 20;
1431
1497
  DEFAULT_CONVERSATIONS_LIMIT = 20;
1432
- DM_EDIT_WINDOW_MS = 5 * 60 * 1e3;
1433
- GROUP_EDIT_WINDOW_MS = 10 * 60 * 1e3;
1434
1498
  MAX_ATTACHMENT_CAPTION_LENGTH = 100;
1435
1499
  ATTACHMENT_FILENAME_TIMESTAMP_LENGTH = 13;
1436
1500
  }
@@ -1633,6 +1697,206 @@ var init_uploads_api = __esm({
1633
1697
  }
1634
1698
  });
1635
1699
 
1700
+ // 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;
1702
+ var init_settings_types = __esm({
1703
+ "src/types/settings.types.ts"() {
1704
+ exports.DEFAULT_EFFECTIVE_SETTINGS = {
1705
+ pushNotificationEnabled: true,
1706
+ videoCallEnabled: true,
1707
+ audioCallEnabled: true,
1708
+ blockUsersEnabled: true,
1709
+ calls: {
1710
+ enabled: true,
1711
+ configured: true
1712
+ },
1713
+ groupPolicy: {
1714
+ defaults: {
1715
+ onlyAdminCanSendMessage: false,
1716
+ onlyAdminCanEditInfo: false,
1717
+ senderCanEditMessage: true,
1718
+ allowMemberAdd: true,
1719
+ allowMemberRemove: false,
1720
+ moderationEnabled: false
1721
+ },
1722
+ locked: []
1723
+ },
1724
+ messageTimers: {
1725
+ dmEditMinutes: 5,
1726
+ dmDeleteMinutes: 5,
1727
+ groupEditMinutes: 10,
1728
+ groupDeleteMinutes: 10
1729
+ },
1730
+ attachments: {
1731
+ enabled: true,
1732
+ maxFileSizeMB: 10,
1733
+ allowedMimeTypes: ["image/*", "video/*", "audio/*", "application/pdf"],
1734
+ maxAttachmentsPerMessage: 10
1735
+ },
1736
+ maxAdminsPerGroup: 5,
1737
+ maxParticipantsPerGroup: 100,
1738
+ maxPinnedMessagesPerConversation: 10,
1739
+ groupCreationEnabled: true
1740
+ };
1741
+ exports.ADMIN_PERMISSION_DENIED_MESSAGE = "This feature is disabled by your administrator.";
1742
+ exports.CALLS_NOT_CONFIGURED_MESSAGE = "Calls are not configured. Please contact your administrator.";
1743
+ }
1744
+ });
1745
+
1746
+ // src/services/api/settings.api.ts
1747
+ function asBoolean(value, fallback) {
1748
+ return typeof value === "boolean" ? value : fallback;
1749
+ }
1750
+ function asPositiveNumber(value, fallback) {
1751
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
1752
+ }
1753
+ function asStringArray(value, fallback) {
1754
+ if (!Array.isArray(value)) return fallback;
1755
+ const next = value.filter((item) => typeof item === "string");
1756
+ return next.length > 0 ? next : fallback;
1757
+ }
1758
+ function normalizeGroupPermissions(value, fallback) {
1759
+ if (!value || typeof value !== "object") return { ...fallback };
1760
+ const raw = value;
1761
+ return {
1762
+ onlyAdminCanSendMessage: asBoolean(
1763
+ raw.onlyAdminCanSendMessage,
1764
+ fallback.onlyAdminCanSendMessage
1765
+ ),
1766
+ onlyAdminCanEditInfo: asBoolean(
1767
+ raw.onlyAdminCanEditInfo,
1768
+ fallback.onlyAdminCanEditInfo
1769
+ ),
1770
+ senderCanEditMessage: asBoolean(
1771
+ raw.senderCanEditMessage,
1772
+ fallback.senderCanEditMessage
1773
+ ),
1774
+ allowMemberAdd: asBoolean(raw.allowMemberAdd, fallback.allowMemberAdd),
1775
+ allowMemberRemove: asBoolean(
1776
+ raw.allowMemberRemove,
1777
+ fallback.allowMemberRemove
1778
+ ),
1779
+ moderationEnabled: asBoolean(
1780
+ raw.moderationEnabled,
1781
+ fallback.moderationEnabled
1782
+ )
1783
+ };
1784
+ }
1785
+ function normalizeCalls(value, fallback) {
1786
+ if (!value || typeof value !== "object") return { ...fallback };
1787
+ const raw = value;
1788
+ return {
1789
+ enabled: asBoolean(raw.enabled, fallback.enabled),
1790
+ configured: asBoolean(raw.configured, fallback.configured)
1791
+ };
1792
+ }
1793
+ function normalizeGroupPolicy(value, fallback) {
1794
+ if (!value || typeof value !== "object") {
1795
+ return {
1796
+ defaults: { ...fallback.defaults },
1797
+ locked: [...fallback.locked]
1798
+ };
1799
+ }
1800
+ const raw = value;
1801
+ return {
1802
+ defaults: normalizeGroupPermissions(raw.defaults, fallback.defaults),
1803
+ locked: asStringArray(raw.locked, fallback.locked)
1804
+ };
1805
+ }
1806
+ function normalizeMessageTimers(value, fallback) {
1807
+ if (!value || typeof value !== "object") return { ...fallback };
1808
+ const raw = value;
1809
+ return {
1810
+ dmEditMinutes: asPositiveNumber(raw.dmEditMinutes, fallback.dmEditMinutes),
1811
+ dmDeleteMinutes: asPositiveNumber(
1812
+ raw.dmDeleteMinutes,
1813
+ fallback.dmDeleteMinutes
1814
+ ),
1815
+ groupEditMinutes: asPositiveNumber(
1816
+ raw.groupEditMinutes,
1817
+ fallback.groupEditMinutes
1818
+ ),
1819
+ groupDeleteMinutes: asPositiveNumber(
1820
+ raw.groupDeleteMinutes,
1821
+ fallback.groupDeleteMinutes
1822
+ )
1823
+ };
1824
+ }
1825
+ function normalizeAttachments(value, fallback) {
1826
+ if (!value || typeof value !== "object") return { ...fallback };
1827
+ const raw = value;
1828
+ return {
1829
+ enabled: asBoolean(raw.enabled, fallback.enabled),
1830
+ maxFileSizeMB: asPositiveNumber(raw.maxFileSizeMB, fallback.maxFileSizeMB),
1831
+ allowedMimeTypes: asStringArray(
1832
+ raw.allowedMimeTypes,
1833
+ fallback.allowedMimeTypes
1834
+ ),
1835
+ maxAttachmentsPerMessage: asPositiveNumber(
1836
+ raw.maxAttachmentsPerMessage,
1837
+ fallback.maxAttachmentsPerMessage
1838
+ )
1839
+ };
1840
+ }
1841
+ function normalizeEffectiveSettings(raw) {
1842
+ const payload = raw && typeof raw === "object" ? raw.data ?? raw : null;
1843
+ const source = payload && typeof payload === "object" ? payload : {};
1844
+ const defaults = exports.DEFAULT_EFFECTIVE_SETTINGS;
1845
+ return {
1846
+ pushNotificationEnabled: asBoolean(
1847
+ source.pushNotificationEnabled,
1848
+ defaults.pushNotificationEnabled
1849
+ ),
1850
+ videoCallEnabled: asBoolean(
1851
+ source.videoCallEnabled,
1852
+ defaults.videoCallEnabled
1853
+ ),
1854
+ audioCallEnabled: asBoolean(
1855
+ source.audioCallEnabled,
1856
+ defaults.audioCallEnabled
1857
+ ),
1858
+ blockUsersEnabled: asBoolean(
1859
+ source.blockUsersEnabled,
1860
+ defaults.blockUsersEnabled
1861
+ ),
1862
+ calls: normalizeCalls(source.calls, defaults.calls),
1863
+ groupPolicy: normalizeGroupPolicy(source.groupPolicy, defaults.groupPolicy),
1864
+ messageTimers: normalizeMessageTimers(
1865
+ source.messageTimers,
1866
+ defaults.messageTimers
1867
+ ),
1868
+ attachments: normalizeAttachments(source.attachments, defaults.attachments),
1869
+ maxAdminsPerGroup: asPositiveNumber(
1870
+ source.maxAdminsPerGroup,
1871
+ defaults.maxAdminsPerGroup
1872
+ ),
1873
+ maxParticipantsPerGroup: asPositiveNumber(
1874
+ source.maxParticipantsPerGroup,
1875
+ defaults.maxParticipantsPerGroup
1876
+ ),
1877
+ maxPinnedMessagesPerConversation: asPositiveNumber(
1878
+ source.maxPinnedMessagesPerConversation,
1879
+ defaults.maxPinnedMessagesPerConversation
1880
+ ),
1881
+ groupCreationEnabled: asBoolean(
1882
+ source.groupCreationEnabled,
1883
+ defaults.groupCreationEnabled
1884
+ )
1885
+ };
1886
+ }
1887
+ exports.fetchEffectiveSettings = void 0;
1888
+ var init_settings_api = __esm({
1889
+ "src/services/api/settings.api.ts"() {
1890
+ init_api_constants();
1891
+ init_client();
1892
+ init_settings_types();
1893
+ exports.fetchEffectiveSettings = async () => {
1894
+ const response = await apiClient.get(API_ENDPOINTS.SETTINGS.PERMISSIONS);
1895
+ return normalizeEffectiveSettings(response.data);
1896
+ };
1897
+ }
1898
+ });
1899
+
1636
1900
  // src/services/api/index.ts
1637
1901
  var init_api = __esm({
1638
1902
  "src/services/api/index.ts"() {
@@ -1641,6 +1905,7 @@ var init_api = __esm({
1641
1905
  init_messages_api();
1642
1906
  init_groups_api();
1643
1907
  init_uploads_api();
1908
+ init_settings_api();
1644
1909
  }
1645
1910
  });
1646
1911
 
@@ -3750,7 +4015,7 @@ function resolveApiUrl(apiUrl) {
3750
4015
  function resolveSocketUrl(socketUrl, apiUrl) {
3751
4016
  const explicit = (socketUrl ?? "").trim();
3752
4017
  if (explicit) return explicit;
3753
- return resolveApiUrl(apiUrl).replace(/\/api\/v1\/?$/, "");
4018
+ return resolveApiUrl(apiUrl).replace(/\/api\/v\d+\/?$/i, "");
3754
4019
  }
3755
4020
 
3756
4021
  // src/hooks/chat-sync/useChatSync.ts
@@ -4324,7 +4589,7 @@ var syncLocalMessagesOnDmMarkRead = (prev, lastDMMarkRead, conversations, logged
4324
4589
 
4325
4590
  // src/hooks/chat-sync/useChatSync.ts
4326
4591
  var useChatSync = () => {
4327
- const [localMessages, setLocalMessages] = React2.useState({});
4592
+ const [localMessages, setLocalMessages] = React3.useState({});
4328
4593
  const lastDM = useSocketStore((s) => s.lastDM);
4329
4594
  const lastEditDM = useSocketStore((s) => s.lastEditDM);
4330
4595
  const lastDeleteDM = useSocketStore((s) => s.lastDeleteDM);
@@ -4337,17 +4602,17 @@ var useChatSync = () => {
4337
4602
  const messagesByConversation = useSocketStore((s) => s.messagesByConversation);
4338
4603
  const messagesMetadata = useSocketStore((s) => s.messagesMetadata);
4339
4604
  const loggedUserDetails = useAuthStore((s) => s.loggedUserDetails);
4340
- React2.useEffect(() => {
4605
+ React3.useEffect(() => {
4341
4606
  setLocalMessages(
4342
4607
  (prev) => syncLocalMessagesOnStoreClear(prev, messagesByConversation, messagesMetadata)
4343
4608
  );
4344
4609
  }, [messagesByConversation, messagesMetadata]);
4345
- React2.useEffect(() => {
4610
+ React3.useEffect(() => {
4346
4611
  if (lastPinUpdate) {
4347
4612
  setLocalMessages((prev) => syncLocalMessagesOnPinUpdate(prev, lastPinUpdate));
4348
4613
  }
4349
4614
  }, [lastPinUpdate]);
4350
- React2.useEffect(() => {
4615
+ React3.useEffect(() => {
4351
4616
  if (lastStarUpdate) {
4352
4617
  setLocalMessages((prev) => {
4353
4618
  const next = syncLocalMessagesOnStarUpdate(
@@ -4359,18 +4624,18 @@ var useChatSync = () => {
4359
4624
  });
4360
4625
  }
4361
4626
  }, [lastStarUpdate, loggedUserDetails?._id]);
4362
- React2.useEffect(() => {
4627
+ React3.useEffect(() => {
4363
4628
  if (!lastReactionUpdate) return;
4364
4629
  setLocalMessages(
4365
4630
  (prev) => syncLocalMessagesOnReactionUpdate(prev, lastReactionUpdate, messagesByConversation)
4366
4631
  );
4367
4632
  }, [lastReactionUpdate, messagesByConversation]);
4368
- React2.useEffect(() => {
4633
+ React3.useEffect(() => {
4369
4634
  if (lastDeleteDM) {
4370
4635
  setLocalMessages((prev) => syncLocalMessagesOnDelete(prev, lastDeleteDM));
4371
4636
  }
4372
4637
  }, [lastDeleteDM]);
4373
- React2.useEffect(() => {
4638
+ React3.useEffect(() => {
4374
4639
  if (lastEditDM) {
4375
4640
  setLocalMessages((prev) => {
4376
4641
  const next = syncLocalMessagesOnEdit(prev, lastEditDM);
@@ -4378,7 +4643,7 @@ var useChatSync = () => {
4378
4643
  });
4379
4644
  }
4380
4645
  }, [lastEditDM]);
4381
- React2.useEffect(() => {
4646
+ React3.useEffect(() => {
4382
4647
  if (!lastDM) return;
4383
4648
  const result = buildIncomingMessageFromDM(
4384
4649
  lastDM,
@@ -4390,14 +4655,14 @@ var useChatSync = () => {
4390
4655
  (prev) => applyMessageUpdate(prev, result.storageKey, result.incomingMsg, result.isMe)
4391
4656
  );
4392
4657
  }, [lastDM, loggedUserDetails, conversations]);
4393
- React2.useEffect(() => {
4658
+ React3.useEffect(() => {
4394
4659
  if (lastStatusUpdate) {
4395
4660
  setLocalMessages(
4396
4661
  (prev) => syncLocalMessagesOnStatusUpdate(prev, lastStatusUpdate, loggedUserDetails)
4397
4662
  );
4398
4663
  }
4399
4664
  }, [lastStatusUpdate, loggedUserDetails]);
4400
- React2.useEffect(() => {
4665
+ React3.useEffect(() => {
4401
4666
  if (lastDMMarkRead) {
4402
4667
  setLocalMessages((prev) => {
4403
4668
  const next = syncLocalMessagesOnDmMarkRead(
@@ -4427,16 +4692,16 @@ var mergeChatCustomization = (config) => ({
4427
4692
  classNames: { ...defaultChatClassNames, ...config?.classNames ?? {} },
4428
4693
  features: { ...defaultChatFeatures, ...config?.features ?? {} }
4429
4694
  });
4430
- var ChatCustomizationContext = React2.createContext(void 0);
4695
+ var ChatCustomizationContext = React3.createContext(void 0);
4431
4696
  var ChatCustomizationProvider = ({
4432
4697
  children,
4433
4698
  customization
4434
4699
  }) => {
4435
- const merged = React2.useMemo(
4700
+ const merged = React3.useMemo(
4436
4701
  () => mergeChatCustomization(customization),
4437
4702
  [customization]
4438
4703
  );
4439
- const value = React2.useMemo(
4704
+ const value = React3.useMemo(
4440
4705
  () => ({
4441
4706
  components: merged.components,
4442
4707
  classNames: merged.classNames,
@@ -4452,7 +4717,7 @@ var ChatCustomizationProvider = ({
4452
4717
  return /* @__PURE__ */ jsxRuntime.jsx(ChatCustomizationContext.Provider, { value, children });
4453
4718
  };
4454
4719
  var useChatCustomization = () => {
4455
- const context = React2.useContext(ChatCustomizationContext);
4720
+ const context = React3.useContext(ChatCustomizationContext);
4456
4721
  if (!context) {
4457
4722
  throw new Error(
4458
4723
  "useChatCustomization must be used within ChatCustomizationProvider"
@@ -4461,7 +4726,7 @@ var useChatCustomization = () => {
4461
4726
  return context;
4462
4727
  };
4463
4728
  var useChatCustomizationOptional = () => {
4464
- const context = React2.useContext(ChatCustomizationContext);
4729
+ const context = React3.useContext(ChatCustomizationContext);
4465
4730
  return context ?? {
4466
4731
  components: {},
4467
4732
  classNames: {},
@@ -4472,9 +4737,146 @@ var useChatCustomizationOptional = () => {
4472
4737
  };
4473
4738
  var useChatFeatures = () => useChatCustomization().features;
4474
4739
  var useChatFeaturesOptional = () => useChatCustomizationOptional().features;
4475
- var ChatContext = React2.createContext(void 0);
4740
+
4741
+ // src/context/ChatEffectiveSettingsContext.tsx
4742
+ init_settings_api();
4743
+ init_settings_types();
4744
+ var ChatEffectiveSettingsContext = React3.createContext(null);
4745
+ function buildCallGate(settings, featureEnabled) {
4746
+ if (!settings.calls.configured) {
4747
+ return {
4748
+ visible: true,
4749
+ enabled: false,
4750
+ disabledReason: exports.CALLS_NOT_CONFIGURED_MESSAGE
4751
+ };
4752
+ }
4753
+ if (!settings.calls.enabled) {
4754
+ return {
4755
+ visible: true,
4756
+ enabled: false,
4757
+ disabledReason: exports.ADMIN_PERMISSION_DENIED_MESSAGE
4758
+ };
4759
+ }
4760
+ if (!featureEnabled) {
4761
+ return {
4762
+ visible: true,
4763
+ enabled: false,
4764
+ disabledReason: exports.ADMIN_PERMISSION_DENIED_MESSAGE
4765
+ };
4766
+ }
4767
+ return { visible: true, enabled: true, disabledReason: null };
4768
+ }
4769
+ function ChatEffectiveSettingsProvider({
4770
+ children,
4771
+ enabled = true
4772
+ }) {
4773
+ const [settings, setSettings] = React3.useState(
4774
+ exports.DEFAULT_EFFECTIVE_SETTINGS
4775
+ );
4776
+ const [loading, setLoading] = React3.useState(true);
4777
+ const [error, setError] = React3.useState(null);
4778
+ const refresh = React3.useCallback(async () => {
4779
+ if (!enabled) {
4780
+ setLoading(false);
4781
+ return;
4782
+ }
4783
+ setLoading(true);
4784
+ try {
4785
+ const next = await exports.fetchEffectiveSettings();
4786
+ setSettings(next);
4787
+ setError(null);
4788
+ } catch (err) {
4789
+ console.error("Failed to fetch effective settings:", err);
4790
+ setError(
4791
+ err instanceof Error ? err.message : "Could not load chat permissions"
4792
+ );
4793
+ setSettings(exports.DEFAULT_EFFECTIVE_SETTINGS);
4794
+ } finally {
4795
+ setLoading(false);
4796
+ }
4797
+ }, [enabled]);
4798
+ React3.useEffect(() => {
4799
+ void refresh();
4800
+ }, [refresh]);
4801
+ const value = React3.useMemo(() => {
4802
+ const isGroupPermissionLocked = (key) => settings.groupPolicy.locked.includes(key);
4803
+ return {
4804
+ settings,
4805
+ loading,
4806
+ error,
4807
+ refresh,
4808
+ isGroupPermissionLocked,
4809
+ getAudioCallGate: () => buildCallGate(settings, settings.audioCallEnabled),
4810
+ getVideoCallGate: () => buildCallGate(settings, settings.videoCallEnabled),
4811
+ getPushNotificationGate: () => ({
4812
+ visible: true,
4813
+ enabled: settings.pushNotificationEnabled,
4814
+ disabledReason: settings.pushNotificationEnabled ? null : exports.ADMIN_PERMISSION_DENIED_MESSAGE
4815
+ }),
4816
+ getBlockUsersGate: () => ({
4817
+ visible: true,
4818
+ enabled: settings.blockUsersEnabled,
4819
+ disabledReason: settings.blockUsersEnabled ? null : exports.ADMIN_PERMISSION_DENIED_MESSAGE
4820
+ }),
4821
+ getAttachmentsGate: () => ({
4822
+ visible: true,
4823
+ enabled: settings.attachments.enabled,
4824
+ disabledReason: settings.attachments.enabled ? null : exports.ADMIN_PERMISSION_DENIED_MESSAGE
4825
+ }),
4826
+ getGroupCreationGate: () => ({
4827
+ visible: true,
4828
+ enabled: settings.groupCreationEnabled,
4829
+ disabledReason: settings.groupCreationEnabled ? null : exports.ADMIN_PERMISSION_DENIED_MESSAGE
4830
+ })
4831
+ };
4832
+ }, [settings, loading, error, refresh]);
4833
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatEffectiveSettingsContext.Provider, { value, children });
4834
+ }
4835
+ function useEffectiveSettings() {
4836
+ const ctx = React3.useContext(ChatEffectiveSettingsContext);
4837
+ if (!ctx) {
4838
+ throw new Error(
4839
+ "useEffectiveSettings must be used within ChatEffectiveSettingsProvider"
4840
+ );
4841
+ }
4842
+ return ctx;
4843
+ }
4844
+ function useEffectiveSettingsOptional() {
4845
+ const ctx = React3.useContext(ChatEffectiveSettingsContext);
4846
+ if (ctx) return ctx;
4847
+ return {
4848
+ settings: exports.DEFAULT_EFFECTIVE_SETTINGS,
4849
+ loading: false,
4850
+ error: null,
4851
+ refresh: async () => void 0,
4852
+ isGroupPermissionLocked: () => false,
4853
+ getAudioCallGate: () => buildCallGate(exports.DEFAULT_EFFECTIVE_SETTINGS, true),
4854
+ getVideoCallGate: () => buildCallGate(exports.DEFAULT_EFFECTIVE_SETTINGS, true),
4855
+ getPushNotificationGate: () => ({
4856
+ visible: true,
4857
+ enabled: true,
4858
+ disabledReason: null
4859
+ }),
4860
+ getBlockUsersGate: () => ({
4861
+ visible: true,
4862
+ enabled: true,
4863
+ disabledReason: null
4864
+ }),
4865
+ getAttachmentsGate: () => ({
4866
+ visible: true,
4867
+ enabled: true,
4868
+ disabledReason: null
4869
+ }),
4870
+ getGroupCreationGate: () => ({
4871
+ visible: true,
4872
+ enabled: true,
4873
+ disabledReason: null
4874
+ })
4875
+ };
4876
+ }
4877
+ var ChatContext = React3.createContext(void 0);
4476
4878
  var useChatContext = () => {
4477
- const context = React2.useContext(ChatContext);
4879
+ const context = React3.useContext(ChatContext);
4478
4880
  if (!context) {
4479
4881
  throw new Error("useChatContext must be used within a Chat provider");
4480
4882
  }
@@ -4497,6 +4899,7 @@ var Chat = ({
4497
4899
  queryParams,
4498
4900
  queryParamApis,
4499
4901
  queryParamsByApi,
4902
+ apiVersions: apiVersions2,
4500
4903
  channelsData,
4501
4904
  messagesData,
4502
4905
  onMessageReceived,
@@ -4510,7 +4913,7 @@ var Chat = ({
4510
4913
  const status = exports.useChatStore((s) => s.status);
4511
4914
  const error = exports.useChatStore((s) => s.error);
4512
4915
  useChatSync();
4513
- React2__namespace.default.useEffect(() => {
4916
+ React3__namespace.default.useEffect(() => {
4514
4917
  const clientId = client?.id;
4515
4918
  const initializeChat = async () => {
4516
4919
  if (!clientId || !loggedUserDetails) return;
@@ -4522,6 +4925,7 @@ var Chat = ({
4522
4925
  const resolvedApiUrl = resolveApiUrl(apiUrl);
4523
4926
  const resolvedSocketUrl = resolveSocketUrl(socketUrl, resolvedApiUrl);
4524
4927
  if (resolvedApiUrl) exports.setChatBaseURL(resolvedApiUrl);
4928
+ exports.setChatApiVersions(apiVersions2);
4525
4929
  if (resolvedSocketUrl) exports.setChatSocketUrl(resolvedSocketUrl);
4526
4930
  let currentToken = accessToken || "";
4527
4931
  let sessionUser = resolveUserFromAccessToken(currentToken, loggedUserDetails);
@@ -4599,28 +5003,28 @@ var Chat = ({
4599
5003
  queryParamApis: queryParamApis ?? ["get"],
4600
5004
  queryParamsByApi: queryParamsByApi ?? {}
4601
5005
  });
4602
- React2__namespace.default.useEffect(() => {
5006
+ React3__namespace.default.useEffect(() => {
4603
5007
  exports.setChatQueryParams(queryParams);
4604
5008
  exports.setChatQueryParamApis(queryParamApis);
4605
5009
  exports.setChatQueryParamsByApi(queryParamsByApi);
4606
5010
  }, [queryConfigKey]);
4607
- React2__namespace.default.useEffect(() => {
5011
+ React3__namespace.default.useEffect(() => {
4608
5012
  if (lastDM && onMessageReceived) {
4609
5013
  onMessageReceived(lastDM);
4610
5014
  }
4611
5015
  }, [lastDM, onMessageReceived]);
4612
- React2__namespace.default.useEffect(() => {
5016
+ React3__namespace.default.useEffect(() => {
4613
5017
  if (status === "connected" && onSocketConnected) {
4614
5018
  const socketId = exports.useChatStore.getState().socketId;
4615
5019
  if (socketId) onSocketConnected(socketId);
4616
5020
  }
4617
5021
  }, [status, onSocketConnected]);
4618
- React2__namespace.default.useEffect(() => {
5022
+ React3__namespace.default.useEffect(() => {
4619
5023
  if (status === "error" && error && onSocketError) {
4620
5024
  onSocketError(error);
4621
5025
  }
4622
5026
  }, [status, error, onSocketError]);
4623
- const contextValue = React2__namespace.default.useMemo(
5027
+ const contextValue = React3__namespace.default.useMemo(
4624
5028
  () => ({
4625
5029
  onSendMessage,
4626
5030
  client,
@@ -4640,7 +5044,7 @@ var Chat = ({
4640
5044
  messagesData
4641
5045
  ]
4642
5046
  );
4643
- const customization = React2__namespace.default.useMemo(
5047
+ const customization = React3__namespace.default.useMemo(
4644
5048
  () => ({
4645
5049
  components: uiConfig.components,
4646
5050
  classNames: uiConfig.classNames,
@@ -4648,7 +5052,7 @@ var Chat = ({
4648
5052
  }),
4649
5053
  [uiConfig.components, uiConfig.classNames, uiConfig.features]
4650
5054
  );
4651
- return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chat-container-root flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? children : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-gray-400 font-bold", children: "Initializing session..." }) }) }) }) });
5055
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chat-container-root flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? /* @__PURE__ */ jsxRuntime.jsx(ChatEffectiveSettingsProvider, { enabled: true, children }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-gray-400 font-bold", children: "Initializing session..." }) }) }) }) });
4652
5056
  };
4653
5057
 
4654
5058
  // src/hooks/useChatController.ts
@@ -4946,7 +5350,7 @@ function toDisplayAttachments(attachments) {
4946
5350
  }));
4947
5351
  }
4948
5352
  var useChatController = () => {
4949
- const [state, setState] = React2.useState({
5353
+ const [state, setState] = React3.useState({
4950
5354
  activeChannel: null,
4951
5355
  activeConversationId: null,
4952
5356
  replyToMessage: null,
@@ -4956,15 +5360,15 @@ var useChatController = () => {
4956
5360
  debouncedSearch: "",
4957
5361
  isCreateModalOpen: false
4958
5362
  });
4959
- const updateState = React2.useCallback((updates) => {
5363
+ const updateState = React3.useCallback((updates) => {
4960
5364
  setState((prev) => ({ ...prev, ...updates }));
4961
5365
  }, []);
4962
- const handleSelectionModeChange = React2.useCallback((active) => {
5366
+ const handleSelectionModeChange = React3.useCallback((active) => {
4963
5367
  setState(
4964
5368
  (prev) => prev.isMessageSelectionMode === active ? prev : { ...prev, isMessageSelectionMode: active }
4965
5369
  );
4966
5370
  }, []);
4967
- const handleActiveChannelPatch = React2.useCallback((patch) => {
5371
+ const handleActiveChannelPatch = React3.useCallback((patch) => {
4968
5372
  setState(
4969
5373
  (prev) => prev.activeChannel ? { ...prev, activeChannel: { ...prev.activeChannel, ...patch } } : prev
4970
5374
  );
@@ -4988,11 +5392,11 @@ var useChatController = () => {
4988
5392
  const allUsers = exports.useChatStore((s) => s.allUsers);
4989
5393
  const isFetchingConversations = exports.useChatStore((s) => s.isFetchingConversations);
4990
5394
  const conversations = exports.useChatStore((s) => s.conversations);
4991
- const [pendingOpenRequest, setPendingOpenRequest] = React2.useState(() => peekPendingOpenRequest());
5395
+ const [pendingOpenRequest, setPendingOpenRequest] = React3.useState(() => peekPendingOpenRequest());
4992
5396
  const conversationsLength = exports.useChatStore(
4993
5397
  (s) => state.activeChannel?.conversationId ? 0 : s.conversations.length
4994
5398
  );
4995
- const lastConversationActionsRef = React2.useRef(null);
5399
+ const lastConversationActionsRef = React3.useRef(null);
4996
5400
  const activeChannelId = state.activeChannel?.id;
4997
5401
  const activeChannelConvId = state.activeChannel?.conversationId;
4998
5402
  const activeChannelIsGroup = !!state.activeChannel?.isGroup;
@@ -5017,7 +5421,7 @@ var useChatController = () => {
5017
5421
  isGroupConversation(latestInfo)
5018
5422
  ].join("|");
5019
5423
  });
5020
- React2.useEffect(() => {
5424
+ React3.useEffect(() => {
5021
5425
  const onOpen = (event) => {
5022
5426
  const detail = event.detail;
5023
5427
  if (detail?.conversationId) {
@@ -5034,7 +5438,7 @@ var useChatController = () => {
5034
5438
  window.addEventListener(OPEN_CONVERSATION_EVENT, onOpen);
5035
5439
  return () => window.removeEventListener(OPEN_CONVERSATION_EVENT, onOpen);
5036
5440
  }, []);
5037
- React2.useEffect(() => {
5441
+ React3.useEffect(() => {
5038
5442
  if (!pendingOpenRequest?.conversationId || !loggedUserDetails) return;
5039
5443
  let cancelled = false;
5040
5444
  const openPending = async () => {
@@ -5100,13 +5504,13 @@ var useChatController = () => {
5100
5504
  allUsers,
5101
5505
  fetchConversationInfo2
5102
5506
  ]);
5103
- React2.useEffect(() => {
5507
+ React3.useEffect(() => {
5104
5508
  const handler = setTimeout(() => {
5105
5509
  updateState({ debouncedSearch: state.searchQuery });
5106
5510
  }, 400);
5107
5511
  return () => clearTimeout(handler);
5108
5512
  }, [state.searchQuery, updateState]);
5109
- React2.useEffect(() => {
5513
+ React3.useEffect(() => {
5110
5514
  if (!state.activeChannel || !loggedUserDetails) {
5111
5515
  setState(
5112
5516
  (prev) => prev.activeConversationId === null ? prev : { ...prev, activeConversationId: null }
@@ -5122,7 +5526,7 @@ var useChatController = () => {
5122
5526
  (prev) => prev.activeConversationId === nextId ? prev : { ...prev, activeConversationId: nextId }
5123
5527
  );
5124
5528
  }, [state.activeChannel, loggedUserDetails, conversationsLength]);
5125
- React2.useEffect(() => {
5529
+ React3.useEffect(() => {
5126
5530
  if (!lastDM?.conversationId || !loggedUserDetails) return;
5127
5531
  const convId = String(lastDM.conversationId);
5128
5532
  const senderId = String(lastDM.sender?._id || lastDM.sender || "");
@@ -5155,7 +5559,7 @@ var useChatController = () => {
5155
5559
  };
5156
5560
  });
5157
5561
  }, [lastDM, loggedUserDetails]);
5158
- const resolvedLocalMessages = React2.useMemo(
5562
+ const resolvedLocalMessages = React3.useMemo(
5159
5563
  () => resolveLocalMessagesForChannel(
5160
5564
  localMessages,
5161
5565
  state.activeConversationId,
@@ -5163,7 +5567,7 @@ var useChatController = () => {
5163
5567
  ),
5164
5568
  [localMessages, state.activeConversationId, state.activeChannel?.id]
5165
5569
  );
5166
- const resolvedConversationMessages = React2.useMemo(() => {
5570
+ const resolvedConversationMessages = React3.useMemo(() => {
5167
5571
  if (state.activeConversationId) {
5168
5572
  return messagesByConversation[state.activeConversationId] || [];
5169
5573
  }
@@ -5171,7 +5575,7 @@ var useChatController = () => {
5171
5575
  const groupConversationId = String(state.activeChannel.id);
5172
5576
  return messagesByConversation[groupConversationId] || [];
5173
5577
  }, [messagesByConversation, state.activeConversationId, state.activeChannel]);
5174
- React2.useEffect(() => {
5578
+ React3.useEffect(() => {
5175
5579
  if (!activeChannelSyncKey || !state.activeChannel) return;
5176
5580
  const [convId, pinned, starred, isLeft, isBlocked, isGroup] = activeChannelSyncKey.split("|");
5177
5581
  setState((prev) => {
@@ -5194,10 +5598,10 @@ var useChatController = () => {
5194
5598
  };
5195
5599
  });
5196
5600
  }, [activeChannelSyncKey, state.activeChannel]);
5197
- React2.useEffect(() => {
5601
+ React3.useEffect(() => {
5198
5602
  setActiveConversationId(state.activeConversationId);
5199
5603
  }, [state.activeConversationId, setActiveConversationId]);
5200
- React2.useEffect(() => {
5604
+ React3.useEffect(() => {
5201
5605
  if (!state.activeConversationId || !state.activeChannel || !loggedUserDetails) {
5202
5606
  lastConversationActionsRef.current = null;
5203
5607
  return;
@@ -5244,7 +5648,7 @@ var useChatController = () => {
5244
5648
  emitGroupMarkRead,
5245
5649
  emitMarkAsRead
5246
5650
  ]);
5247
- React2.useEffect(() => {
5651
+ React3.useEffect(() => {
5248
5652
  if (globalAppError?.type === "participant" && state.activeChannel) {
5249
5653
  updateState({ activeChannel: null, activeConversationId: null });
5250
5654
  }
@@ -5421,7 +5825,7 @@ function withThemeTransition(apply) {
5421
5825
  }
5422
5826
  reactDom.flushSync(apply);
5423
5827
  }
5424
- var ChatThemeContext = React2.createContext(null);
5828
+ var ChatThemeContext = React3.createContext(null);
5425
5829
  var ChatThemeProvider = ({
5426
5830
  children,
5427
5831
  themeColor,
@@ -5431,10 +5835,10 @@ var ChatThemeProvider = ({
5431
5835
  showColorModeToggle = true
5432
5836
  }) => {
5433
5837
  const isControlled = colorMode !== void 0;
5434
- const [internalMode, setInternalMode] = React2.useState(defaultColorMode);
5435
- const [isThemeTransitionReady, setIsThemeTransitionReady] = React2.useState(false);
5436
- const skipThemeTransitionRef = React2.useRef(true);
5437
- React2.useLayoutEffect(() => {
5838
+ const [internalMode, setInternalMode] = React3.useState(defaultColorMode);
5839
+ const [isThemeTransitionReady, setIsThemeTransitionReady] = React3.useState(false);
5840
+ const skipThemeTransitionRef = React3.useRef(true);
5841
+ React3.useLayoutEffect(() => {
5438
5842
  if (!isControlled) {
5439
5843
  const stored = loadChatColorMode();
5440
5844
  if (stored) {
@@ -5449,7 +5853,7 @@ var ChatThemeProvider = ({
5449
5853
  }, [isControlled]);
5450
5854
  const resolvedMode = isControlled ? colorMode : internalMode;
5451
5855
  const isDark = resolvedMode === "dark";
5452
- const setColorMode = React2.useCallback(
5856
+ const setColorMode = React3.useCallback(
5453
5857
  (mode) => {
5454
5858
  const apply = () => {
5455
5859
  if (!isControlled) {
@@ -5466,10 +5870,10 @@ var ChatThemeProvider = ({
5466
5870
  },
5467
5871
  [isControlled, onColorModeChange]
5468
5872
  );
5469
- const toggleColorMode = React2.useCallback(() => {
5873
+ const toggleColorMode = React3.useCallback(() => {
5470
5874
  setColorMode(resolvedMode === "dark" ? "light" : "dark");
5471
5875
  }, [resolvedMode, setColorMode]);
5472
- const value = React2.useMemo(
5876
+ const value = React3.useMemo(
5473
5877
  () => ({
5474
5878
  colorMode: resolvedMode,
5475
5879
  isDark,
@@ -5497,7 +5901,7 @@ var ChatThemeProvider = ({
5497
5901
  return /* @__PURE__ */ jsxRuntime.jsx(ChatThemeContext.Provider, { value, children });
5498
5902
  };
5499
5903
  var useChatTheme = () => {
5500
- const context = React2.useContext(ChatThemeContext);
5904
+ const context = React3.useContext(ChatThemeContext);
5501
5905
  if (!context) {
5502
5906
  throw new Error("useChatTheme must be used within ChatThemeProvider");
5503
5907
  }
@@ -5639,6 +6043,18 @@ function formatMemberName(name) {
5639
6043
  function formatLoggedUserName(name) {
5640
6044
  return name ? name.charAt(0).toUpperCase() + name.slice(1) : "User";
5641
6045
  }
6046
+
6047
+ // src/chat/sidebar/build-logged-user-profile.ts
6048
+ var buildLoggedUserProfile = (loggedUserDetails) => ({
6049
+ _id: loggedUserDetails?._id || "1",
6050
+ name: formatLoggedUserName(loggedUserDetails?.name),
6051
+ email: loggedUserDetails?.email || "user@example.com",
6052
+ role: loggedUserDetails?.role || "user",
6053
+ isActive: loggedUserDetails?.isActive ?? true,
6054
+ createdAt: loggedUserDetails?.createdAt ? new Date(loggedUserDetails.createdAt) : /* @__PURE__ */ new Date(),
6055
+ updatedAt: loggedUserDetails?.updatedAt ? new Date(loggedUserDetails.updatedAt) : /* @__PURE__ */ new Date(),
6056
+ image: loggedUserDetails?.image || void 0
6057
+ });
5642
6058
  function Dialog({
5643
6059
  ...props
5644
6060
  }) {
@@ -6030,7 +6446,7 @@ function useCreateChatDialog({
6030
6446
  onUserSelect,
6031
6447
  setIsCreateModalOpen
6032
6448
  }) {
6033
- const [state, setState] = React2.useState(initialState);
6449
+ const [state, setState] = React3.useState(initialState);
6034
6450
  const updateState = (updates) => {
6035
6451
  setState((prev) => ({ ...prev, ...updates }));
6036
6452
  };
@@ -6039,8 +6455,10 @@ function useCreateChatDialog({
6039
6455
  const fetchAllUsers = exports.useChatStore((storeState) => storeState.fetchAllUsers);
6040
6456
  const accessToken = exports.useChatStore((storeState) => storeState.accessToken);
6041
6457
  const fetchConversations2 = exports.useChatStore((storeState) => storeState.fetchConversations);
6042
- const fileInputRef = React2.useRef(null);
6043
- React2.useEffect(() => {
6458
+ const { settings } = useEffectiveSettingsOptional();
6459
+ const maxParticipants = settings.maxParticipantsPerGroup;
6460
+ const fileInputRef = React3.useRef(null);
6461
+ React3.useEffect(() => {
6044
6462
  if (accessToken) {
6045
6463
  fetchAllUsers();
6046
6464
  }
@@ -6052,9 +6470,18 @@ function useCreateChatDialog({
6052
6470
  setState((prev) => {
6053
6471
  const memberId = String(member._id);
6054
6472
  const exists = prev.selectedMembers.some((m) => String(m._id) === memberId);
6473
+ if (exists) {
6474
+ return {
6475
+ ...prev,
6476
+ selectedMembers: prev.selectedMembers.filter((m) => String(m._id) !== memberId)
6477
+ };
6478
+ }
6479
+ if (prev.selectedMembers.length + 1 >= maxParticipants) {
6480
+ return prev;
6481
+ }
6055
6482
  return {
6056
6483
  ...prev,
6057
- selectedMembers: exists ? prev.selectedMembers.filter((m) => String(m._id) !== memberId) : [...prev.selectedMembers, member]
6484
+ selectedMembers: [...prev.selectedMembers, member]
6058
6485
  };
6059
6486
  });
6060
6487
  };
@@ -6438,8 +6865,25 @@ function GroupCreateTab({
6438
6865
  ) })
6439
6866
  ] });
6440
6867
  }
6868
+ function AdminPermissionTooltip({
6869
+ disabled,
6870
+ message,
6871
+ side = "top",
6872
+ className,
6873
+ children
6874
+ }) {
6875
+ if (!disabled || !message) {
6876
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
6877
+ }
6878
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
6879
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("inline-flex", className), children }) }),
6880
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side, className: "max-w-[240px] text-xs", children: message })
6881
+ ] });
6882
+ }
6441
6883
  var CreateChatDialog = (props) => {
6442
6884
  const { isCreateModalOpen = false, setIsCreateModalOpen } = props;
6885
+ const { getGroupCreationGate } = useEffectiveSettingsOptional();
6886
+ const groupCreationGate = getGroupCreationGate();
6443
6887
  const {
6444
6888
  state,
6445
6889
  updateState,
@@ -6493,24 +6937,39 @@ var CreateChatDialog = (props) => {
6493
6937
  ) })
6494
6938
  ] }),
6495
6939
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid shrink-0 grid-cols-2 gap-2 px-5", children: [
6496
- { id: "dm", label: "Message", icon: lucideReact.MessageCircle },
6497
- { id: "group", label: "New group", icon: lucideReact.Users }
6498
- ].map(({ id, label, icon: Icon }) => /* @__PURE__ */ jsxRuntime.jsxs(
6499
- "button",
6940
+ { id: "dm", label: "Message", icon: lucideReact.MessageCircle, disabled: false, reason: null },
6500
6941
  {
6501
- type: "button",
6502
- onClick: () => updateState({ activeTab: id }),
6503
- className: cn(
6504
- "flex items-center justify-center gap-2 rounded-full border py-2 text-sm transition-colors",
6505
- state.activeTab === id ? "border-(--chat-theme-20) bg-(--chat-theme-5) text-(--chat-theme)" : "border-(--chat-border) text-(--chat-muted) hover:border-(--chat-theme-20) hover:text-(--chat-text)"
6506
- ),
6507
- children: [
6508
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }),
6509
- label
6510
- ]
6511
- },
6512
- id
6513
- )) }),
6942
+ id: "group",
6943
+ label: "New group",
6944
+ icon: lucideReact.Users,
6945
+ disabled: !groupCreationGate.enabled,
6946
+ reason: groupCreationGate.disabledReason
6947
+ }
6948
+ ].map(({ id, label, icon: Icon, disabled, reason }) => {
6949
+ const tabButton = /* @__PURE__ */ jsxRuntime.jsxs(
6950
+ "button",
6951
+ {
6952
+ type: "button",
6953
+ disabled,
6954
+ onClick: () => {
6955
+ if (disabled) return;
6956
+ updateState({ activeTab: id });
6957
+ },
6958
+ className: cn(
6959
+ "flex w-full items-center justify-center gap-2 rounded-full border py-2 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-40",
6960
+ state.activeTab === id ? "border-(--chat-theme-20) bg-(--chat-theme-5) text-(--chat-theme)" : "border-(--chat-border) text-(--chat-muted) hover:border-(--chat-theme-20) hover:text-(--chat-text)"
6961
+ ),
6962
+ children: [
6963
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }),
6964
+ label
6965
+ ]
6966
+ }
6967
+ );
6968
+ if (!disabled || !reason) {
6969
+ return /* @__PURE__ */ jsxRuntime.jsx(React3.Fragment, { children: tabButton }, id);
6970
+ }
6971
+ return /* @__PURE__ */ jsxRuntime.jsx(AdminPermissionTooltip, { disabled: true, message: reason, children: tabButton }, id);
6972
+ }) }),
6514
6973
  state.activeTab === "dm" && /* @__PURE__ */ jsxRuntime.jsx(
6515
6974
  DmContactsTab,
6516
6975
  {
@@ -6521,7 +6980,7 @@ var CreateChatDialog = (props) => {
6521
6980
  onStartDM: handleStartDM
6522
6981
  }
6523
6982
  ),
6524
- state.activeTab === "group" && /* @__PURE__ */ jsxRuntime.jsx(
6983
+ state.activeTab === "group" && groupCreationGate.enabled && /* @__PURE__ */ jsxRuntime.jsx(
6525
6984
  GroupCreateTab,
6526
6985
  {
6527
6986
  state,
@@ -6573,6 +7032,7 @@ function Switch({
6573
7032
 
6574
7033
  // src/notifications/push.service.ts
6575
7034
  init_client();
7035
+ init_api_version();
6576
7036
  var PUSH_TOKEN_STORAGE_KEY = "realtimex-push-token";
6577
7037
  var SERVICE_WORKER_PATH = "/firebase-messaging-sw.js";
6578
7038
  var PUSH_CHANGED_EVENT = "realtimex:push-changed";
@@ -6584,33 +7044,33 @@ var emitPushChanged = (enabled) => {
6584
7044
  } catch {
6585
7045
  }
6586
7046
  };
6587
- var v2Url = "https://realtimex.softwareco.com/api/v2";
7047
+ var pushApiUrl = (path) => exports.buildVersionedApiUrl(path, { service: "v2" });
6588
7048
  var apiGetPushConfig = async () => {
6589
- const { data } = await apiClient.get(`${v2Url}/notifications/push/config`);
7049
+ const { data } = await apiClient.get(pushApiUrl("/notifications/push/config"));
6590
7050
  return data?.data;
6591
7051
  };
6592
7052
  var apiRegisterPushToken = async (token, deviceLabel) => {
6593
- const { data } = await apiClient.post(`${v2Url}/notifications/push/token`, {
7053
+ const { data } = await apiClient.post(pushApiUrl("/notifications/push/token"), {
6594
7054
  token,
6595
7055
  deviceLabel
6596
7056
  });
6597
7057
  return data?.data;
6598
7058
  };
6599
7059
  var apiRemovePushToken = async (token) => {
6600
- const { data } = await apiClient.delete(`${v2Url}/notifications/push/token`, {
7060
+ const { data } = await apiClient.delete(pushApiUrl("/notifications/push/token"), {
6601
7061
  data: { token }
6602
7062
  });
6603
7063
  return data;
6604
7064
  };
6605
7065
  var apiGetPushPreference = async () => {
6606
7066
  const { data } = await apiClient.get(
6607
- `${v2Url}/notifications/push/preference`
7067
+ pushApiUrl("/notifications/push/preference")
6608
7068
  );
6609
7069
  return data?.data;
6610
7070
  };
6611
7071
  var apiUpdatePushPreference = async (enabled) => {
6612
7072
  const { data } = await apiClient.patch(
6613
- `${v2Url}/notifications/push/preference`,
7073
+ pushApiUrl("/notifications/push/preference"),
6614
7074
  { enabled }
6615
7075
  );
6616
7076
  return data?.data;
@@ -6733,7 +7193,7 @@ var onForegroundPush = async (callback) => {
6733
7193
 
6734
7194
  // src/notifications/usePushNotifications.ts
6735
7195
  var usePushNotifications = () => {
6736
- const [state, setState] = React2.useState({
7196
+ const [state, setState] = React3.useState({
6737
7197
  supported: false,
6738
7198
  allowedByWorkspace: false,
6739
7199
  configured: false,
@@ -6743,7 +7203,7 @@ var usePushNotifications = () => {
6743
7203
  loading: true,
6744
7204
  error: null
6745
7205
  });
6746
- const refresh = React2.useCallback(async () => {
7206
+ const refresh = React3.useCallback(async () => {
6747
7207
  if (!isPushSupported()) {
6748
7208
  setState((prev) => ({
6749
7209
  ...prev,
@@ -6776,10 +7236,10 @@ var usePushNotifications = () => {
6776
7236
  }));
6777
7237
  }
6778
7238
  }, []);
6779
- React2.useEffect(() => {
7239
+ React3.useEffect(() => {
6780
7240
  void refresh();
6781
7241
  }, [refresh]);
6782
- const enable = React2.useCallback(async () => {
7242
+ const enable = React3.useCallback(async () => {
6783
7243
  setState((prev) => ({ ...prev, loading: true, error: null }));
6784
7244
  try {
6785
7245
  await enablePushOnThisDevice();
@@ -6795,7 +7255,7 @@ var usePushNotifications = () => {
6795
7255
  return false;
6796
7256
  }
6797
7257
  }, [refresh]);
6798
- const disable = React2.useCallback(async () => {
7258
+ const disable = React3.useCallback(async () => {
6799
7259
  setState((prev) => ({ ...prev, loading: true, error: null }));
6800
7260
  try {
6801
7261
  await disablePushOnThisDevice();
@@ -6803,15 +7263,17 @@ var usePushNotifications = () => {
6803
7263
  await refresh();
6804
7264
  }
6805
7265
  }, [refresh]);
6806
- const setUserEnabled = React2.useCallback(
7266
+ const setUserEnabled = React3.useCallback(
6807
7267
  async (enabled) => {
6808
- setState((prev) => ({ ...prev, userEnabled: enabled }));
7268
+ setState((prev) => ({ ...prev, loading: true, error: null }));
6809
7269
  try {
6810
7270
  await apiUpdatePushPreference(enabled);
7271
+ setState((prev) => ({ ...prev, userEnabled: enabled, loading: false }));
6811
7272
  } catch (error) {
6812
7273
  setState((prev) => ({
6813
7274
  ...prev,
6814
7275
  userEnabled: !enabled,
7276
+ loading: false,
6815
7277
  error: error instanceof Error ? error.message : "Could not update push preference"
6816
7278
  }));
6817
7279
  }
@@ -6823,95 +7285,229 @@ var usePushNotifications = () => {
6823
7285
  var PushNotificationToggle = ({
6824
7286
  className,
6825
7287
  label = "Push notifications",
6826
- description = "Get notified about new messages on this device"
7288
+ description = "Get notified about new messages on this device",
7289
+ children
6827
7290
  }) => {
6828
7291
  const push = usePushNotifications();
6829
- if (!push.supported || !push.allowedByWorkspace || !push.configured) {
7292
+ const { getPushNotificationGate } = useEffectiveSettingsOptional();
7293
+ const adminGate = getPushNotificationGate();
7294
+ const [isToggling, setIsToggling] = React3.useState(false);
7295
+ if (!push.supported) {
6830
7296
  return null;
6831
7297
  }
6832
7298
  const isOn = push.deviceEnabled && push.userEnabled;
6833
7299
  const permissionBlocked = push.permission === "denied";
7300
+ const adminBlocked = !adminGate.enabled;
7301
+ const notConfigured = !push.configured;
7302
+ const showLoader = isToggling;
7303
+ const isDisabled = showLoader || permissionBlocked || push.loading || adminBlocked || notConfigured;
6834
7304
  const handleChange = async (checked) => {
6835
- if (checked) {
6836
- if (!push.userEnabled) await push.setUserEnabled(true);
6837
- if (!push.deviceEnabled) await push.enable();
6838
- } else {
6839
- await push.disable();
7305
+ if (adminBlocked || notConfigured) return;
7306
+ setIsToggling(true);
7307
+ try {
7308
+ if (checked) {
7309
+ if (!push.userEnabled) await push.setUserEnabled(true);
7310
+ if (!push.deviceEnabled) await push.enable();
7311
+ } else {
7312
+ await push.disable();
7313
+ }
7314
+ } finally {
7315
+ setIsToggling(false);
6840
7316
  }
6841
7317
  };
7318
+ const descriptionText = adminBlocked ? adminGate.disabledReason || description : notConfigured ? "Push notifications are not configured. Please contact your administrator." : permissionBlocked ? "Notifications are blocked in your browser settings" : push.error ?? description;
7319
+ const renderProps = {
7320
+ label,
7321
+ description: descriptionText,
7322
+ checked: adminBlocked || notConfigured ? false : isOn,
7323
+ disabled: isDisabled,
7324
+ loading: showLoader || push.loading,
7325
+ permissionBlocked,
7326
+ error: push.error,
7327
+ onCheckedChange: (checked) => void handleChange(checked)
7328
+ };
7329
+ if (children) {
7330
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(renderProps) });
7331
+ }
6842
7332
  return /* @__PURE__ */ jsxRuntime.jsxs(
6843
7333
  "div",
6844
7334
  {
6845
- className: className ?? "flex items-center justify-between gap-4 rounded-xl border border-(--chat-border) bg-(--chat-surface) px-4 py-3",
7335
+ className: cn(
7336
+ "flex items-center justify-between gap-4 rounded-xl border border-(--chat-border) bg-(--chat-surface) px-4 py-3",
7337
+ className
7338
+ ),
6846
7339
  children: [
6847
7340
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
6848
7341
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-(--chat-text)", children: label }),
6849
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-(--chat-muted)", children: permissionBlocked ? "Notifications are blocked in your browser settings" : push.error ?? description })
7342
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-(--chat-muted)", children: renderProps.description })
6850
7343
  ] }),
6851
- /* @__PURE__ */ jsxRuntime.jsx(
6852
- Switch,
7344
+ showLoader ? /* @__PURE__ */ jsxRuntime.jsx(
7345
+ lucideReact.Loader2,
6853
7346
  {
6854
- checked: isOn,
6855
- disabled: push.loading || permissionBlocked,
6856
- onCheckedChange: (checked) => void handleChange(checked),
6857
- "aria-label": `Toggle ${label}`
7347
+ className: "size-5 shrink-0 animate-spin text-(--chat-theme)",
7348
+ "aria-label": "Updating push notifications"
7349
+ }
7350
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
7351
+ AdminPermissionTooltip,
7352
+ {
7353
+ disabled: adminBlocked || notConfigured,
7354
+ message: adminBlocked ? adminGate.disabledReason : "Push notifications are not configured. Please contact your administrator.",
7355
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7356
+ Switch,
7357
+ {
7358
+ checked: adminBlocked || notConfigured ? false : isOn,
7359
+ disabled: isDisabled,
7360
+ onCheckedChange: (checked) => void handleChange(checked),
7361
+ "aria-label": `Toggle ${label}`
7362
+ }
7363
+ )
6858
7364
  }
6859
7365
  )
6860
7366
  ]
6861
7367
  }
6862
7368
  );
6863
7369
  };
6864
- var LoggedUserSettingsDialog = ({
6865
- loggedUserDetails
7370
+ var LoggedUserSettingsContent = ({
7371
+ user,
7372
+ title = "Account settings",
7373
+ showProfile = true,
7374
+ showPushToggle = true,
7375
+ className
6866
7376
  }) => {
6867
- const [open, setOpen] = React2.useState(false);
6868
- const user = {
6869
- _id: loggedUserDetails?._id || "1",
6870
- name: formatLoggedUserName(loggedUserDetails?.name),
6871
- email: loggedUserDetails?.email || "user@example.com",
6872
- role: loggedUserDetails?.role || "user",
6873
- isActive: loggedUserDetails?.isActive ?? true,
6874
- createdAt: loggedUserDetails?.createdAt ? new Date(loggedUserDetails.createdAt) : /* @__PURE__ */ new Date(),
6875
- updatedAt: loggedUserDetails?.updatedAt ? new Date(loggedUserDetails.updatedAt) : /* @__PURE__ */ new Date(),
6876
- image: loggedUserDetails?.image || void 0
6877
- };
6878
- return /* @__PURE__ */ jsxRuntime.jsxs(Dialog, { open, onOpenChange: setOpen, children: [
6879
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
6880
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
6881
- Button,
6882
- {
6883
- type: "button",
6884
- variant: "ghost",
6885
- size: "icon",
6886
- className: sidebarActionBtnClass,
6887
- "aria-label": "Account settings",
6888
- onClick: () => setOpen(true),
6889
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "size-3.5", strokeWidth: 2 })
6890
- }
6891
- ) }),
6892
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Settings" })
6893
- ] }),
6894
- /* @__PURE__ */ jsxRuntime.jsxs(ChatDialogContent, { className: "gap-0 overflow-hidden p-0 sm:max-w-[380px]", children: [
6895
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-(--chat-border) px-5 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "text-left text-base font-semibold text-(--chat-text)", children: "Account settings" }) }),
6896
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5 px-5 py-5", children: [
6897
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", children: [
6898
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6899
- /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: "size-16 ring-2 ring-(--chat-theme-20)", children: [
6900
- user.image ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: user.image, alt: user.name }) : null,
6901
- /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-(--chat-theme-10) text-lg font-medium text-(--chat-theme)", children: (user.name || "?").charAt(0).toUpperCase() })
7377
+ const { resolveComponent, slotClassName } = useChatCustomization();
7378
+ const PushToggleSlot = resolveComponent(
7379
+ "PushNotificationToggle",
7380
+ PushNotificationToggle
7381
+ );
7382
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7383
+ /* @__PURE__ */ jsxRuntime.jsx(
7384
+ "div",
7385
+ {
7386
+ className: cn(
7387
+ "border-b border-(--chat-border) px-5 py-4",
7388
+ slotClassName("loggedUserSettingsHeader")
7389
+ ),
7390
+ children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "text-left text-base font-semibold text-(--chat-text)", children: title })
7391
+ }
7392
+ ),
7393
+ /* @__PURE__ */ jsxRuntime.jsxs(
7394
+ "div",
7395
+ {
7396
+ className: cn(
7397
+ "space-y-5 px-5 py-5",
7398
+ slotClassName("loggedUserSettingsContent", className)
7399
+ ),
7400
+ children: [
7401
+ showProfile ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", children: [
7402
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
7403
+ /* @__PURE__ */ jsxRuntime.jsxs(
7404
+ Avatar,
7405
+ {
7406
+ className: cn(
7407
+ "size-16 ring-2 ring-(--chat-theme-20)",
7408
+ slotClassName("loggedUserSettingsAvatar")
7409
+ ),
7410
+ children: [
7411
+ user.image ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: user.image, alt: user.name }) : null,
7412
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-(--chat-theme-10) text-lg font-medium text-(--chat-theme)", children: (user.name || "?").charAt(0).toUpperCase() })
7413
+ ]
7414
+ }
7415
+ ),
7416
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0.5 right-0.5 size-3 rounded-full border-2 border-(--chat-surface) bg-(--chat-online)" })
6902
7417
  ] }),
6903
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0.5 right-0.5 size-3 rounded-full border-2 border-(--chat-surface) bg-(--chat-online)" })
6904
- ] }),
6905
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mt-3 text-base font-semibold text-(--chat-text)", children: user.name }),
6906
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-sm text-(--chat-muted)", children: user.email }),
6907
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 inline-flex items-center gap-1.5 rounded-full bg-(--chat-theme-10) px-2.5 py-0.5 text-xs font-medium text-(--chat-theme)", children: [
6908
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1.5 rounded-full bg-(--chat-online)" }),
6909
- "Online"
6910
- ] })
6911
- ] }),
6912
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-(--chat-border) pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(PushNotificationToggle, { className: "flex items-center justify-between gap-4 rounded-xl border border-(--chat-border) bg-(--chat-theme-10) px-4 py-3" }) })
6913
- ] })
6914
- ] })
7418
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mt-3 text-base font-semibold text-(--chat-text)", children: user.name }),
7419
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-sm text-(--chat-muted)", children: user.email }),
7420
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 inline-flex items-center gap-1.5 rounded-full bg-(--chat-theme-10) px-2.5 py-0.5 text-xs font-medium text-(--chat-theme)", children: [
7421
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1.5 rounded-full bg-(--chat-online)" }),
7422
+ "Online"
7423
+ ] })
7424
+ ] }) : null,
7425
+ showPushToggle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-(--chat-border) pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
7426
+ PushToggleSlot,
7427
+ {
7428
+ className: cn(
7429
+ "flex items-center justify-between gap-4 rounded-xl border border-(--chat-border) bg-(--chat-theme-10) px-4 py-3",
7430
+ slotClassName("pushNotificationToggle")
7431
+ )
7432
+ }
7433
+ ) }) : null
7434
+ ]
7435
+ }
7436
+ )
7437
+ ] });
7438
+ };
7439
+ var LoggedUserSettingsContent_default = LoggedUserSettingsContent;
7440
+ var LoggedUserSettingsTrigger = ({
7441
+ onClick,
7442
+ className,
7443
+ tooltip = "Settings",
7444
+ ariaLabel = "Account settings"
7445
+ }) => {
7446
+ const { slotClassName } = useChatCustomization();
7447
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7448
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7449
+ Button,
7450
+ {
7451
+ type: "button",
7452
+ variant: "ghost",
7453
+ size: "icon",
7454
+ className: cn(
7455
+ sidebarActionBtnClass,
7456
+ slotClassName("loggedUserSettingsTrigger", className)
7457
+ ),
7458
+ "aria-label": ariaLabel,
7459
+ onClick,
7460
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "size-3.5", strokeWidth: 2 })
7461
+ }
7462
+ ) }),
7463
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: tooltip })
7464
+ ] });
7465
+ };
7466
+ var LoggedUserSettingsTrigger_default = LoggedUserSettingsTrigger;
7467
+ var LoggedUserSettingsDialog = ({
7468
+ loggedUserDetails,
7469
+ open: controlledOpen,
7470
+ onOpenChange: controlledOnOpenChange,
7471
+ title,
7472
+ showProfile = true,
7473
+ showPushToggle = true,
7474
+ className
7475
+ }) => {
7476
+ const { resolveComponent, slotClassName } = useChatCustomization();
7477
+ const [internalOpen, setInternalOpen] = React3.useState(false);
7478
+ const open = controlledOpen ?? internalOpen;
7479
+ const onOpenChange = controlledOnOpenChange ?? setInternalOpen;
7480
+ const TriggerSlot = resolveComponent(
7481
+ "LoggedUserSettingsTrigger",
7482
+ LoggedUserSettingsTrigger_default
7483
+ );
7484
+ const ContentSlot = resolveComponent(
7485
+ "LoggedUserSettingsContent",
7486
+ LoggedUserSettingsContent_default
7487
+ );
7488
+ const user = buildLoggedUserProfile(loggedUserDetails);
7489
+ return /* @__PURE__ */ jsxRuntime.jsxs(Dialog, { open, onOpenChange, children: [
7490
+ /* @__PURE__ */ jsxRuntime.jsx(TriggerSlot, { onClick: () => onOpenChange(true) }),
7491
+ /* @__PURE__ */ jsxRuntime.jsx(
7492
+ ChatDialogContent,
7493
+ {
7494
+ className: cn(
7495
+ "gap-0 overflow-hidden p-0 sm:max-w-[380px]",
7496
+ slotClassName("loggedUserSettingsDialog", className)
7497
+ ),
7498
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7499
+ ContentSlot,
7500
+ {
7501
+ loggedUserDetails,
7502
+ user,
7503
+ onClose: () => onOpenChange(false),
7504
+ title,
7505
+ showProfile,
7506
+ showPushToggle
7507
+ }
7508
+ )
7509
+ }
7510
+ )
6915
7511
  ] });
6916
7512
  };
6917
7513
  var LoggedUserSettingsDialog_default = LoggedUserSettingsDialog;
@@ -6924,16 +7520,12 @@ var LoggedUserDetails = ({
6924
7520
  setSearchQuery
6925
7521
  }) => {
6926
7522
  const { showColorModeToggle } = useChatTheme();
6927
- const user = {
6928
- _id: loggedUserDetails?._id || "1",
6929
- name: formatLoggedUserName(loggedUserDetails?.name),
6930
- email: loggedUserDetails?.email || "user@example.com",
6931
- role: loggedUserDetails?.role || "user",
6932
- isActive: loggedUserDetails?.isActive ?? true,
6933
- createdAt: loggedUserDetails?.createdAt ? new Date(loggedUserDetails.createdAt) : /* @__PURE__ */ new Date(),
6934
- updatedAt: loggedUserDetails?.updatedAt ? new Date(loggedUserDetails.updatedAt) : /* @__PURE__ */ new Date(),
6935
- image: loggedUserDetails?.image || void 0
6936
- };
7523
+ const { resolveComponent } = useChatCustomization();
7524
+ const SettingsDialogSlot = resolveComponent(
7525
+ "LoggedUserSettingsDialog",
7526
+ LoggedUserSettingsDialog_default
7527
+ );
7528
+ const user = buildLoggedUserProfile(loggedUserDetails);
6937
7529
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b border-(--chat-border) bg-(--chat-surface) px-3 pb-3 pt-3", children: [
6938
7530
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [
6939
7531
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative shrink-0", children: [
@@ -6944,14 +7536,11 @@ var LoggedUserDetails = ({
6944
7536
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -bottom-0.5 -right-0.5 size-2 rounded-full border-2 border-(--chat-surface) bg-(--chat-online)" })
6945
7537
  ] }),
6946
7538
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
6947
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "truncate text-xs font-medium leading-tight text-(--chat-text)", children: [
6948
- user.name,
6949
- "123"
6950
- ] }),
7539
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "truncate text-xs font-medium leading-tight text-(--chat-text)", children: user.name }),
6951
7540
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-[10px] text-(--chat-muted)", children: user.email })
6952
7541
  ] }),
6953
7542
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [
6954
- /* @__PURE__ */ jsxRuntime.jsx(LoggedUserSettingsDialog_default, { loggedUserDetails }),
7543
+ /* @__PURE__ */ jsxRuntime.jsx(SettingsDialogSlot, { loggedUserDetails }),
6955
7544
  showColorModeToggle ? /* @__PURE__ */ jsxRuntime.jsx(ChatThemeToggle_default, {}) : null
6956
7545
  ] })
6957
7546
  ] }),
@@ -7073,7 +7662,7 @@ function getDraftRevision() {
7073
7662
  return draftRevision;
7074
7663
  }
7075
7664
  function useDraftRevision() {
7076
- return React2.useSyncExternalStore(subscribeToDrafts, getDraftRevision, getDraftRevision);
7665
+ return React3.useSyncExternalStore(subscribeToDrafts, getDraftRevision, getDraftRevision);
7077
7666
  }
7078
7667
  function getConversationDraft(conversationId) {
7079
7668
  if (!conversationId) return emptyDraft();
@@ -7111,11 +7700,11 @@ function getConversationDraftPreview(conversationId) {
7111
7700
  return null;
7112
7701
  }
7113
7702
  function useConversationDraft(conversationId) {
7114
- const composerRef = React2.useRef(emptyDraft());
7115
- const syncComposerRef = React2.useCallback((draft) => {
7703
+ const composerRef = React3.useRef(emptyDraft());
7704
+ const syncComposerRef = React3.useCallback((draft) => {
7116
7705
  composerRef.current = draft;
7117
7706
  }, []);
7118
- React2.useEffect(() => {
7707
+ React3.useEffect(() => {
7119
7708
  const id = conversationId;
7120
7709
  return () => {
7121
7710
  if (!id) return;
@@ -7334,7 +7923,7 @@ var saveChatDateTimePreferences = (prefs) => {
7334
7923
  } catch {
7335
7924
  }
7336
7925
  };
7337
- var ChatLocaleContext = React2.createContext(null);
7926
+ var ChatLocaleContext = React3.createContext(null);
7338
7927
  var resolveInitialPrefs = (defaults) => {
7339
7928
  const stored = loadChatDateTimePreferences();
7340
7929
  const fallback = resolveStoredPrefs(defaults);
@@ -7361,7 +7950,7 @@ var ChatLocaleProvider = ({
7361
7950
  const localeControlled = controlledLocale !== void 0;
7362
7951
  const timeZoneControlled = controlledTimeZone !== void 0;
7363
7952
  const hour12Controlled = controlledHour12 !== void 0;
7364
- const [internalPrefs, setInternalPrefs] = React2.useState(
7953
+ const [internalPrefs, setInternalPrefs] = React3.useState(
7365
7954
  () => resolveInitialPrefs({
7366
7955
  locale: defaultLocale,
7367
7956
  timeZone: defaultTimeZone,
@@ -7371,7 +7960,7 @@ var ChatLocaleProvider = ({
7371
7960
  const resolvedLocale = localeControlled ? controlledLocale : internalPrefs.locale;
7372
7961
  const resolvedTimeZone = timeZoneControlled ? controlledTimeZone : internalPrefs.timeZone;
7373
7962
  const resolvedHour12 = hour12Controlled ? controlledHour12 ?? getDefaultHour12() : internalPrefs.hour12 ?? getDefaultHour12();
7374
- const updatePrefs = React2.useCallback(
7963
+ const updatePrefs = React3.useCallback(
7375
7964
  (updates) => {
7376
7965
  const next = {
7377
7966
  locale: updates.locale !== void 0 ? updates.locale : resolvedLocale,
@@ -7400,19 +7989,19 @@ var ChatLocaleProvider = ({
7400
7989
  onDateTimePreferencesChange
7401
7990
  ]
7402
7991
  );
7403
- const setLocale = React2.useCallback(
7992
+ const setLocale = React3.useCallback(
7404
7993
  (locale) => updatePrefs({ locale }),
7405
7994
  [updatePrefs]
7406
7995
  );
7407
- const setTimeZone = React2.useCallback(
7996
+ const setTimeZone = React3.useCallback(
7408
7997
  (timeZone) => updatePrefs({ timeZone }),
7409
7998
  [updatePrefs]
7410
7999
  );
7411
- const setHour12 = React2.useCallback(
8000
+ const setHour12 = React3.useCallback(
7412
8001
  (hour12) => updatePrefs({ hour12 }),
7413
8002
  [updatePrefs]
7414
8003
  );
7415
- const prefs = React2.useMemo(
8004
+ const prefs = React3.useMemo(
7416
8005
  () => ({
7417
8006
  locale: resolvedLocale,
7418
8007
  timeZone: resolvedTimeZone,
@@ -7420,8 +8009,8 @@ var ChatLocaleProvider = ({
7420
8009
  }),
7421
8010
  [resolvedLocale, resolvedTimeZone, resolvedHour12]
7422
8011
  );
7423
- const formatters = React2.useMemo(() => createChatDateTimeFormatters(prefs), [prefs]);
7424
- const value = React2.useMemo(
8012
+ const formatters = React3.useMemo(() => createChatDateTimeFormatters(prefs), [prefs]);
8013
+ const value = React3.useMemo(
7425
8014
  () => ({
7426
8015
  ...formatters,
7427
8016
  locale: resolvedLocale,
@@ -7446,7 +8035,7 @@ var ChatLocaleProvider = ({
7446
8035
  return /* @__PURE__ */ jsxRuntime.jsx(ChatLocaleContext.Provider, { value, children });
7447
8036
  };
7448
8037
  var useChatLocale = () => {
7449
- const context = React2.useContext(ChatLocaleContext);
8038
+ const context = React3.useContext(ChatLocaleContext);
7450
8039
  if (!context) {
7451
8040
  throw new Error("useChatLocale must be used within ChatLocaleProvider");
7452
8041
  }
@@ -7568,15 +8157,15 @@ function useChannelList({ debouncedSearch = "" }) {
7568
8157
  const conversationsPage = exports.useChatStore((s) => s.conversationsPage);
7569
8158
  const accessToken = exports.useChatStore((s) => s.accessToken);
7570
8159
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
7571
- const lastFetchKeyRef = React2.useRef(null);
8160
+ const lastFetchKeyRef = React3.useRef(null);
7572
8161
  useDraftRevision();
7573
8162
  const { formatSidebarTime } = useChatLocale();
7574
- React2.useEffect(() => {
8163
+ React3.useEffect(() => {
7575
8164
  if (accessToken) {
7576
8165
  exports.useChatStore.getState().fetchAllUsers();
7577
8166
  }
7578
8167
  }, [accessToken]);
7579
- React2.useEffect(() => {
8168
+ React3.useEffect(() => {
7580
8169
  if (!accessToken) return;
7581
8170
  const fetchKey = debouncedSearch;
7582
8171
  if (lastFetchKeyRef.current === fetchKey) return;
@@ -7588,7 +8177,7 @@ function useChannelList({ debouncedSearch = "" }) {
7588
8177
  }
7589
8178
  fetchConversations2(DEFAULT_CONVERSATIONS_LIMIT, 1, debouncedSearch, true);
7590
8179
  }, [accessToken, debouncedSearch]);
7591
- const mappedChannels = React2.useMemo(() => {
8180
+ const mappedChannels = React3.useMemo(() => {
7592
8181
  const safeConversations = Array.isArray(conversations) ? conversations : [];
7593
8182
  const seenConversationIds = /* @__PURE__ */ new Set();
7594
8183
  return safeConversations.map((c) => {
@@ -8192,7 +8781,7 @@ function highlightSearchMatch(text, query) {
8192
8781
  children: part
8193
8782
  },
8194
8783
  `${part}-${index}`
8195
- ) : /* @__PURE__ */ jsxRuntime.jsx(React2__namespace.default.Fragment, { children: part }, `${part}-${index}`)
8784
+ ) : /* @__PURE__ */ jsxRuntime.jsx(React3__namespace.default.Fragment, { children: part }, `${part}-${index}`)
8196
8785
  );
8197
8786
  }
8198
8787
  function getMessagePreview(message) {
@@ -8239,14 +8828,14 @@ var HeaderSearchPopover = ({
8239
8828
  }) => {
8240
8829
  const { scopeClassName, themeStyles } = useChatTheme();
8241
8830
  const { formatMessageTime, formatShortDate } = useChatLocale();
8242
- const inputRef = React2.useRef(null);
8243
- React2.useEffect(() => {
8831
+ const inputRef = React3.useRef(null);
8832
+ React3.useEffect(() => {
8244
8833
  if (!open) return;
8245
8834
  const timer = window.setTimeout(() => inputRef.current?.focus(), 0);
8246
8835
  return () => window.clearTimeout(timer);
8247
8836
  }, [open]);
8248
8837
  const showResults = searchQuery.trim().length > 0;
8249
- const resultCountLabel = React2.useMemo(() => {
8838
+ const resultCountLabel = React3.useMemo(() => {
8250
8839
  if (!searchResults.length) return "0 results";
8251
8840
  return `${searchResults.length}${hasMore ? "+" : ""} result${searchResults.length === 1 ? "" : "s"}`;
8252
8841
  }, [hasMore, searchResults.length]);
@@ -8372,7 +8961,7 @@ var HeaderSearchPopover = ({
8372
8961
  ] });
8373
8962
  };
8374
8963
  var HeaderSearchPopover_default = HeaderSearchPopover;
8375
- var actionBtnClass = "size-8 rounded-lg text-(--chat-muted) transition-colors hover:bg-(--chat-surface) hover:text-(--chat-text)";
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";
8376
8965
  var DefaultHeaderCallActions = ({
8377
8966
  activeChannel,
8378
8967
  conversationId,
@@ -8383,48 +8972,91 @@ var DefaultHeaderCallActions = ({
8383
8972
  className
8384
8973
  }) => {
8385
8974
  const { components, slotClassName, features } = useChatCustomizationOptional();
8975
+ const { getAudioCallGate, getVideoCallGate } = useEffectiveSettingsOptional();
8386
8976
  const PhoneSlot = components.PhoneCallButton;
8387
8977
  const VideoSlot = components.VideoCallButton;
8388
8978
  const hideInGroups = features.hideCallActionsInGroupChats ?? false;
8979
+ const audioGate = getAudioCallGate();
8980
+ const videoGate = getVideoCallGate();
8389
8981
  if (hideInGroups && activeChannel.isGroup) return null;
8390
8982
  if (!showPhoneCall && !showVideoCall) return null;
8983
+ const phoneEnabled = showPhoneCall && audioGate.enabled;
8984
+ const videoEnabled = showVideoCall && videoGate.enabled;
8391
8985
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8392
8986
  showPhoneCall && (PhoneSlot ? /* @__PURE__ */ jsxRuntime.jsx(
8393
- PhoneSlot,
8987
+ AdminPermissionTooltip,
8394
8988
  {
8395
- activeChannel,
8396
- conversationId,
8397
- onPhoneCall,
8398
- className: cn(className, slotClassName("phoneCallButton"))
8989
+ disabled: !audioGate.enabled,
8990
+ message: audioGate.disabledReason,
8991
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(
8992
+ PhoneSlot,
8993
+ {
8994
+ activeChannel,
8995
+ conversationId,
8996
+ onPhoneCall: phoneEnabled ? onPhoneCall : void 0,
8997
+ className: cn(
8998
+ className,
8999
+ slotClassName("phoneCallButton"),
9000
+ !audioGate.enabled && "pointer-events-none opacity-40"
9001
+ )
9002
+ }
9003
+ ) })
8399
9004
  }
8400
9005
  ) : /* @__PURE__ */ jsxRuntime.jsx(
8401
- Button,
9006
+ AdminPermissionTooltip,
8402
9007
  {
8403
- variant: "ghost",
8404
- size: "icon",
8405
- className: cn(actionBtnClass, className, slotClassName("phoneCallButton")),
8406
- "aria-label": "Voice call",
8407
- onClick: onPhoneCall,
8408
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { size: 15 })
9008
+ disabled: !audioGate.enabled,
9009
+ message: audioGate.disabledReason,
9010
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9011
+ Button,
9012
+ {
9013
+ variant: "ghost",
9014
+ size: "icon",
9015
+ className: cn(actionBtnClass, className, slotClassName("phoneCallButton")),
9016
+ "aria-label": "Voice call",
9017
+ disabled: !audioGate.enabled,
9018
+ onClick: phoneEnabled ? onPhoneCall : void 0,
9019
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { size: 15 })
9020
+ }
9021
+ )
8409
9022
  }
8410
9023
  )),
8411
9024
  showVideoCall && (VideoSlot ? /* @__PURE__ */ jsxRuntime.jsx(
8412
- VideoSlot,
9025
+ AdminPermissionTooltip,
8413
9026
  {
8414
- activeChannel,
8415
- conversationId,
8416
- onVideoCall,
8417
- className: cn(className, slotClassName("videoCallButton"))
9027
+ disabled: !videoGate.enabled,
9028
+ message: videoGate.disabledReason,
9029
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(
9030
+ VideoSlot,
9031
+ {
9032
+ activeChannel,
9033
+ conversationId,
9034
+ onVideoCall: videoEnabled ? onVideoCall : void 0,
9035
+ className: cn(
9036
+ className,
9037
+ slotClassName("videoCallButton"),
9038
+ !videoGate.enabled && "pointer-events-none opacity-40"
9039
+ )
9040
+ }
9041
+ ) })
8418
9042
  }
8419
9043
  ) : /* @__PURE__ */ jsxRuntime.jsx(
8420
- Button,
9044
+ AdminPermissionTooltip,
8421
9045
  {
8422
- variant: "ghost",
8423
- size: "icon",
8424
- className: cn(actionBtnClass, className, slotClassName("videoCallButton")),
8425
- "aria-label": "Video call",
8426
- onClick: onVideoCall,
8427
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 15 })
9046
+ disabled: !videoGate.enabled,
9047
+ message: videoGate.disabledReason,
9048
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9049
+ Button,
9050
+ {
9051
+ variant: "ghost",
9052
+ size: "icon",
9053
+ className: cn(actionBtnClass, className, slotClassName("videoCallButton")),
9054
+ "aria-label": "Video call",
9055
+ disabled: !videoGate.enabled,
9056
+ onClick: videoEnabled ? onVideoCall : void 0,
9057
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 15 })
9058
+ }
9059
+ )
8428
9060
  }
8429
9061
  ))
8430
9062
  ] });
@@ -8446,19 +9078,38 @@ function OptionsMenuItem({
8446
9078
  icon: Icon,
8447
9079
  label,
8448
9080
  onSelect,
8449
- destructive = false
9081
+ destructive = false,
9082
+ disabled = false,
9083
+ disabledReason
8450
9084
  }) {
8451
- return /* @__PURE__ */ jsxRuntime.jsxs(
9085
+ const item = /* @__PURE__ */ jsxRuntime.jsxs(
8452
9086
  DropdownMenuItem,
8453
9087
  {
8454
- className: destructive ? chatOptionsMenuItemDestructiveClass : chatOptionsMenuItemClass,
8455
- onSelect,
9088
+ className: cn(
9089
+ destructive ? chatOptionsMenuItemDestructiveClass : chatOptionsMenuItemClass,
9090
+ disabled && "pointer-events-none opacity-40"
9091
+ ),
9092
+ disabled,
9093
+ onSelect: (event) => {
9094
+ if (disabled) {
9095
+ event.preventDefault();
9096
+ return;
9097
+ }
9098
+ onSelect?.();
9099
+ },
8456
9100
  children: [
8457
9101
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { strokeWidth: 1.75 }),
8458
9102
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: label })
8459
9103
  ]
8460
9104
  }
8461
9105
  );
9106
+ if (!disabled || !disabledReason) {
9107
+ return item;
9108
+ }
9109
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
9110
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: item }) }),
9111
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "left", className: "max-w-[220px] text-xs", children: disabledReason })
9112
+ ] });
8462
9113
  }
8463
9114
  var HeaderRightSide = ({
8464
9115
  activeChannel,
@@ -8493,6 +9144,8 @@ var HeaderRightSide = ({
8493
9144
  const { scopeClassName, themeStyles, showColorModeToggle } = useChatTheme();
8494
9145
  const { showDateTimeSettings } = useChatLocale();
8495
9146
  const features = useChatFeaturesOptional();
9147
+ const { getBlockUsersGate } = useEffectiveSettingsOptional();
9148
+ const blockUsersGate = getBlockUsersGate();
8496
9149
  const showPhoneCall = features.showPhoneCall ?? defaultChatFeatures.showPhoneCall;
8497
9150
  const showVideoCall = features.showVideoCall ?? defaultChatFeatures.showVideoCall;
8498
9151
  const handlePhoneCall = () => {
@@ -8641,7 +9294,9 @@ var HeaderRightSide = ({
8641
9294
  icon: lucideReact.Ban,
8642
9295
  label: isBlocked ? "Unblock User" : "Block User",
8643
9296
  onSelect: onToggleBlock,
8644
- destructive: true
9297
+ destructive: true,
9298
+ disabled: !blockUsersGate.enabled,
9299
+ disabledReason: blockUsersGate.disabledReason
8645
9300
  }
8646
9301
  )
8647
9302
  ]
@@ -9012,13 +9667,13 @@ function AudioAttachmentPlayer({
9012
9667
  showDownload = true,
9013
9668
  downloadName
9014
9669
  }) {
9015
- const audioRef = React2.useRef(null);
9016
- const [isPlaying, setIsPlaying] = React2.useState(false);
9017
- const [currentTime, setCurrentTime] = React2.useState(0);
9018
- const [duration, setDuration] = React2.useState(durationProp ?? 0);
9670
+ const audioRef = React3.useRef(null);
9671
+ const [isPlaying, setIsPlaying] = React3.useState(false);
9672
+ const [currentTime, setCurrentTime] = React3.useState(0);
9673
+ const [duration, setDuration] = React3.useState(durationProp ?? 0);
9019
9674
  const resolvedDuration = duration > 0 ? duration : 0;
9020
9675
  const progressPercent = resolvedDuration > 0 ? Math.min(100, currentTime / resolvedDuration * 100) : 0;
9021
- const togglePlay = React2.useCallback(async () => {
9676
+ const togglePlay = React3.useCallback(async () => {
9022
9677
  const audio = audioRef.current;
9023
9678
  if (!audio || !url) return;
9024
9679
  if (audio.paused) {
@@ -9040,7 +9695,7 @@ function AudioAttachmentPlayer({
9040
9695
  audio.currentTime = next;
9041
9696
  setCurrentTime(next);
9042
9697
  };
9043
- React2.useEffect(() => {
9698
+ React3.useEffect(() => {
9044
9699
  const audio = audioRef.current;
9045
9700
  if (!audio) return;
9046
9701
  const onTimeUpdate = () => setCurrentTime(audio.currentTime);
@@ -9068,7 +9723,7 @@ function AudioAttachmentPlayer({
9068
9723
  audio.removeEventListener("play", onPlay);
9069
9724
  };
9070
9725
  }, [url]);
9071
- React2.useEffect(() => {
9726
+ React3.useEffect(() => {
9072
9727
  if (durationProp && durationProp > 0) {
9073
9728
  setDuration(durationProp);
9074
9729
  }
@@ -9190,12 +9845,12 @@ function VideoAttachmentPlayer({
9190
9845
  downloadName,
9191
9846
  onOpenPreview
9192
9847
  }) {
9193
- const videoRef = React2.useRef(null);
9194
- const [isPlaying, setIsPlaying] = React2.useState(false);
9195
- const [duration, setDuration] = React2.useState(durationProp ?? 0);
9848
+ const videoRef = React3.useRef(null);
9849
+ const [isPlaying, setIsPlaying] = React3.useState(false);
9850
+ const [duration, setDuration] = React3.useState(durationProp ?? 0);
9196
9851
  const onThemeBubble = inBubble && isSender;
9197
9852
  const resolvedDuration = duration > 0 ? duration : 0;
9198
- const togglePlay = React2.useCallback(async () => {
9853
+ const togglePlay = React3.useCallback(async () => {
9199
9854
  const video = videoRef.current;
9200
9855
  if (!video || !url) return;
9201
9856
  if (video.paused) {
@@ -9210,7 +9865,7 @@ function VideoAttachmentPlayer({
9210
9865
  setIsPlaying(false);
9211
9866
  }
9212
9867
  }, [url]);
9213
- React2.useEffect(() => {
9868
+ React3.useEffect(() => {
9214
9869
  if (durationProp && durationProp > 0) {
9215
9870
  setDuration(durationProp);
9216
9871
  }
@@ -9488,10 +10143,10 @@ async function fetchAuthenticatedBlob(url) {
9488
10143
  return response.blob();
9489
10144
  }
9490
10145
  function PdfPreviewPanel({ url, name }) {
9491
- const [blobUrl, setBlobUrl] = React2.useState(null);
9492
- const [fallbackUrl, setFallbackUrl] = React2.useState(null);
9493
- const [loading, setLoading] = React2.useState(true);
9494
- React2.useEffect(() => {
10146
+ const [blobUrl, setBlobUrl] = React3.useState(null);
10147
+ const [fallbackUrl, setFallbackUrl] = React3.useState(null);
10148
+ const [loading, setLoading] = React3.useState(true);
10149
+ React3.useEffect(() => {
9495
10150
  let active = true;
9496
10151
  let objectUrl = null;
9497
10152
  const loadPdf = async () => {
@@ -9995,60 +10650,60 @@ function AttachmentImageLightbox({
9995
10650
  onSaveCaption,
9996
10651
  initialCaptionEditing = false
9997
10652
  }) {
9998
- const [index, setIndex] = React2.useState(initialIndex);
9999
- const [zoom, setZoom] = React2.useState(1);
10000
- const [pan, setPan] = React2.useState({ x: 0, y: 0 });
10001
- const [imageVersion, setImageVersion] = React2.useState(0);
10002
- const [localCaptions, setLocalCaptions] = React2.useState({});
10003
- const [captionDraft, setCaptionDraft] = React2.useState("");
10004
- const [isCaptionEditing, setIsCaptionEditing] = React2.useState(false);
10005
- const thumbRefs = React2.useRef([]);
10006
- const captionInputRef = React2.useRef(null);
10007
- const touchStartX = React2.useRef(null);
10008
- const isPanning = React2.useRef(false);
10009
- const panStart = React2.useRef({ x: 0, y: 0, originX: 0, originY: 0 });
10653
+ const [index, setIndex] = React3.useState(initialIndex);
10654
+ const [zoom, setZoom] = React3.useState(1);
10655
+ const [pan, setPan] = React3.useState({ x: 0, y: 0 });
10656
+ const [imageVersion, setImageVersion] = React3.useState(0);
10657
+ const [localCaptions, setLocalCaptions] = React3.useState({});
10658
+ const [captionDraft, setCaptionDraft] = React3.useState("");
10659
+ const [isCaptionEditing, setIsCaptionEditing] = React3.useState(false);
10660
+ const thumbRefs = React3.useRef([]);
10661
+ const captionInputRef = React3.useRef(null);
10662
+ const touchStartX = React3.useRef(null);
10663
+ const isPanning = React3.useRef(false);
10664
+ const panStart = React3.useRef({ x: 0, y: 0, originX: 0, originY: 0 });
10010
10665
  const canEdit = canEditCaption && !!onSaveCaption;
10011
- React2.useEffect(() => {
10666
+ React3.useEffect(() => {
10012
10667
  setIndex(initialIndex);
10013
10668
  }, [initialIndex]);
10014
- const resetView = React2.useCallback(() => {
10669
+ const resetView = React3.useCallback(() => {
10015
10670
  setZoom(1);
10016
10671
  setPan({ x: 0, y: 0 });
10017
10672
  }, []);
10018
- const reloadImage = React2.useCallback(() => {
10673
+ const reloadImage = React3.useCallback(() => {
10019
10674
  resetView();
10020
10675
  setImageVersion((version) => version + 1);
10021
10676
  }, [resetView]);
10022
- React2.useEffect(() => {
10677
+ React3.useEffect(() => {
10023
10678
  resetView();
10024
10679
  setIsCaptionEditing(false);
10025
10680
  }, [index, resetView]);
10026
- React2.useEffect(() => {
10681
+ React3.useEffect(() => {
10027
10682
  if (!initialCaptionEditing || !canEdit) return;
10028
10683
  setIsCaptionEditing(true);
10029
10684
  }, [initialCaptionEditing, canEdit]);
10030
- React2.useEffect(() => {
10685
+ React3.useEffect(() => {
10031
10686
  if (!isCaptionEditing) return;
10032
10687
  const id = window.setTimeout(() => captionInputRef.current?.focus(), 40);
10033
10688
  return () => window.clearTimeout(id);
10034
10689
  }, [isCaptionEditing]);
10035
- const goPrev = React2.useCallback(() => {
10690
+ const goPrev = React3.useCallback(() => {
10036
10691
  setIndex((current2) => current2 > 0 ? current2 - 1 : images.length - 1);
10037
10692
  }, [images.length]);
10038
- const goNext = React2.useCallback(() => {
10693
+ const goNext = React3.useCallback(() => {
10039
10694
  setIndex((current2) => current2 < images.length - 1 ? current2 + 1 : 0);
10040
10695
  }, [images.length]);
10041
- const zoomIn = React2.useCallback(() => {
10696
+ const zoomIn = React3.useCallback(() => {
10042
10697
  setZoom((current2) => clampZoom(Number((current2 + ZOOM_STEP).toFixed(2))));
10043
10698
  }, []);
10044
- const zoomOut = React2.useCallback(() => {
10699
+ const zoomOut = React3.useCallback(() => {
10045
10700
  setZoom((current2) => {
10046
10701
  const next = clampZoom(Number((current2 - ZOOM_STEP).toFixed(2)));
10047
10702
  if (next <= MIN_ZOOM) setPan({ x: 0, y: 0 });
10048
10703
  return next;
10049
10704
  });
10050
10705
  }, []);
10051
- React2.useEffect(() => {
10706
+ React3.useEffect(() => {
10052
10707
  const onKeyDown = (event) => {
10053
10708
  const tag = event.target?.tagName;
10054
10709
  const isTyping = tag === "INPUT" || tag === "TEXTAREA";
@@ -10062,14 +10717,14 @@ function AttachmentImageLightbox({
10062
10717
  window.addEventListener("keydown", onKeyDown);
10063
10718
  return () => window.removeEventListener("keydown", onKeyDown);
10064
10719
  }, [goPrev, goNext, zoomIn, zoomOut, resetView]);
10065
- React2.useEffect(() => {
10720
+ React3.useEffect(() => {
10066
10721
  thumbRefs.current[index]?.scrollIntoView({
10067
10722
  behavior: "smooth",
10068
10723
  block: "nearest",
10069
10724
  inline: "center"
10070
10725
  });
10071
10726
  }, [index]);
10072
- React2.useEffect(() => {
10727
+ React3.useEffect(() => {
10073
10728
  const attachment = images[index];
10074
10729
  if (!attachment) return;
10075
10730
  setCaptionDraft(getCaptionForAttachment(attachment, localCaptions));
@@ -10540,7 +11195,7 @@ function MessageAttachmentGrid({
10540
11195
  onToggleAttachmentSelect,
10541
11196
  onAttachmentHover
10542
11197
  }) {
10543
- const [lightboxIndex, setLightboxIndex] = React2.useState(null);
11198
+ const [lightboxIndex, setLightboxIndex] = React3.useState(null);
10544
11199
  if (!images.length) return null;
10545
11200
  const visible = images.slice(0, MAX_VISIBLE);
10546
11201
  const overflow = images.length - MAX_VISIBLE;
@@ -10760,7 +11415,7 @@ function MessageAttachmentsImageSection({
10760
11415
  hoveredAttachmentId,
10761
11416
  onAttachmentHover
10762
11417
  }) {
10763
- const [lightboxIndex, setLightboxIndex] = React2.useState(null);
11418
+ const [lightboxIndex, setLightboxIndex] = React3.useState(null);
10764
11419
  if (images.length === 0) return null;
10765
11420
  const renderMediaMetaOverlay = () => /* @__PURE__ */ jsxRuntime.jsx(
10766
11421
  MediaMetaOverlay,
@@ -11235,7 +11890,7 @@ function MessageAttachmentsView({
11235
11890
  senderName,
11236
11891
  senderId
11237
11892
  }) {
11238
- const [documentPreview, setDocumentPreview] = React2.useState(null);
11893
+ const [documentPreview, setDocumentPreview] = React3.useState(null);
11239
11894
  const visibleAttachments = getVisibleAttachments(attachments);
11240
11895
  if (isDeletedForEveryone || visibleAttachments.length === 0) return null;
11241
11896
  const messageCaption = caption?.trim() || "";
@@ -11620,8 +12275,8 @@ var SavedMessagesDrawer = ({
11620
12275
  const conversationMessages = exports.useChatStore(
11621
12276
  (s) => conversationId ? s.messagesByConversation[conversationId] ?? EMPTY_CONVERSATION_MESSAGES : EMPTY_CONVERSATION_MESSAGES
11622
12277
  );
11623
- const [searchQuery, setSearchQuery] = React2.useState("");
11624
- const parsedEntries = React2.useMemo(() => {
12278
+ const [searchQuery, setSearchQuery] = React3.useState("");
12279
+ const parsedEntries = React3.useMemo(() => {
11625
12280
  return messages.map((msg) => ({
11626
12281
  msg,
11627
12282
  item: parseSavedMessage(msg, {
@@ -11642,7 +12297,7 @@ var SavedMessagesDrawer = ({
11642
12297
  variant,
11643
12298
  prefs
11644
12299
  ]);
11645
- const groupedEntries = React2.useMemo(() => {
12300
+ const groupedEntries = React3.useMemo(() => {
11646
12301
  const query = searchQuery.trim().toLowerCase();
11647
12302
  const filtered = parsedEntries.filter(({ item, msg }) => {
11648
12303
  if (!query) return true;
@@ -11915,15 +12570,15 @@ function SectionScrollArea({
11915
12570
  onLoadMore,
11916
12571
  dependencyKey
11917
12572
  }) {
11918
- const scrollRef = React2.useRef(null);
11919
- const [showArrow, setShowArrow] = React2.useState(false);
11920
- const updateArrow = React2.useCallback(() => {
12573
+ const scrollRef = React3.useRef(null);
12574
+ const [showArrow, setShowArrow] = React3.useState(false);
12575
+ const updateArrow = React3.useCallback(() => {
11921
12576
  const el = scrollRef.current;
11922
12577
  if (!el) return;
11923
12578
  const canScrollDown = el.scrollHeight - el.scrollTop - el.clientHeight > 6;
11924
12579
  setShowArrow(canScrollDown || hasMoreToLoad);
11925
12580
  }, [hasMoreToLoad]);
11926
- React2.useEffect(() => {
12581
+ React3.useEffect(() => {
11927
12582
  updateArrow();
11928
12583
  const el = scrollRef.current;
11929
12584
  if (!el) return;
@@ -12099,31 +12754,31 @@ var AttachmentsDrawer = ({ isOpen, onOpenChange, conversationId }) => {
12099
12754
  const messagesByConversation = exports.useChatStore((s) => s.messagesByConversation);
12100
12755
  const messagesMetadata = exports.useChatStore((s) => s.messagesMetadata);
12101
12756
  const fetchMessages2 = exports.useChatStore((s) => s.fetchMessages);
12102
- const [sectionVisibleCount, setSectionVisibleCount] = React2.useState({});
12103
- const [documentPreview, setDocumentPreview] = React2.useState(null);
12104
- const [imageLightboxIndex, setImageLightboxIndex] = React2.useState(null);
12757
+ const [sectionVisibleCount, setSectionVisibleCount] = React3.useState({});
12758
+ const [documentPreview, setDocumentPreview] = React3.useState(null);
12759
+ const [imageLightboxIndex, setImageLightboxIndex] = React3.useState(null);
12105
12760
  const hasLoadedMessages = conversationId ? Boolean(messagesMetadata[conversationId]?.isFirstPage) : false;
12106
12761
  const isLoading = isOpen && !!conversationId && !hasLoadedMessages;
12107
- React2.useEffect(() => {
12762
+ React3.useEffect(() => {
12108
12763
  if (!isOpen) {
12109
12764
  setSectionVisibleCount({});
12110
12765
  setDocumentPreview(null);
12111
12766
  setImageLightboxIndex(null);
12112
12767
  }
12113
12768
  }, [isOpen]);
12114
- React2.useEffect(() => {
12769
+ React3.useEffect(() => {
12115
12770
  setSectionVisibleCount({});
12116
12771
  }, [conversationId]);
12117
- React2.useEffect(() => {
12772
+ React3.useEffect(() => {
12118
12773
  if (!isOpen || !conversationId || hasLoadedMessages) return;
12119
12774
  fetchMessages2(conversationId);
12120
12775
  }, [isOpen, conversationId, hasLoadedMessages, fetchMessages2]);
12121
- const attachments = React2.useMemo(() => {
12776
+ const attachments = React3.useMemo(() => {
12122
12777
  if (!conversationId) return [];
12123
12778
  const messages = messagesByConversation[conversationId] || [];
12124
12779
  return collectConversationAttachments(messages);
12125
12780
  }, [conversationId, messagesByConversation]);
12126
- const imageLightboxItems = React2.useMemo(
12781
+ const imageLightboxItems = React3.useMemo(
12127
12782
  () => attachments.filter((item) => item.type === "image").map((item) => ({
12128
12783
  id: item.id,
12129
12784
  url: item.url,
@@ -12132,14 +12787,14 @@ var AttachmentsDrawer = ({ isOpen, onOpenChange, conversationId }) => {
12132
12787
  })),
12133
12788
  [attachments]
12134
12789
  );
12135
- const openImagePreview = React2.useCallback(
12790
+ const openImagePreview = React3.useCallback(
12136
12791
  (item) => {
12137
12792
  const index = imageLightboxItems.findIndex((entry) => entry.id === item.id);
12138
12793
  if (index >= 0) setImageLightboxIndex(index);
12139
12794
  },
12140
12795
  [imageLightboxItems]
12141
12796
  );
12142
- const grouped = React2.useMemo(() => {
12797
+ const grouped = React3.useMemo(() => {
12143
12798
  const map = /* @__PURE__ */ new Map();
12144
12799
  for (const item of attachments) {
12145
12800
  const bucket = map.get(item.type) ?? [];
@@ -12239,13 +12894,13 @@ var useGroupInfo = (conversationId) => {
12239
12894
  const conversationInfos = exports.useChatStore((s) => s.conversationInfos);
12240
12895
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
12241
12896
  const info = conversationId ? conversationInfos[conversationId] : null;
12242
- const isAdmin = React2.useMemo(() => {
12897
+ const isAdmin = React3.useMemo(() => {
12243
12898
  if (!info || !loggedUserDetails) return false;
12244
12899
  return info.groupAdmins?.some(
12245
12900
  (admin) => (typeof admin === "string" ? admin : admin._id) === String(loggedUserDetails._id)
12246
12901
  ) || false;
12247
12902
  }, [info, loggedUserDetails]);
12248
- const isOwner = React2.useMemo(() => {
12903
+ const isOwner = React3.useMemo(() => {
12249
12904
  if (!info || !loggedUserDetails) return false;
12250
12905
  let ownerId = null;
12251
12906
  if (info.owner) {
@@ -12254,7 +12909,7 @@ var useGroupInfo = (conversationId) => {
12254
12909
  return String(ownerId) === String(loggedUserDetails._id);
12255
12910
  }, [info, loggedUserDetails]);
12256
12911
  const permissions = info?.groupPermissions || null;
12257
- const userPermissions = React2.useMemo(() => {
12912
+ const userPermissions = React3.useMemo(() => {
12258
12913
  return {
12259
12914
  canSendMessage: isOwner || isAdmin || !permissions?.onlyAdminCanSendMessage,
12260
12915
  canEditInfo: isOwner || isAdmin || !permissions?.onlyAdminCanEditInfo,
@@ -12303,21 +12958,21 @@ function useChannelDetailsDrawer({
12303
12958
  const conversationId = conversationInfo?._id || activeChannel?.conversationId || storeActiveConversationId || null;
12304
12959
  const { userPermissions } = useGroupInfo(conversationId);
12305
12960
  const { formatLongDate } = useChatLocale();
12306
- const storeConversation = React2.useMemo(
12961
+ const storeConversation = React3.useMemo(
12307
12962
  () => conversations.find((c) => conversationId && String(c._id) === String(conversationId)),
12308
12963
  [conversations, conversationId]
12309
12964
  );
12310
- const isGroupChannel = React2.useMemo(() => {
12965
+ const isGroupChannel = React3.useMemo(() => {
12311
12966
  if (storeConversation) {
12312
12967
  return storeConversation.conversationType === "group" || !!storeConversation.isGroup;
12313
12968
  }
12314
12969
  return !!activeChannel?.isGroup;
12315
12970
  }, [storeConversation, activeChannel?.isGroup]);
12316
- const participantsSource = React2.useMemo(
12971
+ const participantsSource = React3.useMemo(
12317
12972
  () => conversationInfo?.participants || storeConversation?.participants || [],
12318
12973
  [conversationInfo?.participants, storeConversation?.participants]
12319
12974
  );
12320
- const resolvedOtherParticipant = React2.useMemo(() => {
12975
+ const resolvedOtherParticipant = React3.useMemo(() => {
12321
12976
  if (isGroupChannel) return { id: "", user: null };
12322
12977
  return resolveOtherParticipant(
12323
12978
  {
@@ -12336,7 +12991,7 @@ function useChannelDetailsDrawer({
12336
12991
  allUsers
12337
12992
  ]);
12338
12993
  const otherUser = resolvedOtherParticipant.user;
12339
- const otherUserEmail = React2.useMemo(() => {
12994
+ const otherUserEmail = React3.useMemo(() => {
12340
12995
  if (isGroupChannel) return null;
12341
12996
  const fromResolved = resolvedOtherParticipant.user?.email?.trim();
12342
12997
  if (fromResolved) return fromResolved;
@@ -12344,7 +12999,7 @@ function useChannelDetailsDrawer({
12344
12999
  if (!otherId) return null;
12345
13000
  return allUsers.find((user) => String(user._id) === String(otherId))?.email?.trim() || activeChannel?.email?.trim() || null;
12346
13001
  }, [isGroupChannel, resolvedOtherParticipant.id, resolvedOtherParticipant.user?.email, allUsers, activeChannel?.email]);
12347
- const resolvedCreatedAt = React2.useMemo(
13002
+ const resolvedCreatedAt = React3.useMemo(
12348
13003
  () => pickDate(
12349
13004
  conversationInfo?.createdAt,
12350
13005
  conversationInfo?.updatedAt,
@@ -12357,9 +13012,9 @@ function useChannelDetailsDrawer({
12357
13012
  ),
12358
13013
  [conversationInfo, storeConversation, otherUser, conversationId, messagesByConversation]
12359
13014
  );
12360
- const [detailsLoading, setDetailsLoading] = React2.useState(false);
12361
- const fetchStartedRef = React2.useRef(null);
12362
- React2.useEffect(() => {
13015
+ const [detailsLoading, setDetailsLoading] = React3.useState(false);
13016
+ const fetchStartedRef = React3.useRef(null);
13017
+ React3.useEffect(() => {
12363
13018
  if (!isOpen) {
12364
13019
  fetchStartedRef.current = null;
12365
13020
  setDetailsLoading(false);
@@ -12401,16 +13056,16 @@ function useChannelDetailsDrawer({
12401
13056
  };
12402
13057
  }, [isOpen, conversationId, isGroupChannel, fetchAllUsers]);
12403
13058
  const canEditInfo = activeChannel ? userPermissions.canEditInfo : false;
12404
- const [isEditing, setIsEditing] = React2.useState(false);
12405
- const [name, setName] = React2.useState("");
12406
- const [description, setDescription] = React2.useState("");
12407
- const [image, setImage] = React2.useState(null);
12408
- const [uploadedImageUrl, setUploadedImageUrl] = React2.useState(null);
12409
- const [imageUploadStatus, setImageUploadStatus] = React2.useState("idle");
12410
- const [imageUploadError, setImageUploadError] = React2.useState(null);
12411
- const [isSaving, setIsSaving] = React2.useState(false);
12412
- const fileInputRef = React2.useRef(null);
12413
- React2.useEffect(() => {
13059
+ const [isEditing, setIsEditing] = React3.useState(false);
13060
+ const [name, setName] = React3.useState("");
13061
+ const [description, setDescription] = React3.useState("");
13062
+ const [image, setImage] = React3.useState(null);
13063
+ const [uploadedImageUrl, setUploadedImageUrl] = React3.useState(null);
13064
+ const [imageUploadStatus, setImageUploadStatus] = React3.useState("idle");
13065
+ const [imageUploadError, setImageUploadError] = React3.useState(null);
13066
+ const [isSaving, setIsSaving] = React3.useState(false);
13067
+ const fileInputRef = React3.useRef(null);
13068
+ React3.useEffect(() => {
12414
13069
  if (conversationInfo) {
12415
13070
  setName(conversationInfo.groupName || activeChannel?.name || "");
12416
13071
  setDescription(conversationInfo.groupDescription || "");
@@ -12681,17 +13336,24 @@ var ChannelDetailsDrawer = (props) => {
12681
13336
  };
12682
13337
  var ChannelDetailsDrawer_default = ChannelDetailsDrawer;
12683
13338
  init_useStore();
13339
+ init_settings_types();
12684
13340
  var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo }) => {
12685
13341
  const updateGroupPermissions = exports.useChatStore((s) => s.updateGroupPermissions);
12686
- const [permissions, setPermissions] = React2.useState(null);
12687
- const [isSaving, setIsSaving] = React2.useState(false);
12688
- React2.useEffect(() => {
13342
+ const { settings, isGroupPermissionLocked } = useEffectiveSettingsOptional();
13343
+ const [permissions, setPermissions] = React3.useState(null);
13344
+ const [isSaving, setIsSaving] = React3.useState(false);
13345
+ React3.useEffect(() => {
12689
13346
  if (conversationInfo?.groupPermissions) {
12690
13347
  setPermissions(conversationInfo.groupPermissions);
13348
+ return;
13349
+ }
13350
+ if (isOpen) {
13351
+ setPermissions({ ...settings.groupPolicy.defaults });
12691
13352
  }
12692
- }, [conversationInfo]);
13353
+ }, [conversationInfo, isOpen, settings.groupPolicy.defaults]);
12693
13354
  if (!conversationId || !permissions) return null;
12694
13355
  const handleToggle = (key) => {
13356
+ if (isGroupPermissionLocked(key)) return;
12695
13357
  setPermissions((prev) => prev ? { ...prev, [key]: !prev[key] } : null);
12696
13358
  };
12697
13359
  const handleSave = async () => {
@@ -12723,31 +13385,43 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
12723
13385
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(chatSheetBodyClass, "flex flex-col px-2.5 py-2"), children: [
12724
13386
  /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mb-2.5 flex-1", children: [
12725
13387
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1 px-0.5 text-[10px] font-semibold uppercase tracking-wide text-(--chat-muted)", children: "Permissions" }),
12726
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-(--chat-border) px-3", children: permissionItems.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
12727
- "div",
12728
- {
12729
- className: cn(
12730
- "flex items-start justify-between gap-3 py-2.5",
12731
- index < permissionItems.length - 1 && "border-b border-(--chat-border)"
12732
- ),
12733
- children: [
12734
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-0.5", children: [
12735
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] font-semibold leading-snug text-(--chat-text)", children: item.label }),
12736
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] leading-relaxed text-(--chat-muted)", children: item.desc })
12737
- ] }),
12738
- /* @__PURE__ */ jsxRuntime.jsx(
12739
- Switch,
12740
- {
12741
- size: "sm",
12742
- className: "mt-0.5 shrink-0 data-[state=checked]:bg-(--chat-theme) data-[state=unchecked]:bg-(--chat-border)",
12743
- checked: "inverted" in item && item.inverted ? !permissions[item.key] : permissions[item.key],
12744
- onCheckedChange: () => handleToggle(item.key)
12745
- }
12746
- )
12747
- ]
12748
- },
12749
- item.key
12750
- )) })
13388
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-(--chat-border) px-3", children: permissionItems.map((item, index) => {
13389
+ const locked = isGroupPermissionLocked(item.key);
13390
+ return /* @__PURE__ */ jsxRuntime.jsxs(
13391
+ "div",
13392
+ {
13393
+ className: cn(
13394
+ "flex items-start justify-between gap-3 py-2.5",
13395
+ index < permissionItems.length - 1 && "border-b border-(--chat-border)"
13396
+ ),
13397
+ children: [
13398
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-0.5", children: [
13399
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] font-semibold leading-snug text-(--chat-text)", children: item.label }),
13400
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] leading-relaxed text-(--chat-muted)", children: item.desc }),
13401
+ locked ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-medium text-(--chat-theme)", children: "Locked by administrator" }) : null
13402
+ ] }),
13403
+ /* @__PURE__ */ jsxRuntime.jsx(
13404
+ AdminPermissionTooltip,
13405
+ {
13406
+ disabled: locked,
13407
+ message: exports.ADMIN_PERMISSION_DENIED_MESSAGE,
13408
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13409
+ Switch,
13410
+ {
13411
+ size: "sm",
13412
+ className: "mt-0.5 shrink-0 data-[state=checked]:bg-(--chat-theme) data-[state=unchecked]:bg-(--chat-border)",
13413
+ disabled: locked,
13414
+ checked: item.inverted ? !permissions[item.key] : permissions[item.key],
13415
+ onCheckedChange: () => handleToggle(item.key)
13416
+ }
13417
+ )
13418
+ }
13419
+ )
13420
+ ]
13421
+ },
13422
+ item.key
13423
+ );
13424
+ }) })
12751
13425
  ] }),
12752
13426
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky bottom-0 border-t border-(--chat-border) bg-(--chat-surface) px-0.5 pt-2.5 pb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
12753
13427
  Button,
@@ -12773,51 +13447,61 @@ var PermissionDrawer_default = PermissionDrawer;
12773
13447
  // src/chat/header/admins-drawer/useAdminsDrawer.ts
12774
13448
  init_useStore();
12775
13449
  function useAdminsDrawer({ conversationId, conversationInfo }) {
12776
- const [searchQuery, setSearchQuery] = React2.useState("");
12777
- const [isManaging, setIsManaging] = React2.useState(null);
13450
+ const [searchQuery, setSearchQuery] = React3.useState("");
13451
+ const [isManaging, setIsManaging] = React3.useState(null);
12778
13452
  const manageGroupAdmins = exports.useChatStore((s) => s.manageGroupAdmins);
12779
13453
  const { isOwner } = useGroupInfo(conversationId);
12780
- const participants = React2.useMemo(
13454
+ const { settings } = useEffectiveSettingsOptional();
13455
+ const maxAdmins = settings.maxAdminsPerGroup;
13456
+ const participants = React3.useMemo(
12781
13457
  () => conversationInfo?.participants ?? [],
12782
13458
  [conversationInfo?.participants]
12783
13459
  );
12784
- const admins = React2.useMemo(
13460
+ const admins = React3.useMemo(
12785
13461
  () => conversationInfo?.groupAdmins ?? [],
12786
13462
  [conversationInfo?.groupAdmins]
12787
13463
  );
12788
- const ownerId = React2.useMemo(() => {
13464
+ const ownerId = React3.useMemo(() => {
12789
13465
  if (!conversationInfo?.owner) return null;
12790
13466
  return typeof conversationInfo.owner === "object" && conversationInfo.owner !== null ? conversationInfo.owner._id || conversationInfo.owner.id : conversationInfo.owner;
12791
13467
  }, [conversationInfo?.owner]);
12792
- const adminIds = React2.useMemo(
13468
+ const adminIds = React3.useMemo(
12793
13469
  () => new Set(
12794
13470
  admins.map((a) => typeof a === "string" ? a : a._id).filter((id) => Boolean(id))
12795
13471
  ),
12796
13472
  [admins]
12797
13473
  );
12798
- const isAdmin = React2.useCallback((userId) => adminIds.has(userId), [adminIds]);
12799
- const filteredParticipants = React2.useMemo(() => {
13474
+ const isAdmin = React3.useCallback((userId) => adminIds.has(userId), [adminIds]);
13475
+ const filteredParticipants = React3.useMemo(() => {
12800
13476
  if (!searchQuery.trim()) return participants;
12801
13477
  return participants.filter(
12802
13478
  (p) => p.name?.toLowerCase().includes(searchQuery.toLowerCase()) || p.email?.toLowerCase().includes(searchQuery.toLowerCase())
12803
13479
  );
12804
13480
  }, [participants, searchQuery]);
12805
- const adminParticipants = React2.useMemo(
13481
+ const adminParticipants = React3.useMemo(
12806
13482
  () => filteredParticipants.filter((p) => {
12807
13483
  const id = typeof p === "string" ? p : p._id;
12808
13484
  return id && (adminIds.has(id) || id === ownerId);
12809
13485
  }),
12810
13486
  [filteredParticipants, adminIds, ownerId]
12811
13487
  );
12812
- const promotableParticipants = React2.useMemo(
13488
+ const promotableParticipants = React3.useMemo(
12813
13489
  () => filteredParticipants.filter((p) => {
12814
13490
  const id = typeof p === "string" ? p : p._id;
12815
13491
  return id && id !== ownerId && !adminIds.has(id);
12816
13492
  }),
12817
13493
  [filteredParticipants, adminIds, ownerId]
12818
13494
  );
13495
+ const adminCount = admins.length + (ownerId ? 1 : 0);
13496
+ const atAdminLimit = adminCount >= maxAdmins;
12819
13497
  const handleManageAdmin = async (userId, type) => {
12820
13498
  if (!conversationId) return;
13499
+ if (type === "add" && adminCount >= maxAdmins) {
13500
+ exports.useChatStore.getState().handleAppError(
13501
+ `You can have up to ${maxAdmins} admins in this group.`
13502
+ );
13503
+ return;
13504
+ }
12821
13505
  setIsManaging(userId);
12822
13506
  try {
12823
13507
  await manageGroupAdmins({
@@ -12829,7 +13513,6 @@ function useAdminsDrawer({ conversationId, conversationInfo }) {
12829
13513
  setIsManaging(null);
12830
13514
  }
12831
13515
  };
12832
- const adminCount = admins.length + (ownerId ? 1 : 0);
12833
13516
  return {
12834
13517
  searchQuery,
12835
13518
  setSearchQuery,
@@ -12841,6 +13524,8 @@ function useAdminsDrawer({ conversationId, conversationInfo }) {
12841
13524
  promotableParticipants,
12842
13525
  handleManageAdmin,
12843
13526
  adminCount,
13527
+ maxAdmins,
13528
+ atAdminLimit,
12844
13529
  hasRequiredData: Boolean(conversationId && conversationInfo)
12845
13530
  };
12846
13531
  }
@@ -12853,6 +13538,8 @@ function AdminParticipantRow({
12853
13538
  isAdmin,
12854
13539
  showPromote,
12855
13540
  showDemote,
13541
+ promoteDisabled = false,
13542
+ promoteDisabledReason,
12856
13543
  isOwnerViewer,
12857
13544
  isManaging,
12858
13545
  onManageAdmin
@@ -12893,14 +13580,24 @@ function AdminParticipantRow({
12893
13580
  }
12894
13581
  ),
12895
13582
  showPromote && isOwnerViewer && /* @__PURE__ */ jsxRuntime.jsx(
12896
- Button,
13583
+ AdminPermissionTooltip,
12897
13584
  {
12898
- variant: "ghost",
12899
- size: "sm",
12900
- className: "size-7 rounded-md p-0 text-(--chat-muted) hover:bg-(--chat-theme-10) hover:text-(--chat-theme)",
12901
- onClick: () => onManageAdmin(memberId, "add"),
12902
- disabled: isManaging === memberId,
12903
- children: isManaging === memberId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { size: 13 })
13585
+ disabled: promoteDisabled,
13586
+ message: promoteDisabledReason,
13587
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13588
+ Button,
13589
+ {
13590
+ variant: "ghost",
13591
+ size: "sm",
13592
+ className: "size-7 rounded-md p-0 text-(--chat-muted) hover:bg-(--chat-theme-10) hover:text-(--chat-theme) disabled:opacity-40",
13593
+ onClick: () => {
13594
+ if (promoteDisabled) return;
13595
+ onManageAdmin(memberId, "add");
13596
+ },
13597
+ disabled: isManaging === memberId || promoteDisabled,
13598
+ children: isManaging === memberId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { size: 13 })
13599
+ }
13600
+ )
12904
13601
  }
12905
13602
  )
12906
13603
  ] })
@@ -12924,7 +13621,9 @@ function AdminParticipantList({
12924
13621
  isAdmin,
12925
13622
  onManageAdmin,
12926
13623
  showPromote,
12927
- showDemoteForAdmins
13624
+ showDemoteForAdmins,
13625
+ promoteDisabled = false,
13626
+ promoteDisabledReason
12928
13627
  }) {
12929
13628
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: members.map((member, index) => {
12930
13629
  const { memberId, memberName, memberEmail, memberImage } = getMemberFields(member);
@@ -12942,6 +13641,8 @@ function AdminParticipantList({
12942
13641
  isAdmin: userIsAdmin,
12943
13642
  showPromote: showPromote && !userIsAdmin && !userIsOwner,
12944
13643
  showDemote: showDemoteForAdmins ? userIsAdmin && !userIsOwner : false,
13644
+ promoteDisabled,
13645
+ promoteDisabledReason,
12945
13646
  isOwnerViewer,
12946
13647
  isManaging,
12947
13648
  onManageAdmin
@@ -12961,6 +13662,8 @@ var AdminsDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
12961
13662
  promotableParticipants,
12962
13663
  handleManageAdmin,
12963
13664
  adminCount,
13665
+ maxAdmins,
13666
+ atAdminLimit,
12964
13667
  hasRequiredData
12965
13668
  } = useAdminsDrawer({ conversationId, conversationInfo });
12966
13669
  if (!hasRequiredData) return null;
@@ -12980,7 +13683,10 @@ var AdminsDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
12980
13683
  /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: chatSheetTitleClass, children: "Group Administrators" }),
12981
13684
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-(--chat-theme-10) px-2 py-0.5 text-[11px] font-medium leading-none text-(--chat-theme)", children: adminCount })
12982
13685
  ] }),
12983
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: chatSheetSubtitleClass, children: "Manage who can adjust group settings and members." })
13686
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: chatSheetSubtitleClass, children: [
13687
+ "Manage who can adjust group settings and members",
13688
+ maxAdmins ? ` (max ${maxAdmins}).` : "."
13689
+ ] })
12984
13690
  ] }),
12985
13691
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(chatSheetBodyClass, "px-2.5 py-2"), children: [
12986
13692
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2.5 px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -13023,7 +13729,9 @@ var AdminsDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
13023
13729
  ownerId,
13024
13730
  isAdmin,
13025
13731
  onManageAdmin: handleManageAdmin,
13026
- showPromote: true
13732
+ showPromote: true,
13733
+ promoteDisabled: atAdminLimit,
13734
+ promoteDisabledReason: `Maximum ${maxAdmins} admins allowed in this group.`
13027
13735
  }
13028
13736
  ) })
13029
13737
  ] }),
@@ -13040,7 +13748,9 @@ var AdminsDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
13040
13748
  isAdmin,
13041
13749
  onManageAdmin: handleManageAdmin,
13042
13750
  showPromote: true,
13043
- showDemoteForAdmins: true
13751
+ showDemoteForAdmins: true,
13752
+ promoteDisabled: atAdminLimit,
13753
+ promoteDisabledReason: `Maximum ${maxAdmins} admins allowed in this group.`
13044
13754
  }
13045
13755
  ) })
13046
13756
  ] }),
@@ -13056,21 +13766,24 @@ var AdminsDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
13056
13766
  var AdminsDrawer_default = AdminsDrawer;
13057
13767
  init_useStore();
13058
13768
  var MembersDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo }) => {
13059
- const [searchQuery, setSearchQuery] = React2.useState("");
13060
- const [isManaging, setIsManaging] = React2.useState(null);
13769
+ const [searchQuery, setSearchQuery] = React3.useState("");
13770
+ const [isManaging, setIsManaging] = React3.useState(null);
13061
13771
  const allUsers = exports.useChatStore((s) => s.allUsers);
13062
13772
  const manageGroupMembers = exports.useChatStore((s) => s.manageGroupMembers);
13063
13773
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
13064
- const filteredUsers = React2.useMemo(() => {
13774
+ const filteredUsers = React3.useMemo(() => {
13065
13775
  if (!searchQuery.trim()) return allUsers;
13066
13776
  return allUsers.filter(
13067
13777
  (u) => u.name?.toLowerCase().includes(searchQuery.toLowerCase()) || u.email?.toLowerCase().includes(searchQuery.toLowerCase())
13068
13778
  );
13069
13779
  }, [allUsers, searchQuery]);
13070
13780
  const { userPermissions } = useGroupInfo(conversationId);
13781
+ const { settings } = useEffectiveSettingsOptional();
13782
+ const maxParticipants = settings.maxParticipantsPerGroup;
13071
13783
  if (!conversationId || !conversationInfo) return null;
13072
13784
  const participants = conversationInfo.participants || [];
13073
13785
  const admins = conversationInfo.groupAdmins || [];
13786
+ const atParticipantLimit = participants.length >= maxParticipants;
13074
13787
  let ownerId = null;
13075
13788
  if (conversationInfo.owner) {
13076
13789
  ownerId = typeof conversationInfo.owner === "object" && conversationInfo.owner !== null ? conversationInfo.owner._id || conversationInfo.owner.id : conversationInfo.owner;
@@ -13124,14 +13837,24 @@ var MembersDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
13124
13837
  }
13125
13838
  ),
13126
13839
  options.showAdd && /* @__PURE__ */ jsxRuntime.jsx(
13127
- Button,
13840
+ AdminPermissionTooltip,
13128
13841
  {
13129
- variant: "ghost",
13130
- size: "sm",
13131
- className: "size-7 rounded-md p-0 text-(--chat-muted) hover:bg-(--chat-theme-10) hover:text-(--chat-theme)",
13132
- onClick: () => handleManageMember(memberId, "add"),
13133
- disabled: isManaging === memberId,
13134
- children: isManaging === memberId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { size: 13 })
13842
+ disabled: atParticipantLimit,
13843
+ message: `Maximum ${maxParticipants} participants allowed in this group.`,
13844
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13845
+ Button,
13846
+ {
13847
+ variant: "ghost",
13848
+ size: "sm",
13849
+ className: "size-7 rounded-md p-0 text-(--chat-muted) hover:bg-(--chat-theme-10) hover:text-(--chat-theme) disabled:opacity-40",
13850
+ onClick: () => {
13851
+ if (atParticipantLimit) return;
13852
+ handleManageMember(memberId, "add");
13853
+ },
13854
+ disabled: isManaging === memberId || atParticipantLimit,
13855
+ children: isManaging === memberId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserPlus, { size: 13 })
13856
+ }
13857
+ )
13135
13858
  }
13136
13859
  )
13137
13860
  ] })
@@ -13153,7 +13876,11 @@ var MembersDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo })
13153
13876
  /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: chatSheetTitleClass, children: "Group Management" }),
13154
13877
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-(--chat-theme-10) px-2 py-0.5 text-[11px] font-medium leading-none text-(--chat-theme)", children: participants.length })
13155
13878
  ] }),
13156
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: chatSheetSubtitleClass, children: "Manage members and invite people." })
13879
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: chatSheetSubtitleClass, children: [
13880
+ "Manage members and invite people (max ",
13881
+ maxParticipants,
13882
+ ")."
13883
+ ] })
13157
13884
  ] }),
13158
13885
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(chatSheetBodyClass, "px-2.5 py-2"), children: [
13159
13886
  !searchQuery && /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mb-2.5", children: [
@@ -13274,8 +14001,8 @@ var ChatActionModal_default = ChatActionModal;
13274
14001
  // src/chat/channel-header/useChannelHeader.ts
13275
14002
  init_useStore();
13276
14003
  function useDebounce(value, delay) {
13277
- const [debouncedValue, setDebouncedValue] = React2.useState(value);
13278
- React2.useEffect(() => {
14004
+ const [debouncedValue, setDebouncedValue] = React3.useState(value);
14005
+ React3.useEffect(() => {
13279
14006
  const timer = setTimeout(() => setDebouncedValue(value), delay);
13280
14007
  return () => {
13281
14008
  clearTimeout(timer);
@@ -13295,7 +14022,9 @@ init_store_helpers();
13295
14022
  function useBlockUser(activeChannel, conversationId, onActiveChannelPatch) {
13296
14023
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
13297
14024
  const composerDisabledByConversation = exports.useChatStore((s) => s.composerDisabledByConversation);
13298
- const [isBlocking, setIsBlocking] = React2.useState(false);
14025
+ const { getBlockUsersGate } = useEffectiveSettingsOptional();
14026
+ const blockUsersGate = getBlockUsersGate();
14027
+ const [isBlocking, setIsBlocking] = React3.useState(false);
13299
14028
  const resolvedConversationId = conversationId ?? activeChannel?.conversationId ?? null;
13300
14029
  const composerDisabledReason = resolvedConversationId ? composerDisabledByConversation[resolvedConversationId] : void 0;
13301
14030
  const isBlockedFromConversation = exports.useChatStore((s) => {
@@ -13313,9 +14042,15 @@ function useBlockUser(activeChannel, conversationId, onActiveChannelPatch) {
13313
14042
  return !!conv?.isBlocked || !!activeChannel.isBlocked;
13314
14043
  });
13315
14044
  const isBlocked = isBlockedFromConversation || !activeChannel?.isGroup && !!composerDisabledReason && /block/i.test(composerDisabledReason);
13316
- const toggleBlock = React2.useCallback(async () => {
14045
+ const toggleBlock = React3.useCallback(async () => {
13317
14046
  if (!activeChannel?.id || activeChannel.isGroup) return false;
13318
14047
  const nextBlocked = !isBlocked;
14048
+ if (nextBlocked && !blockUsersGate.enabled) {
14049
+ exports.useChatStore.getState().handleAppError(
14050
+ blockUsersGate.disabledReason || "Blocking users is disabled by your administrator."
14051
+ );
14052
+ return false;
14053
+ }
13319
14054
  const type = nextBlocked ? "block" : "unblock";
13320
14055
  try {
13321
14056
  setIsBlocking(true);
@@ -13336,8 +14071,8 @@ function useBlockUser(activeChannel, conversationId, onActiveChannelPatch) {
13336
14071
  } finally {
13337
14072
  setIsBlocking(false);
13338
14073
  }
13339
- }, [activeChannel, isBlocked, onActiveChannelPatch, resolvedConversationId]);
13340
- return { isBlocked, isBlocking, toggleBlock };
14074
+ }, [activeChannel, isBlocked, onActiveChannelPatch, resolvedConversationId, blockUsersGate]);
14075
+ return { isBlocked, isBlocking, toggleBlock, blockUsersGate };
13341
14076
  }
13342
14077
 
13343
14078
  // src/chat/channel-header/useChannelHeader.ts
@@ -13370,8 +14105,8 @@ function useChannelHeader({
13370
14105
  onBack,
13371
14106
  onActiveChannelPatch
13372
14107
  }) {
13373
- const [state, setState] = React2.useState(initialState2);
13374
- const updateState = React2.useCallback((updates) => {
14108
+ const [state, setState] = React3.useState(initialState2);
14109
+ const updateState = React3.useCallback((updates) => {
13375
14110
  setState((prev) => ({ ...prev, ...updates }));
13376
14111
  }, []);
13377
14112
  const { formatLongDate } = useChatLocale();
@@ -13411,7 +14146,7 @@ function useChannelHeader({
13411
14146
  const setSearchedMessageId = exports.useChatStore((s) => s.setSearchedMessageId);
13412
14147
  const isSearchActive = exports.useChatStore((s) => s.isSearchActive);
13413
14148
  const debouncedSearch = useDebounce(state.searchQuery, 800);
13414
- const handleSearch = React2.useCallback(async (query, cursor) => {
14149
+ const handleSearch = React3.useCallback(async (query, cursor) => {
13415
14150
  if (!conversationId) return;
13416
14151
  const isFirstPage = !cursor;
13417
14152
  updateState({ isSearching: isFirstPage, isLoadingMoreSearch: !isFirstPage });
@@ -13432,7 +14167,7 @@ function useChannelHeader({
13432
14167
  updateState({ isSearching: false, isLoadingMoreSearch: false });
13433
14168
  }
13434
14169
  }, [conversationId, updateState]);
13435
- React2.useEffect(() => {
14170
+ React3.useEffect(() => {
13436
14171
  if (debouncedSearch && conversationId) {
13437
14172
  updateState({ searchCursor: null, searchHasMore: false });
13438
14173
  handleSearch(debouncedSearch);
@@ -13466,32 +14201,32 @@ function useChannelHeader({
13466
14201
  handleCloseSearch();
13467
14202
  };
13468
14203
  const canViewMembers = isAdmin || userPermissions.canAddMember || userPermissions.canRemoveMember;
13469
- const prevIsClearingRef = React2.useRef(isClearingChat);
13470
- React2.useEffect(() => {
14204
+ const prevIsClearingRef = React3.useRef(isClearingChat);
14205
+ React3.useEffect(() => {
13471
14206
  if (prevIsClearingRef.current === true && !isClearingChat) {
13472
14207
  updateState({ isClearChatAlertOpen: false });
13473
14208
  }
13474
14209
  prevIsClearingRef.current = isClearingChat;
13475
14210
  }, [isClearingChat, updateState]);
13476
- const prevIsDeletingRef = React2.useRef(isDeletingConversation);
13477
- React2.useEffect(() => {
14211
+ const prevIsDeletingRef = React3.useRef(isDeletingConversation);
14212
+ React3.useEffect(() => {
13478
14213
  if (prevIsDeletingRef.current === true && !isDeletingConversation) {
13479
14214
  updateState({ isDeletingConversationAlertOpen: false });
13480
14215
  onBack?.();
13481
14216
  }
13482
14217
  prevIsDeletingRef.current = isDeletingConversation;
13483
14218
  }, [isDeletingConversation, onBack, updateState]);
13484
- React2.useEffect(() => {
14219
+ React3.useEffect(() => {
13485
14220
  if (state.isPinOpen && conversationId) {
13486
14221
  fetchPinnedMessages2(conversationId);
13487
14222
  }
13488
14223
  }, [state.isPinOpen, conversationId, fetchPinnedMessages2]);
13489
- React2.useEffect(() => {
14224
+ React3.useEffect(() => {
13490
14225
  if (state.isFavoriteOpen && conversationId) {
13491
14226
  fetchStarredMessages2(conversationId);
13492
14227
  }
13493
14228
  }, [state.isFavoriteOpen, conversationId, fetchStarredMessages2]);
13494
- React2.useEffect(() => {
14229
+ React3.useEffect(() => {
13495
14230
  if (state.isAttachmentsOpen && conversationId) {
13496
14231
  const meta = exports.useChatStore.getState().messagesMetadata[conversationId];
13497
14232
  if (!meta?.isFirstPage) {
@@ -13873,6 +14608,7 @@ var MessageReactions_default = MessageReactions;
13873
14608
  var MessageToolbar = ({
13874
14609
  isSender,
13875
14610
  isRecent,
14611
+ isWithinDeleteWindow = isRecent,
13876
14612
  isPinned,
13877
14613
  isStarred,
13878
14614
  isPinPending,
@@ -13896,7 +14632,7 @@ var MessageToolbar = ({
13896
14632
  }) => {
13897
14633
  const { isDark, scopeClassName, themeStyles } = useChatTheme();
13898
14634
  const showDeleteMenu = isSender || hasAttachments || attachmentDeleteMode;
13899
- const showDeleteForEveryone = isSender && isRecent;
14635
+ const showDeleteForEveryone = isSender && isWithinDeleteWindow;
13900
14636
  return /* @__PURE__ */ jsxRuntime.jsxs(
13901
14637
  "div",
13902
14638
  {
@@ -14051,8 +14787,8 @@ function SentAttachmentCaptionModal({
14051
14787
  canEdit = false,
14052
14788
  onSave
14053
14789
  }) {
14054
- const [draft, setDraft] = React2.useState("");
14055
- React2.useEffect(() => {
14790
+ const [draft, setDraft] = React3.useState("");
14791
+ React3.useEffect(() => {
14056
14792
  if (open && attachment) {
14057
14793
  setDraft(attachment.caption || "");
14058
14794
  }
@@ -14218,16 +14954,59 @@ function resolveForwardItems(selectedKeys, messages) {
14218
14954
  }
14219
14955
 
14220
14956
  // src/chat/lib/chat-edit-window.ts
14221
- init_chat_constants();
14222
- function getMessageEditWindowMs(isGroup) {
14223
- return isGroup ? GROUP_EDIT_WINDOW_MS : DM_EDIT_WINDOW_MS;
14957
+ init_settings_types();
14958
+
14959
+ // src/lib/effective-settings.helpers.ts
14960
+ function minutesToMs(minutes) {
14961
+ return Math.max(0, minutes) * 60 * 1e3;
14224
14962
  }
14225
- function isWithinMessageEditWindow(createdAt, isGroup) {
14963
+ function getEditWindowMs(timers, isGroup) {
14964
+ return minutesToMs(isGroup ? timers.groupEditMinutes : timers.dmEditMinutes);
14965
+ }
14966
+ function getDeleteWindowMs(timers, isGroup) {
14967
+ return minutesToMs(
14968
+ isGroup ? timers.groupDeleteMinutes : timers.dmDeleteMinutes
14969
+ );
14970
+ }
14971
+ function isWithinWindow(createdAt, windowMs) {
14226
14972
  if (!createdAt) return false;
14227
14973
  const date = new Date(createdAt);
14228
14974
  if (isNaN(date.getTime())) return false;
14229
14975
  const diff = Date.now() - date.getTime();
14230
- return diff >= 0 && diff < getMessageEditWindowMs(isGroup);
14976
+ return diff >= 0 && diff < windowMs;
14977
+ }
14978
+ function isWithinMessageEditWindow(createdAt, timers, isGroup) {
14979
+ return isWithinWindow(createdAt, getEditWindowMs(timers, isGroup));
14980
+ }
14981
+ function isWithinMessageDeleteWindow(createdAt, timers, isGroup) {
14982
+ return isWithinWindow(createdAt, getDeleteWindowMs(timers, isGroup));
14983
+ }
14984
+ function matchesAllowedMimeType(fileType, allowedMimeTypes) {
14985
+ const mime = (fileType || "application/octet-stream").toLowerCase();
14986
+ return allowedMimeTypes.some((pattern) => {
14987
+ const normalized = pattern.trim().toLowerCase();
14988
+ if (!normalized) return false;
14989
+ if (normalized.endsWith("/*")) {
14990
+ const prefix = normalized.slice(0, -1);
14991
+ return mime.startsWith(prefix);
14992
+ }
14993
+ return mime === normalized;
14994
+ });
14995
+ }
14996
+ function buildAcceptFromMimeTypes(allowedMimeTypes) {
14997
+ return allowedMimeTypes.map((type) => type.trim()).filter(Boolean).join(",");
14998
+ }
14999
+ function formatFileSizeMB(sizeBytes) {
15000
+ return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`;
15001
+ }
15002
+
15003
+ // src/chat/lib/chat-edit-window.ts
15004
+ var FALLBACK_TIMERS = exports.DEFAULT_EFFECTIVE_SETTINGS.messageTimers;
15005
+ function isWithinMessageEditWindow2(createdAt, isGroup, timers = FALLBACK_TIMERS) {
15006
+ return isWithinMessageEditWindow(createdAt, timers, isGroup);
15007
+ }
15008
+ function isWithinMessageDeleteWindow2(createdAt, isGroup, timers = FALLBACK_TIMERS) {
15009
+ return isWithinMessageDeleteWindow(createdAt, timers, isGroup);
14231
15010
  }
14232
15011
 
14233
15012
  // src/chat/message-item/useMessageItemActions.ts
@@ -14252,7 +15031,7 @@ function useMessageItemActions({
14252
15031
  selectionMode = false,
14253
15032
  selectedKeys
14254
15033
  }) {
14255
- const [state, setState] = React2.useState({
15034
+ const [state, setState] = React3.useState({
14256
15035
  isHoverMessage: false,
14257
15036
  isEditing: false,
14258
15037
  editValue: String(message || ""),
@@ -14265,7 +15044,7 @@ function useMessageItemActions({
14265
15044
  const updateState = (updates) => {
14266
15045
  setState((prev) => ({ ...prev, ...updates }));
14267
15046
  };
14268
- React2.useEffect(() => {
15047
+ React3.useEffect(() => {
14269
15048
  setState((prev) => ({ ...prev, editValue: String(message || "") }));
14270
15049
  }, [message]);
14271
15050
  const emitDmEdit = exports.useChatStore((s) => s.emitDmEdit);
@@ -14305,6 +15084,9 @@ function useMessageItemActions({
14305
15084
  const canUnpin = !isPinned || isPinnedByMe;
14306
15085
  const { userPermissions } = useGroupInfo(conversationId || null);
14307
15086
  const { formatMessageTime } = useChatLocale();
15087
+ const { settings } = useEffectiveSettingsOptional();
15088
+ const messageTimers = settings.messageTimers;
15089
+ const maxPinnedMessages = settings.maxPinnedMessagesPerConversation;
14308
15090
  const visibleAttachments = getVisibleAttachments(attachments);
14309
15091
  const hasAttachments = visibleAttachments.length > 0;
14310
15092
  const hasMultiAttachments = hasAttachments && messageUsesAttachmentSelection({ attachments: visibleAttachments });
@@ -14321,7 +15103,8 @@ function useMessageItemActions({
14321
15103
  const hasText = Boolean(message?.trim());
14322
15104
  const isSelected = !!mid && !!selectedKeys && isMessageWhollySelected(selectedKeys, mid) && !hasMultiAttachments;
14323
15105
  const canEdit = (!isGroup || userPermissions.canEditMessage) && hasText;
14324
- const isWithinEditWindow = () => isWithinMessageEditWindow(createdAt, isGroup);
15106
+ const isWithinEditWindow = () => isWithinMessageEditWindow2(createdAt, isGroup, messageTimers);
15107
+ const isWithinDeleteWindow = () => isWithinMessageDeleteWindow2(createdAt, isGroup, messageTimers);
14325
15108
  const canEditAttachmentCaption = isSender && isWithinEditWindow() && (!isGroup || userPermissions.canEditMessage) && !isDeletedForEveryone && !!mid;
14326
15109
  const handleAttachmentCaptionClick = (attachment) => {
14327
15110
  if (!canEditAttachmentCaption) return;
@@ -14490,6 +15273,15 @@ function useMessageItemActions({
14490
15273
  });
14491
15274
  }
14492
15275
  } else {
15276
+ if (conversationId) {
15277
+ const pinnedCount = (pinnedMessagesByConversation[conversationId] || []).length || exports.useChatStore.getState().liveConversationCounts[conversationId]?.pinnedCount || 0;
15278
+ if (pinnedCount >= maxPinnedMessages) {
15279
+ exports.useChatStore.getState().handleAppError(
15280
+ `You can pin up to ${maxPinnedMessages} messages in this conversation.`
15281
+ );
15282
+ return;
15283
+ }
15284
+ }
14493
15285
  if (isGroup) {
14494
15286
  if (!conversationId) return;
14495
15287
  emitGroupPin({
@@ -14548,6 +15340,7 @@ function useMessageItemActions({
14548
15340
  canUnpin,
14549
15341
  canEdit,
14550
15342
  isWithinEditWindow,
15343
+ isWithinDeleteWindow,
14551
15344
  canEditAttachmentCaption,
14552
15345
  visibleAttachments,
14553
15346
  hasAttachments,
@@ -14579,7 +15372,7 @@ function useMessageItemActions({
14579
15372
  }
14580
15373
 
14581
15374
  // src/chat/lib/reply-preview.ts
14582
- function normalizeAttachments(replyTo, loggedUserId) {
15375
+ function normalizeAttachments2(replyTo, loggedUserId) {
14583
15376
  const raw = replyTo?.attachments;
14584
15377
  if (!Array.isArray(raw) || raw.length === 0) return [];
14585
15378
  return raw.map((att, index) => {
@@ -14592,7 +15385,7 @@ function resolveReplyToMessage(replyTo, messagesById, loggedUserId) {
14592
15385
  if (typeof replyTo === "object" && (replyTo.content?.trim() || replyTo.attachments?.length)) {
14593
15386
  return {
14594
15387
  ...replyTo,
14595
- attachments: normalizeAttachments(replyTo, loggedUserId)
15388
+ attachments: normalizeAttachments2(replyTo, loggedUserId)
14596
15389
  };
14597
15390
  }
14598
15391
  const replyId = String(
@@ -15029,6 +15822,7 @@ function MessageItemView(props) {
15029
15822
  canUnpin,
15030
15823
  canEdit,
15031
15824
  isWithinEditWindow,
15825
+ isWithinDeleteWindow,
15032
15826
  canEditAttachmentCaption,
15033
15827
  visibleAttachments,
15034
15828
  hasAttachments,
@@ -15120,6 +15914,7 @@ function MessageItemView(props) {
15120
15914
  {
15121
15915
  isSender,
15122
15916
  isRecent: isWithinEditWindow(),
15917
+ isWithinDeleteWindow: isWithinDeleteWindow(),
15123
15918
  messageId: mid,
15124
15919
  isPinned,
15125
15920
  isStarred,
@@ -15309,7 +16104,7 @@ function MessageSelectionBar({
15309
16104
  className
15310
16105
  }) {
15311
16106
  const { scopeClassName, themeStyles } = useChatTheme();
15312
- const [isDeleteOpen, setIsDeleteOpen] = React2.useState(false);
16107
+ const [isDeleteOpen, setIsDeleteOpen] = React3.useState(false);
15313
16108
  if (count === 0) return null;
15314
16109
  const countLabel = selectionLabel === "files" ? count === 1 ? "file" : "files" : count === 1 ? "message" : "messages";
15315
16110
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -15414,6 +16209,7 @@ function MessageSelectionBar({
15414
16209
  }
15415
16210
 
15416
16211
  // src/chat/active-channel-messages/utils.ts
16212
+ init_settings_types();
15417
16213
  var getMessageSenderId2 = (msg) => msg ? String(msg.senderId || msg.sender?._id || "") : "";
15418
16214
  var isLikelyObjectId = (value) => /^[a-f\d]{24}$/i.test(value);
15419
16215
  function resolveGroupSenderDisplay(message, allUsers = [], participants = []) {
@@ -15445,7 +16241,7 @@ var isSameMessageSide = (a, b, isGroupChat) => {
15445
16241
  const bId = getMessageSenderId2(b);
15446
16242
  return aId !== "" && aId === bId;
15447
16243
  };
15448
- function canDeleteSelectionForEveryone(selectedKeys, messages, isGroup) {
16244
+ function canDeleteSelectionForEveryone(selectedKeys, messages, isGroup, timers = exports.DEFAULT_EFFECTIVE_SETTINGS.messageTimers) {
15449
16245
  if (selectedKeys.size === 0) return false;
15450
16246
  const parsed = [...selectedKeys].map((key) => parseSelectionKey(key));
15451
16247
  const messageIds = new Set(parsed.map((entry) => entry.messageId));
@@ -15453,7 +16249,7 @@ function canDeleteSelectionForEveryone(selectedKeys, messages, isGroup) {
15453
16249
  const messageId2 = [...messageIds][0];
15454
16250
  const message = messages.find((entry) => String(entry._id || entry.id) === messageId2);
15455
16251
  if (!message?.isOwnMessage) return false;
15456
- if (!isWithinMessageEditWindow(message.timestamp, isGroup)) return false;
16252
+ if (!isWithinMessageDeleteWindow2(message.timestamp, isGroup, timers)) return false;
15457
16253
  const hasAttachmentKey = parsed.some((entry) => entry.attachmentId);
15458
16254
  if (!hasAttachmentKey) {
15459
16255
  return selectedKeys.size === 1;
@@ -15617,22 +16413,24 @@ function useActiveChannelMessages({
15617
16413
  onSelectionModeChange,
15618
16414
  conversationInfo
15619
16415
  }) {
15620
- const [selectionMode, setSelectionMode] = React2.useState(false);
15621
- const [selectedKeys, setSelectedKeys] = React2.useState(/* @__PURE__ */ new Set());
16416
+ const [selectionMode, setSelectionMode] = React3.useState(false);
16417
+ const [selectedKeys, setSelectedKeys] = React3.useState(/* @__PURE__ */ new Set());
15622
16418
  const emitDmDelete = exports.useChatStore((s) => s.emitDmDelete);
15623
16419
  const emitGroupDelete = exports.useChatStore((s) => s.emitGroupDelete);
15624
16420
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
15625
- const scrollContainerRef = React2.useRef(null);
15626
- const bottomRef = React2.useRef(null);
15627
- const messageListInitializedRef = React2.useRef(false);
15628
- const previousMessageCountRef = React2.useRef(0);
15629
- const previousLastMessageIdRef = React2.useRef(null);
15630
- const [enteringMessageIds, setEnteringMessageIds] = React2.useState(/* @__PURE__ */ new Set());
15631
- const [showScrollToBottom, setShowScrollToBottom] = React2.useState(false);
15632
- const stickToBottomRef = React2.useRef(true);
15633
- const pendingJumpRef = React2.useRef(null);
15634
- const jumpPagesRef = React2.useRef(0);
15635
- const [jumpNonce, setJumpNonce] = React2.useState(0);
16421
+ const { settings } = useEffectiveSettingsOptional();
16422
+ const messageTimers = settings.messageTimers;
16423
+ const scrollContainerRef = React3.useRef(null);
16424
+ const bottomRef = React3.useRef(null);
16425
+ const messageListInitializedRef = React3.useRef(false);
16426
+ const previousMessageCountRef = React3.useRef(0);
16427
+ const previousLastMessageIdRef = React3.useRef(null);
16428
+ const [enteringMessageIds, setEnteringMessageIds] = React3.useState(/* @__PURE__ */ new Set());
16429
+ const [showScrollToBottom, setShowScrollToBottom] = React3.useState(false);
16430
+ const stickToBottomRef = React3.useRef(true);
16431
+ const pendingJumpRef = React3.useRef(null);
16432
+ const jumpPagesRef = React3.useRef(0);
16433
+ const [jumpNonce, setJumpNonce] = React3.useState(0);
15636
16434
  const { fetchMessages: fetchMessages2, messagesMetadata, deletedForMeIds, searchedMessageId, isSearchActive, setSearchActive, setSearchedMessageId, fetchSearchMessageContext } = exports.useChatStore();
15637
16435
  const loggedUserImage = exports.useChatStore((s) => s.loggedUserDetails?.image);
15638
16436
  const conversations = exports.useChatStore((s) => s.conversations);
@@ -15641,29 +16439,29 @@ function useActiveChannelMessages({
15641
16439
  const mappedApiMessages = conversationMessages.map(
15642
16440
  (m) => mapApiMessage(m, activeChannel, conversationId, loggedUserId, activeConversation)
15643
16441
  );
15644
- const displayMessages = React2.useMemo(
16442
+ const displayMessages = React3.useMemo(
15645
16443
  () => mergeChatMessages(mappedApiMessages, localMessages, loggedUserId).filter((m) => !deletedForMeIds.includes(String(m._id || m.id)) && !m.isDeletedForMe).filter((m) => !conversationId || String(m.conversationId || "") === String(conversationId)).sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()),
15646
16444
  [mappedApiMessages, localMessages, loggedUserId, deletedForMeIds, conversationId]
15647
16445
  );
15648
- const messagesById = React2.useMemo(
16446
+ const messagesById = React3.useMemo(
15649
16447
  () => new Map(displayMessages.map((message) => [String(message._id || message.id), message])),
15650
16448
  [displayMessages]
15651
16449
  );
15652
- const messagesWithResolvedReplies = React2.useMemo(
16450
+ const messagesWithResolvedReplies = React3.useMemo(
15653
16451
  () => displayMessages.map((message) => ({
15654
16452
  ...message,
15655
16453
  replyTo: resolveReplyToMessage(message.replyTo, messagesById, loggedUserId)
15656
16454
  })),
15657
16455
  [displayMessages, messagesById, loggedUserId]
15658
16456
  );
15659
- const jumpDateBounds = React2.useMemo(
16457
+ const jumpDateBounds = React3.useMemo(
15660
16458
  () => resolveJumpDateBounds(
15661
16459
  conversationInfo?.createdAt,
15662
16460
  displayMessages[0]?.timestamp
15663
16461
  ),
15664
16462
  [conversationInfo?.createdAt, displayMessages]
15665
16463
  );
15666
- React2.useEffect(() => {
16464
+ React3.useEffect(() => {
15667
16465
  const messages = messagesWithResolvedReplies;
15668
16466
  const lastMessage = messages[messages.length - 1];
15669
16467
  const lastMessageId = lastMessage ? String(lastMessage._id || lastMessage.id) : null;
@@ -15690,11 +16488,11 @@ function useActiveChannelMessages({
15690
16488
  }, [messagesWithResolvedReplies]);
15691
16489
  const isInitialLoading = isLoading && currentMeta?.isFirstPage;
15692
16490
  const isLoadingMore = isLoading && !currentMeta?.isFirstPage;
15693
- const clearSelection = React2.useCallback(() => {
16491
+ const clearSelection = React3.useCallback(() => {
15694
16492
  setSelectionMode(false);
15695
16493
  setSelectedKeys(/* @__PURE__ */ new Set());
15696
16494
  }, []);
15697
- React2.useEffect(() => {
16495
+ React3.useEffect(() => {
15698
16496
  clearSelection();
15699
16497
  messageListInitializedRef.current = false;
15700
16498
  previousMessageCountRef.current = 0;
@@ -15705,14 +16503,14 @@ function useActiveChannelMessages({
15705
16503
  stickToBottomRef.current = true;
15706
16504
  setShowScrollToBottom(false);
15707
16505
  }, [conversationId, clearSelection]);
15708
- const onSelectionModeChangeRef = React2.useRef(onSelectionModeChange);
15709
- React2.useEffect(() => {
16506
+ const onSelectionModeChangeRef = React3.useRef(onSelectionModeChange);
16507
+ React3.useEffect(() => {
15710
16508
  onSelectionModeChangeRef.current = onSelectionModeChange;
15711
16509
  }, [onSelectionModeChange]);
15712
- React2.useEffect(() => {
16510
+ React3.useEffect(() => {
15713
16511
  onSelectionModeChangeRef.current?.(selectionMode);
15714
16512
  }, [selectionMode]);
15715
- const toggleSelection = React2.useCallback((message, attachmentId) => {
16513
+ const toggleSelection = React3.useCallback((message, attachmentId) => {
15716
16514
  const messageId2 = String(message._id || message.id);
15717
16515
  if (!messageId2) return;
15718
16516
  const useAttachmentSelection = messageUsesAttachmentSelection(message);
@@ -15725,7 +16523,7 @@ function useActiveChannelMessages({
15725
16523
  return next;
15726
16524
  });
15727
16525
  }, []);
15728
- const enterSelectionMode = React2.useCallback((message, attachmentId) => {
16526
+ const enterSelectionMode = React3.useCallback((message, attachmentId) => {
15729
16527
  const messageId2 = String(message._id || message.id);
15730
16528
  if (!messageId2) return;
15731
16529
  setSelectionMode(true);
@@ -15739,17 +16537,23 @@ function useActiveChannelMessages({
15739
16537
  setSelectedKeys((prev) => new Set(prev).add(messageId2));
15740
16538
  }, []);
15741
16539
  const selectedForwardItems = resolveForwardItems(selectedKeys, displayMessages);
15742
- const canDeleteForEveryone = React2.useMemo(
15743
- () => canDeleteSelectionForEveryone(selectedKeys, displayMessages, !!activeChannel?.isGroup),
15744
- [selectedKeys, displayMessages, activeChannel?.isGroup]
16540
+ const canDeleteForEveryone = React3.useMemo(
16541
+ () => canDeleteSelectionForEveryone(
16542
+ selectedKeys,
16543
+ displayMessages,
16544
+ !!activeChannel?.isGroup,
16545
+ messageTimers
16546
+ ),
16547
+ [selectedKeys, displayMessages, activeChannel?.isGroup, messageTimers]
15745
16548
  );
15746
- const emitSelectionDelete = React2.useCallback(
16549
+ const emitSelectionDelete = React3.useCallback(
15747
16550
  (deleteType) => {
15748
16551
  if (!loggedUserDetails || selectedKeys.size === 0) return;
15749
16552
  if (deleteType === "everyone" && !canDeleteSelectionForEveryone(
15750
16553
  selectedKeys,
15751
16554
  displayMessages,
15752
- !!activeChannel?.isGroup
16555
+ !!activeChannel?.isGroup,
16556
+ messageTimers
15753
16557
  )) {
15754
16558
  return;
15755
16559
  }
@@ -15790,13 +16594,14 @@ function useActiveChannelMessages({
15790
16594
  conversationId,
15791
16595
  emitGroupDelete,
15792
16596
  emitDmDelete,
15793
- clearSelection
16597
+ clearSelection,
16598
+ messageTimers
15794
16599
  ]
15795
16600
  );
15796
- const handleBatchDeleteForMe = React2.useCallback(() => {
16601
+ const handleBatchDeleteForMe = React3.useCallback(() => {
15797
16602
  emitSelectionDelete("me");
15798
16603
  }, [emitSelectionDelete]);
15799
- const handleBatchDeleteForEveryone = React2.useCallback(() => {
16604
+ const handleBatchDeleteForEveryone = React3.useCallback(() => {
15800
16605
  emitSelectionDelete("everyone");
15801
16606
  }, [emitSelectionDelete]);
15802
16607
  const handleBatchForward = () => {
@@ -15820,7 +16625,7 @@ function useActiveChannelMessages({
15820
16625
  }
15821
16626
  await fetchSearchMessageContext(messageId2, { searchMode: false });
15822
16627
  };
15823
- const handleJumpTo = React2.useCallback((preset, specificDate) => {
16628
+ const handleJumpTo = React3.useCallback((preset, specificDate) => {
15824
16629
  stickToBottomRef.current = false;
15825
16630
  if (preset === "beginning") {
15826
16631
  pendingJumpRef.current = { kind: "beginning" };
@@ -15841,7 +16646,7 @@ function useActiveChannelMessages({
15841
16646
  jumpPagesRef.current = 0;
15842
16647
  setJumpNonce((n) => n + 1);
15843
16648
  }, [jumpDateBounds.minDay, jumpDateBounds.maxDay]);
15844
- React2.useEffect(() => {
16649
+ React3.useEffect(() => {
15845
16650
  const pending = pendingJumpRef.current;
15846
16651
  if (!pending || isLoading) return;
15847
16652
  const step = resolveJumpStep(
@@ -15880,13 +16685,13 @@ function useActiveChannelMessages({
15880
16685
  fetchMessages2,
15881
16686
  setSearchedMessageId
15882
16687
  ]);
15883
- const updateScrollToBottomVisibility = React2.useCallback((el) => {
16688
+ const updateScrollToBottomVisibility = React3.useCallback((el) => {
15884
16689
  const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
15885
16690
  const nearBottom = distanceFromBottom <= 96;
15886
16691
  stickToBottomRef.current = nearBottom;
15887
16692
  setShowScrollToBottom(!nearBottom);
15888
16693
  }, []);
15889
- const handleScrollToBottom = React2.useCallback(() => {
16694
+ const handleScrollToBottom = React3.useCallback(() => {
15890
16695
  stickToBottomRef.current = true;
15891
16696
  if (isSearchActive) {
15892
16697
  setSearchActive(false);
@@ -15913,12 +16718,12 @@ function useActiveChannelMessages({
15913
16718
  });
15914
16719
  }
15915
16720
  };
15916
- React2.useLayoutEffect(() => {
16721
+ React3.useLayoutEffect(() => {
15917
16722
  if (!isInitialLoading && stickToBottomRef.current && !pendingJumpRef.current) {
15918
16723
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
15919
16724
  }
15920
16725
  }, [messagesWithResolvedReplies.length, isInitialLoading]);
15921
- React2.useEffect(() => {
16726
+ React3.useEffect(() => {
15922
16727
  if (searchedMessageId && !isLoading) {
15923
16728
  stickToBottomRef.current = false;
15924
16729
  const container = scrollContainerRef.current;
@@ -16128,8 +16933,8 @@ function CalendarDayButton({
16128
16933
  ...props
16129
16934
  }) {
16130
16935
  const defaultClassNames = reactDayPicker.getDefaultClassNames();
16131
- const ref = React2__namespace.useRef(null);
16132
- React2__namespace.useEffect(() => {
16936
+ const ref = React3__namespace.useRef(null);
16937
+ React3__namespace.useEffect(() => {
16133
16938
  if (modifiers.focused) ref.current?.focus();
16134
16939
  }, [modifiers.focused]);
16135
16940
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -16161,12 +16966,12 @@ var PRESETS = [
16161
16966
  ];
16162
16967
  function ChatDateJumpMenu({ label, minDay, maxDay, onJump }) {
16163
16968
  const { scopeClassName } = useChatTheme();
16164
- const [open, setOpen] = React2.useState(false);
16165
- const [showCalendar, setShowCalendar] = React2.useState(false);
16166
- const [calendarMonth, setCalendarMonth] = React2.useState(() => new Date(maxDay));
16969
+ const [open, setOpen] = React3.useState(false);
16970
+ const [showCalendar, setShowCalendar] = React3.useState(false);
16971
+ const [calendarMonth, setCalendarMonth] = React3.useState(() => new Date(maxDay));
16167
16972
  const minDate = new Date(minDay);
16168
16973
  const maxDate = new Date(maxDay);
16169
- React2.useEffect(() => {
16974
+ React3.useEffect(() => {
16170
16975
  if (showCalendar) setCalendarMonth(new Date(maxDay));
16171
16976
  }, [showCalendar, maxDay]);
16172
16977
  const queueJump = (preset, specificDate) => {
@@ -16500,8 +17305,8 @@ function AttachmentCaptionModal({
16500
17305
  attachment,
16501
17306
  onSave
16502
17307
  }) {
16503
- const [draft, setDraft] = React2.useState("");
16504
- React2.useEffect(() => {
17308
+ const [draft, setDraft] = React3.useState("");
17309
+ React3.useEffect(() => {
16505
17310
  if (open && attachment) {
16506
17311
  setDraft(attachment.caption || "");
16507
17312
  }
@@ -16878,9 +17683,9 @@ function ComposerAttachmentPreview({
16878
17683
  embedded = false,
16879
17684
  maxAttachments = COMPOSER_MAX_ATTACHMENTS
16880
17685
  }) {
16881
- const [captionTargetId, setCaptionTargetId] = React2.useState(null);
16882
- const [lightboxState, setLightboxState] = React2.useState(null);
16883
- const { media, files, imageMedia, lightboxImages } = React2.useMemo(() => {
17686
+ const [captionTargetId, setCaptionTargetId] = React3.useState(null);
17687
+ const [lightboxState, setLightboxState] = React3.useState(null);
17688
+ const { media, files, imageMedia, lightboxImages } = React3.useMemo(() => {
16884
17689
  const mediaItems = attachments.filter(
16885
17690
  (item) => (item.type === "image" || item.type === "video") && item.previewUrl
16886
17691
  );
@@ -17033,7 +17838,6 @@ var getAttachmentType = (file) => {
17033
17838
  };
17034
17839
 
17035
17840
  // src/chat/channel-message-box/useChannelMessageBox.ts
17036
- var MAX_ATTACHMENTS = COMPOSER_MAX_ATTACHMENTS;
17037
17841
  function useChannelMessageBox({
17038
17842
  activeChannel,
17039
17843
  conversationId,
@@ -17047,7 +17851,13 @@ function useChannelMessageBox({
17047
17851
  conversationId,
17048
17852
  onActiveChannelPatch
17049
17853
  );
17050
- const [state, setState] = React2.useState({
17854
+ const { settings, getAttachmentsGate } = useEffectiveSettingsOptional();
17855
+ const attachmentsGate = getAttachmentsGate();
17856
+ const maxAttachments = settings.attachments.maxAttachmentsPerMessage;
17857
+ const maxFileSizeBytes = settings.attachments.maxFileSizeMB * 1024 * 1024;
17858
+ const allowedMimeTypes = settings.attachments.allowedMimeTypes;
17859
+ const acceptAttribute = buildAcceptFromMimeTypes(allowedMimeTypes);
17860
+ const [state, setState] = React3.useState({
17051
17861
  messageInput: "",
17052
17862
  attachments: [],
17053
17863
  isFocused: false,
@@ -17057,8 +17867,8 @@ function useChannelMessageBox({
17057
17867
  const updateState = (updates) => {
17058
17868
  setState((prev) => ({ ...prev, ...updates }));
17059
17869
  };
17060
- const typingTimeoutRef = React2.useRef(null);
17061
- const conversationIdRef = React2.useRef(conversationId);
17870
+ const typingTimeoutRef = React3.useRef(null);
17871
+ const conversationIdRef = React3.useRef(conversationId);
17062
17872
  conversationIdRef.current = conversationId;
17063
17873
  const { syncComposerRef } = useConversationDraft(conversationId);
17064
17874
  const emitTypingStart = exports.useChatStore((s) => s.emitTypingStart);
@@ -17069,12 +17879,12 @@ function useChannelMessageBox({
17069
17879
  const allUsers = exports.useChatStore((s) => s.allUsers);
17070
17880
  const composerDisabledByConversation = exports.useChatStore((s) => s.composerDisabledByConversation);
17071
17881
  const composerDisabledReason = conversationId ? composerDisabledByConversation[conversationId] : void 0;
17072
- const fileInputRef = React2.useRef(null);
17073
- const attachmentsCountRef = React2.useRef(0);
17074
- React2__namespace.default.useEffect(() => {
17882
+ const fileInputRef = React3.useRef(null);
17883
+ const attachmentsCountRef = React3.useRef(0);
17884
+ React3__namespace.default.useEffect(() => {
17075
17885
  attachmentsCountRef.current = state.attachments.length;
17076
17886
  }, [state.attachments.length]);
17077
- React2__namespace.default.useEffect(() => {
17887
+ React3__namespace.default.useEffect(() => {
17078
17888
  const draft = {
17079
17889
  messageInput: state.messageInput,
17080
17890
  attachments: state.attachments
@@ -17082,7 +17892,7 @@ function useChannelMessageBox({
17082
17892
  syncComposerRef(draft);
17083
17893
  updateConversationDraft(conversationIdRef.current, draft);
17084
17894
  }, [state.messageInput, state.attachments, syncComposerRef]);
17085
- React2__namespace.default.useEffect(() => {
17895
+ React3__namespace.default.useEffect(() => {
17086
17896
  if (typingTimeoutRef.current) {
17087
17897
  clearTimeout(typingTimeoutRef.current);
17088
17898
  typingTimeoutRef.current = null;
@@ -17142,21 +17952,52 @@ function useChannelMessageBox({
17142
17952
  const handleFileChange = (e) => {
17143
17953
  const files = e.target.files ? Array.from(e.target.files) : [];
17144
17954
  if (!files.length) return;
17955
+ if (!attachmentsGate.enabled) {
17956
+ updateState({
17957
+ attachmentError: attachmentsGate.disabledReason || "Attachments are disabled by your administrator."
17958
+ });
17959
+ if (fileInputRef.current) fileInputRef.current.value = "";
17960
+ return;
17961
+ }
17145
17962
  const currentCount = attachmentsCountRef.current;
17146
- const remaining = MAX_ATTACHMENTS - currentCount;
17963
+ const remaining = maxAttachments - currentCount;
17147
17964
  if (remaining <= 0) {
17148
- updateState({ attachmentError: `Maximum ${MAX_ATTACHMENTS} files allowed` });
17965
+ updateState({ attachmentError: `Maximum ${maxAttachments} files allowed` });
17149
17966
  if (fileInputRef.current) fileInputRef.current.value = "";
17150
17967
  return;
17151
17968
  }
17152
- if (files.length > remaining) {
17969
+ const validFiles = [];
17970
+ const rejectionMessages = [];
17971
+ for (const file of files) {
17972
+ if (!matchesAllowedMimeType(file.type, allowedMimeTypes)) {
17973
+ rejectionMessages.push(`"${file.name}" type is not allowed`);
17974
+ continue;
17975
+ }
17976
+ if (file.size > maxFileSizeBytes) {
17977
+ rejectionMessages.push(
17978
+ `"${file.name}" exceeds ${settings.attachments.maxFileSizeMB} MB (got ${formatFileSizeMB(file.size)})`
17979
+ );
17980
+ continue;
17981
+ }
17982
+ validFiles.push(file);
17983
+ }
17984
+ if (validFiles.length === 0) {
17985
+ updateState({
17986
+ attachmentError: rejectionMessages[0] || "Selected files are not allowed"
17987
+ });
17988
+ if (fileInputRef.current) fileInputRef.current.value = "";
17989
+ return;
17990
+ }
17991
+ if (validFiles.length > remaining) {
17153
17992
  updateState({
17154
17993
  attachmentError: `You can upload only ${remaining} more file${remaining > 1 ? "s" : ""}`
17155
17994
  });
17995
+ } else if (rejectionMessages.length > 0) {
17996
+ updateState({ attachmentError: rejectionMessages[0] });
17156
17997
  } else {
17157
17998
  updateState({ attachmentError: "" });
17158
17999
  }
17159
- const filesToAdd = files.slice(0, remaining);
18000
+ const filesToAdd = validFiles.slice(0, remaining);
17160
18001
  attachmentsCountRef.current = currentCount + filesToAdd.length;
17161
18002
  const newAttachments = filesToAdd.map((file) => ({
17162
18003
  id: `att-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
@@ -17213,7 +18054,7 @@ function useChannelMessageBox({
17213
18054
  return {
17214
18055
  ...prev,
17215
18056
  attachments: nextAttachments,
17216
- attachmentError: nextAttachments.length < MAX_ATTACHMENTS ? "" : prev.attachmentError
18057
+ attachmentError: nextAttachments.length < maxAttachments ? "" : prev.attachmentError
17217
18058
  };
17218
18059
  });
17219
18060
  };
@@ -17320,7 +18161,10 @@ function useChannelMessageBox({
17320
18161
  onlyAdminCanSendMessage,
17321
18162
  fileInputRef,
17322
18163
  hasUploadingAttachments,
17323
- maxAttachments: MAX_ATTACHMENTS,
18164
+ maxAttachments,
18165
+ acceptAttribute,
18166
+ attachmentsEnabled: attachmentsGate.enabled,
18167
+ attachmentsDisabledReason: attachmentsGate.disabledReason,
17324
18168
  handleInputChange,
17325
18169
  handleFileChange,
17326
18170
  handleRemoveAttachment,
@@ -17348,6 +18192,9 @@ var ChannelMessageBoxView = (props) => {
17348
18192
  fileInputRef,
17349
18193
  hasUploadingAttachments,
17350
18194
  maxAttachments,
18195
+ acceptAttribute,
18196
+ attachmentsEnabled,
18197
+ attachmentsDisabledReason,
17351
18198
  handleInputChange,
17352
18199
  handleFileChange,
17353
18200
  handleRemoveAttachment,
@@ -17445,16 +18292,23 @@ var ChannelMessageBoxView = (props) => {
17445
18292
  ),
17446
18293
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-1.5 p-1.5", children: [
17447
18294
  /* @__PURE__ */ jsxRuntime.jsx(
17448
- Button,
18295
+ AdminPermissionTooltip,
17449
18296
  {
17450
- type: "button",
17451
- variant: "ghost",
17452
- size: "icon",
17453
- className: "shrink-0 size-9 rounded-xl text-(--chat-muted) transition-all hover:bg-(--chat-hover) hover:text-(--chat-text)",
17454
- onClick: () => !hasUploadingAttachments && fileInputRef.current?.click(),
17455
- disabled: hasUploadingAttachments,
17456
- "aria-label": "Attach files",
17457
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { size: 16 })
18297
+ disabled: !attachmentsEnabled,
18298
+ message: attachmentsDisabledReason,
18299
+ children: /* @__PURE__ */ jsxRuntime.jsx(
18300
+ Button,
18301
+ {
18302
+ type: "button",
18303
+ variant: "ghost",
18304
+ size: "icon",
18305
+ className: "shrink-0 size-9 rounded-xl text-(--chat-muted) transition-all hover:bg-(--chat-hover) hover:text-(--chat-text) disabled:opacity-40",
18306
+ onClick: () => attachmentsEnabled && !hasUploadingAttachments && fileInputRef.current?.click(),
18307
+ disabled: !attachmentsEnabled || hasUploadingAttachments,
18308
+ "aria-label": "Attach files",
18309
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { size: 16 })
18310
+ }
18311
+ )
17458
18312
  }
17459
18313
  ),
17460
18314
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -17464,8 +18318,9 @@ var ChannelMessageBoxView = (props) => {
17464
18318
  ref: fileInputRef,
17465
18319
  className: "hidden",
17466
18320
  multiple: true,
17467
- accept: "image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx",
17468
- onChange: handleFileChange
18321
+ accept: acceptAttribute || void 0,
18322
+ onChange: handleFileChange,
18323
+ disabled: !attachmentsEnabled
17469
18324
  }
17470
18325
  ),
17471
18326
  /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
@@ -17544,11 +18399,11 @@ var ChannelMessageBoxView_default = ChannelMessageBoxView;
17544
18399
  // src/chat/forward-modal/useForwardModal.ts
17545
18400
  init_useStore();
17546
18401
  function useForwardModal({ isOpen, onClose, items, onForwardComplete }) {
17547
- const [search, setSearch] = React2.useState("");
17548
- const [debouncedSearch, setDebouncedSearch] = React2.useState("");
17549
- const targetChannelRef = React2.useRef(null);
17550
- const wasForwardingRef = React2.useRef(false);
17551
- const completionHandledRef = React2.useRef(false);
18402
+ const [search, setSearch] = React3.useState("");
18403
+ const [debouncedSearch, setDebouncedSearch] = React3.useState("");
18404
+ const targetChannelRef = React3.useRef(null);
18405
+ const wasForwardingRef = React3.useRef(false);
18406
+ const completionHandledRef = React3.useRef(false);
17552
18407
  const emitDmForward = exports.useChatStore((s) => s.emitDmForward);
17553
18408
  const emitGroupForward = exports.useChatStore((s) => s.emitGroupForward);
17554
18409
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
@@ -17556,23 +18411,23 @@ function useForwardModal({ isOpen, onClose, items, onForwardComplete }) {
17556
18411
  const storeConversations = exports.useChatStore((s) => s.conversations);
17557
18412
  const allUsers = exports.useChatStore((s) => s.allUsers);
17558
18413
  const sourceMessage = items[0]?.message ?? null;
17559
- const mids = React2.useMemo(
18414
+ const mids = React3.useMemo(
17560
18415
  () => items.map((item) => String(item.message._id || item.message.id || "")).filter(Boolean),
17561
18416
  [items]
17562
18417
  );
17563
18418
  const isForwarding = mids.some((id) => pendingActions[id]?.includes("forward"));
17564
- const onCloseRef = React2.useRef(onClose);
17565
- const onForwardCompleteRef = React2.useRef(onForwardComplete);
17566
- React2.useEffect(() => {
18419
+ const onCloseRef = React3.useRef(onClose);
18420
+ const onForwardCompleteRef = React3.useRef(onForwardComplete);
18421
+ React3.useEffect(() => {
17567
18422
  onCloseRef.current = onClose;
17568
18423
  }, [onClose]);
17569
- React2.useEffect(() => {
18424
+ React3.useEffect(() => {
17570
18425
  onForwardCompleteRef.current = onForwardComplete;
17571
18426
  }, [onForwardComplete]);
17572
- const handleOpenChange = React2.useCallback((open) => {
18427
+ const handleOpenChange = React3.useCallback((open) => {
17573
18428
  if (!open) onCloseRef.current();
17574
18429
  }, []);
17575
- React2.useEffect(() => {
18430
+ React3.useEffect(() => {
17576
18431
  if (isForwarding) {
17577
18432
  wasForwardingRef.current = true;
17578
18433
  const timeout = setTimeout(() => {
@@ -17590,7 +18445,7 @@ function useForwardModal({ isOpen, onClose, items, onForwardComplete }) {
17590
18445
  queueMicrotask(() => onForwardCompleteRef.current(target));
17591
18446
  }
17592
18447
  }, [isForwarding, mids]);
17593
- React2.useEffect(() => {
18448
+ React3.useEffect(() => {
17594
18449
  if (!isOpen) {
17595
18450
  setSearch("");
17596
18451
  setDebouncedSearch("");
@@ -17599,11 +18454,11 @@ function useForwardModal({ isOpen, onClose, items, onForwardComplete }) {
17599
18454
  completionHandledRef.current = false;
17600
18455
  }
17601
18456
  }, [isOpen]);
17602
- React2.useEffect(() => {
18457
+ React3.useEffect(() => {
17603
18458
  const handler = setTimeout(() => setDebouncedSearch(search), 400);
17604
18459
  return () => clearTimeout(handler);
17605
18460
  }, [search]);
17606
- const mappedChannels = React2.useMemo(() => {
18461
+ const mappedChannels = React3.useMemo(() => {
17607
18462
  const sourceConversationId = sourceMessage?.conversationId ? String(sourceMessage.conversationId) : null;
17608
18463
  const query = debouncedSearch.trim().toLowerCase();
17609
18464
  return storeConversations.filter((c) => !sourceConversationId || String(c._id) !== sourceConversationId).map((c) => {
@@ -17731,7 +18586,7 @@ function ForwardModalView(props) {
17731
18586
 
17732
18587
  // src/chat/ChatPanelAlerts.tsx
17733
18588
  init_useStore();
17734
- var ChatAlertsContext = React2.createContext({});
18589
+ var ChatAlertsContext = React3.createContext({});
17735
18590
  function ChatAlertsProvider({
17736
18591
  children,
17737
18592
  value
@@ -17739,7 +18594,7 @@ function ChatAlertsProvider({
17739
18594
  return /* @__PURE__ */ jsxRuntime.jsx(ChatAlertsContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 h-full w-full flex-1 flex-col overflow-hidden", children }) });
17740
18595
  }
17741
18596
  function useChatAlerts() {
17742
- return React2.useContext(ChatAlertsContext);
18597
+ return React3.useContext(ChatAlertsContext);
17743
18598
  }
17744
18599
  var normalizeMessage = (message) => message.trim().toLowerCase();
17745
18600
  var alertStyles = {
@@ -17754,14 +18609,14 @@ var ChatPanelAlerts = () => {
17754
18609
  const conversationErrors = exports.useChatStore((s) => s.conversationErrors);
17755
18610
  const clearAppError = exports.useChatStore((s) => s.clearAppError);
17756
18611
  const rateLimitResetAt2 = exports.useChatStore((s) => s.rateLimitResetAt);
17757
- const [now, setNow] = React2.useState(() => Date.now());
18612
+ const [now, setNow] = React3.useState(() => Date.now());
17758
18613
  const conversationAppError = activeConversationId ? conversationErrors[activeConversationId] ?? null : null;
17759
- React2.useEffect(() => {
18614
+ React3.useEffect(() => {
17760
18615
  if (rateLimitResetAt2 <= Date.now()) return;
17761
18616
  const id = window.setInterval(() => setNow(Date.now()), 1e3);
17762
18617
  return () => window.clearInterval(id);
17763
18618
  }, [rateLimitResetAt2]);
17764
- const alerts = React2.useMemo(() => {
18619
+ const alerts = React3.useMemo(() => {
17765
18620
  const items = [];
17766
18621
  const seen = /* @__PURE__ */ new Set();
17767
18622
  const push = (item) => {
@@ -18185,9 +19040,9 @@ var emitToast = (payload, onPush) => {
18185
19040
  };
18186
19041
  var ForegroundPushBridge = ({ onPush }) => {
18187
19042
  const lastDM = exports.useChatStore((s) => s.lastDM);
18188
- const onPushRef = React2.useRef(onPush);
19043
+ const onPushRef = React3.useRef(onPush);
18189
19044
  onPushRef.current = onPush;
18190
- React2.useEffect(() => {
19045
+ React3.useEffect(() => {
18191
19046
  if (!lastDM) return;
18192
19047
  if (typeof document !== "undefined" && document.visibilityState !== "visible") {
18193
19048
  return;
@@ -18227,7 +19082,7 @@ var ForegroundPushBridge = ({ onPush }) => {
18227
19082
  onPushRef.current
18228
19083
  );
18229
19084
  }, [lastDM]);
18230
- React2.useEffect(() => {
19085
+ React3.useEffect(() => {
18231
19086
  let unsubscribe = null;
18232
19087
  let retryTimer = null;
18233
19088
  let cancelled = false;
@@ -18303,7 +19158,7 @@ var ForegroundPushBridge = ({ onPush }) => {
18303
19158
  window.removeEventListener(PUSH_CHANGED_EVENT, onPushChanged);
18304
19159
  };
18305
19160
  }, []);
18306
- React2.useEffect(() => {
19161
+ React3.useEffect(() => {
18307
19162
  const onOpen = (event) => {
18308
19163
  const id = String(
18309
19164
  event.detail?.conversationId || ""
@@ -18353,6 +19208,7 @@ var ChatMain = ({
18353
19208
  queryParams,
18354
19209
  queryParamApis,
18355
19210
  queryParamsByApi,
19211
+ apiVersions: apiVersions2,
18356
19212
  themeColor = "#7494ec",
18357
19213
  theme,
18358
19214
  uiConfig,
@@ -18379,8 +19235,8 @@ var ChatMain = ({
18379
19235
  viewportHeight = "full",
18380
19236
  viewportClassName
18381
19237
  }) => {
18382
- const clientObj = React2__namespace.default.useMemo(() => ({ id: clientId }), [clientId]);
18383
- const uiConfigObj = React2__namespace.default.useMemo(
19238
+ const clientObj = React3__namespace.default.useMemo(() => ({ id: clientId }), [clientId]);
19239
+ const uiConfigObj = React3__namespace.default.useMemo(
18384
19240
  () => ({
18385
19241
  ...uiConfig || {},
18386
19242
  components: { ...uiConfig?.components, ...components },
@@ -18422,6 +19278,7 @@ var ChatMain = ({
18422
19278
  queryParams,
18423
19279
  queryParamApis,
18424
19280
  queryParamsByApi,
19281
+ apiVersions: apiVersions2,
18425
19282
  children: [
18426
19283
  /* @__PURE__ */ jsxRuntime.jsx(ForegroundPushBridge, {}),
18427
19284
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -18714,6 +19571,10 @@ var ChannelListItem_default = ChannelListItem2;
18714
19571
  var packageDefaultComponents = {
18715
19572
  ChatLayout: DefaultChatLayout_default,
18716
19573
  LoggedUserDetails: LoggedUserDetails_default,
19574
+ LoggedUserSettingsDialog: LoggedUserSettingsDialog_default,
19575
+ LoggedUserSettingsTrigger: LoggedUserSettingsTrigger_default,
19576
+ LoggedUserSettingsContent: LoggedUserSettingsContent_default,
19577
+ PushNotificationToggle,
18717
19578
  ChannelList: ChannelListView_default,
18718
19579
  ChannelListItem: DefaultChannelListItem,
18719
19580
  ChannelHeader: ChannelHeaderView_default,
@@ -18746,6 +19607,8 @@ var packageDefaultComponents = {
18746
19607
  };
18747
19608
 
18748
19609
  // src/index.ts
19610
+ init_settings_types();
19611
+ init_api_service();
18749
19612
  var index_default = ChatMain_default;
18750
19613
 
18751
19614
  exports.Calendar = Calendar;
@@ -18759,6 +19622,7 @@ exports.ChatAlertsProvider = ChatAlertsProvider;
18759
19622
  exports.ChatAvatar = ChatAvatar_default;
18760
19623
  exports.ChatCustomizationProvider = ChatCustomizationProvider;
18761
19624
  exports.ChatDateJumpMenu = ChatDateJumpMenu;
19625
+ exports.ChatEffectiveSettingsProvider = ChatEffectiveSettingsProvider;
18762
19626
  exports.ChatLayout = ChatLayout_default;
18763
19627
  exports.ChatLocaleProvider = ChatLocaleProvider;
18764
19628
  exports.ChatMain = ChatMain_default;
@@ -18771,6 +19635,9 @@ exports.ForegroundPushBridge = ForegroundPushBridge;
18771
19635
  exports.ForwardModal = ForwardModalView;
18772
19636
  exports.HeaderCallActions = HeaderCallActions_default;
18773
19637
  exports.LoggedUserDetails = LoggedUserDetails_default;
19638
+ exports.LoggedUserSettingsContent = LoggedUserSettingsContent_default;
19639
+ exports.LoggedUserSettingsDialog = LoggedUserSettingsDialog_default;
19640
+ exports.LoggedUserSettingsTrigger = LoggedUserSettingsTrigger_default;
18774
19641
  exports.MessageInput = ChannelMessageBoxView_default;
18775
19642
  exports.MessageItem = MessageItemView;
18776
19643
  exports.MessageList = ActiveChannelMessagesView_default;
@@ -18786,6 +19653,7 @@ exports.apiRegisterPushToken = apiRegisterPushToken;
18786
19653
  exports.apiRemovePushToken = apiRemovePushToken;
18787
19654
  exports.apiUpdatePushPreference = apiUpdatePushPreference;
18788
19655
  exports.buildChannelFromPushRequest = buildChannelFromPushRequest;
19656
+ exports.buildLoggedUserProfile = buildLoggedUserProfile;
18789
19657
  exports.capitalizeFirst = capitalizeFirst;
18790
19658
  exports.clampJumpDate = clampJumpDate;
18791
19659
  exports.consumePendingOpenConversation = consumePendingOpenConversation;
@@ -18834,6 +19702,8 @@ exports.useChatFeatures = useChatFeatures;
18834
19702
  exports.useChatFeaturesOptional = useChatFeaturesOptional;
18835
19703
  exports.useChatLocale = useChatLocale;
18836
19704
  exports.useChatTheme = useChatTheme;
19705
+ exports.useEffectiveSettings = useEffectiveSettings;
19706
+ exports.useEffectiveSettingsOptional = useEffectiveSettingsOptional;
18837
19707
  exports.usePushNotifications = usePushNotifications;
18838
19708
  //# sourceMappingURL=index.cjs.map
18839
19709
  //# sourceMappingURL=index.cjs.map