@realtimexsco/live-chat 1.4.15 → 1.4.17
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/PERMISSIONS.md +236 -0
- package/README.md +26 -18
- package/dist/index.cjs +118 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.mjs +117 -37
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +9 -1
- package/package.json +15 -10
package/PERMISSIONS.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Admin permissions & custom UI
|
|
2
|
+
|
|
3
|
+
How `@realtimexsco/live-chat` applies workspace **effective settings**, and how to keep the same rules when you replace UI with your own components.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
After the chat session is ready, the package fetches:
|
|
10
|
+
|
|
11
|
+
```http
|
|
12
|
+
GET /settings/effective
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Settings are stored in `ChatEffectiveSettingsProvider` and used to **gate** features in the default UI:
|
|
16
|
+
|
|
17
|
+
| Rule | Behavior |
|
|
18
|
+
|------|----------|
|
|
19
|
+
| Feature denied | Control stays **visible** |
|
|
20
|
+
| Interaction | **Disabled** |
|
|
21
|
+
| Hover | Tooltip with reason (e.g. *This feature is disabled by your administrator.*) |
|
|
22
|
+
|
|
23
|
+
Default components already honor these gates. **Custom slot components must honor them too** — the package cannot force-disable arbitrary host JSX.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## What the API controls
|
|
28
|
+
|
|
29
|
+
| API field | UI effect |
|
|
30
|
+
|-----------|-----------|
|
|
31
|
+
| `pushNotificationEnabled` | Settings → push toggle (visible, disabled + tooltip if off) |
|
|
32
|
+
| `audioCallEnabled` / `videoCallEnabled` | Header phone / video buttons |
|
|
33
|
+
| `calls.allowedByPlatform` | Checked first for calls |
|
|
34
|
+
| `calls.configured` | Calls not configured message |
|
|
35
|
+
| `calls.enabled` | Calls disabled by admin |
|
|
36
|
+
| `blockUsersEnabled` | Block user menu item |
|
|
37
|
+
| `attachments.enabled` | Paperclip / attach |
|
|
38
|
+
| `attachments.maxFileSizeMB` | Max upload size |
|
|
39
|
+
| `attachments.allowedMimeTypes` | Allowed MIME (`*` / `*/*` = all) |
|
|
40
|
+
| `attachments.maxAttachmentsPerMessage` | Max files per message |
|
|
41
|
+
| `groupCreationEnabled` | “New group” tab |
|
|
42
|
+
| `groupPolicy.defaults` / `locked` | Group permission drawer |
|
|
43
|
+
| `messageTimers.*` | Edit / delete time windows (DM vs group) |
|
|
44
|
+
| `maxAdminsPerGroup` | Max group admins |
|
|
45
|
+
| `maxParticipantsPerGroup` | Max group members |
|
|
46
|
+
| `maxPinnedMessagesPerConversation` | Max pins |
|
|
47
|
+
|
|
48
|
+
### Calls check order
|
|
49
|
+
|
|
50
|
+
1. `calls.allowedByPlatform`
|
|
51
|
+
2. `calls.configured`
|
|
52
|
+
3. `calls.enabled`
|
|
53
|
+
4. `audioCallEnabled` / `videoCallEnabled`
|
|
54
|
+
|
|
55
|
+
Same rules apply in **DM and group** chats.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Exports you need
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import {
|
|
63
|
+
useAdminFeatureGate,
|
|
64
|
+
useEffectiveSettingsOptional,
|
|
65
|
+
AdminPermissionTooltip,
|
|
66
|
+
ADMIN_PERMISSION_DENIED_MESSAGE,
|
|
67
|
+
type CallFeatureGate,
|
|
68
|
+
type AdminFeatureKey,
|
|
69
|
+
} from "@realtimexsco/live-chat";
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Feature keys (`useAdminFeatureGate`)
|
|
73
|
+
|
|
74
|
+
| Key | Gate |
|
|
75
|
+
|-----|------|
|
|
76
|
+
| `"audioCall"` | Voice call |
|
|
77
|
+
| `"videoCall"` | Video call |
|
|
78
|
+
| `"pushNotification"` | Push toggle |
|
|
79
|
+
| `"blockUsers"` | Block user |
|
|
80
|
+
| `"attachments"` | Attach files |
|
|
81
|
+
| `"groupCreation"` | Create group |
|
|
82
|
+
|
|
83
|
+
### Gate shape (`CallFeatureGate`)
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
{
|
|
87
|
+
visible: boolean; // keep UI visible
|
|
88
|
+
enabled: boolean; // allow click / toggle
|
|
89
|
+
disabledReason: string | null; // tooltip text when disabled
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Two ways to customize (important)
|
|
96
|
+
|
|
97
|
+
### Option A — Prefer small slots (recommended)
|
|
98
|
+
|
|
99
|
+
Override only the look of a control. Let the package wrapper apply gates + tooltips.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// ✅ Good — package HeaderCallActions still applies permissions
|
|
103
|
+
components: {
|
|
104
|
+
PhoneCallButton: MyPhoneButton,
|
|
105
|
+
VideoCallButton: MyVideoButton,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ❌ Avoid unless you wire gates yourself
|
|
109
|
+
components: {
|
|
110
|
+
HeaderCallActions: MyCallActions, // replaces gated wrapper
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Your button should still honor props the package passes:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
function MyVideoButton({
|
|
118
|
+
onVideoCall,
|
|
119
|
+
disabled,
|
|
120
|
+
disabledReason,
|
|
121
|
+
className,
|
|
122
|
+
}: VideoCallButtonSlotProps) {
|
|
123
|
+
return (
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
className={className}
|
|
127
|
+
disabled={disabled}
|
|
128
|
+
aria-disabled={disabled}
|
|
129
|
+
onClick={disabled ? undefined : onVideoCall}
|
|
130
|
+
>
|
|
131
|
+
Video
|
|
132
|
+
</button>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
(The package wraps this with `AdminPermissionTooltip` when used via default `HeaderCallActions`.)
|
|
138
|
+
|
|
139
|
+
### Option B — Full custom slot
|
|
140
|
+
|
|
141
|
+
If you replace a whole slot (e.g. `HeaderCallActions`, custom attach button, custom push row), **you** must read the gate and apply disable + tooltip.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import {
|
|
145
|
+
useAdminFeatureGate,
|
|
146
|
+
AdminPermissionTooltip,
|
|
147
|
+
} from "@realtimexsco/live-chat";
|
|
148
|
+
|
|
149
|
+
function MyVideoCallButton({ onVideoCall, className }) {
|
|
150
|
+
const gate = useAdminFeatureGate("videoCall");
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<AdminPermissionTooltip
|
|
154
|
+
disabled={!gate.enabled}
|
|
155
|
+
message={gate.disabledReason}
|
|
156
|
+
side="bottom"
|
|
157
|
+
>
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
className={className}
|
|
161
|
+
disabled={!gate.enabled}
|
|
162
|
+
onClick={gate.enabled ? onVideoCall : undefined}
|
|
163
|
+
>
|
|
164
|
+
Video
|
|
165
|
+
</button>
|
|
166
|
+
</AdminPermissionTooltip>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Same pattern for other features — change the key only:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
useAdminFeatureGate("audioCall");
|
|
175
|
+
useAdminFeatureGate("pushNotification");
|
|
176
|
+
useAdminFeatureGate("blockUsers");
|
|
177
|
+
useAdminFeatureGate("attachments");
|
|
178
|
+
useAdminFeatureGate("groupCreation");
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Contract for every custom permission-aware control
|
|
184
|
+
|
|
185
|
+
| Do | Don’t |
|
|
186
|
+
|----|--------|
|
|
187
|
+
| Keep the control **visible** when denied | Hide the button when permission is off |
|
|
188
|
+
| Set `disabled={!gate.enabled}` | Leave it clickable |
|
|
189
|
+
| Wrap with `AdminPermissionTooltip` | Skip the tooltip |
|
|
190
|
+
| Use `useAdminFeatureGate` / gate props | Hardcode your own permission logic |
|
|
191
|
+
| Clear `onClick` when disabled | Fire the action when disabled |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Advanced: raw settings
|
|
196
|
+
|
|
197
|
+
For limits (file size, max participants, timers, locked group policy), use:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const { settings, loading, isGroupPermissionLocked } =
|
|
201
|
+
useEffectiveSettingsOptional();
|
|
202
|
+
|
|
203
|
+
settings.attachments.maxFileSizeMB;
|
|
204
|
+
settings.maxParticipantsPerGroup;
|
|
205
|
+
settings.messageTimers.dmEditMinutes;
|
|
206
|
+
isGroupPermissionLocked("onlyAdminCanSendMessage");
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
While `loading` is `true`, show a spinner instead of flashing the wrong enabled state (same idea as the push settings row).
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Loading state
|
|
214
|
+
|
|
215
|
+
Effective settings and feature-specific APIs (e.g. push config) may load after the modal/UI opens. Recommended pattern:
|
|
216
|
+
|
|
217
|
+
1. If `loading` → show row + spinner
|
|
218
|
+
2. When ready → show control with correct enabled/disabled state
|
|
219
|
+
3. Never flash “enabled” then suddenly disable without explanation
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Checklist for host apps
|
|
224
|
+
|
|
225
|
+
- [ ] Default UI: no extra work — gates are built in
|
|
226
|
+
- [ ] Custom `PhoneCallButton` / `VideoCallButton`: honor `disabled` (+ optional `disabledReason`)
|
|
227
|
+
- [ ] Custom `HeaderCallActions` / full call UI: use `useAdminFeatureGate` + `AdminPermissionTooltip`
|
|
228
|
+
- [ ] Custom push / attach / block / create-group UI: same gate + tooltip pattern
|
|
229
|
+
- [ ] Do not override `HeaderCallActions` only for styling — override `PhoneCallButton` / `VideoCallButton` instead
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Related
|
|
234
|
+
|
|
235
|
+
- Package docs: [https://realtimex.softwareco.com/documentations/package](https://realtimex.softwareco.com/documentations/package)
|
|
236
|
+
- npm: [https://www.npmjs.com/package/@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
package/README.md
CHANGED
|
@@ -2,22 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
> Drop-in real-time chat UI for React and Next.js — direct messages, group chats, reactions, replies, forwarding, pinned/starred messages, and more. Built on Socket.IO, Zustand, Tailwind CSS v4, and Radix UI.
|
|
4
4
|
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
|
12
|
-
| **
|
|
13
|
-
| **
|
|
14
|
-
| **
|
|
15
|
-
| **
|
|
5
|
+
[npm version](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
6
|
+
[npm downloads](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
7
|
+
[license](#license)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| | |
|
|
11
|
+
| ------------ | ---------------------------------------------------------------------------------- |
|
|
12
|
+
| **Package** | `[@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat)` |
|
|
13
|
+
| **Styles** | `@realtimexsco/live-chat/styles.css` |
|
|
14
|
+
| **Requires** | React 17+, Tailwind CSS **v4**, Zustand 4+ |
|
|
15
|
+
| **Docs** | [Package documentation](https://realtimex.softwareco.com/documentations/package) |
|
|
16
|
+
| **License** | [MIT](#license) |
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
Socket.IO, REST client, Zustand store, Radix UI, emoji picker, and chat UI are **bundled**. Your app only installs peer dependencies.
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
23
|
+
|
|
24
|
+
|
|
21
25
|
## Install
|
|
22
26
|
|
|
23
27
|
```bash
|
|
@@ -49,23 +53,27 @@ export default function ChatPage() {
|
|
|
49
53
|
|
|
50
54
|
---
|
|
51
55
|
|
|
56
|
+
|
|
57
|
+
|
|
52
58
|
## Features
|
|
53
59
|
|
|
54
60
|
DMs · groups · Socket.IO · reactions · replies · forward · edit/delete · pin/star · attachments · search · typing · read receipts · light/dark · locale/timezone · full UI customization
|
|
55
61
|
|
|
56
62
|
---
|
|
57
63
|
|
|
64
|
+
|
|
65
|
+
|
|
58
66
|
## More information
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
|
|
69
|
+
| Topic | Link |
|
|
70
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
62
71
|
| Full setup & docs | [https://realtimex.softwareco.com/documentations/package](https://realtimex.softwareco.com/documentations/package) |
|
|
63
|
-
| npm package
|
|
64
|
-
| License
|
|
72
|
+
| npm package | [npmjs.com/package/@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat) |
|
|
73
|
+
| License | [MIT](#license) |
|
|
65
74
|
|
|
66
|
-
---
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
---
|
|
69
77
|
|
|
70
78
|
## License
|
|
71
79
|
|
|
@@ -77,4 +85,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
77
85
|
|
|
78
86
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
79
87
|
|
|
80
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
88
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -6906,8 +6906,21 @@ function AdminPermissionTooltip({
|
|
|
6906
6906
|
if (!disabled || !message) {
|
|
6907
6907
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
6908
6908
|
}
|
|
6909
|
+
const gatedChildren = React3__namespace.default.Children.map(children, (child) => {
|
|
6910
|
+
if (!React3__namespace.default.isValidElement(child)) return child;
|
|
6911
|
+
return React3__namespace.default.cloneElement(child, {
|
|
6912
|
+
className: cn(child.props.className, "pointer-events-none")
|
|
6913
|
+
});
|
|
6914
|
+
});
|
|
6909
6915
|
return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
6910
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6916
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6917
|
+
"span",
|
|
6918
|
+
{
|
|
6919
|
+
className: cn("inline-flex cursor-not-allowed", className),
|
|
6920
|
+
tabIndex: 0,
|
|
6921
|
+
children: gatedChildren
|
|
6922
|
+
}
|
|
6923
|
+
) }),
|
|
6911
6924
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side, className: "max-w-[240px] text-xs", children: message })
|
|
6912
6925
|
] });
|
|
6913
6926
|
}
|
|
@@ -7320,9 +7333,34 @@ var PushNotificationToggle = ({
|
|
|
7320
7333
|
children
|
|
7321
7334
|
}) => {
|
|
7322
7335
|
const push = usePushNotifications();
|
|
7323
|
-
const { getPushNotificationGate } = useEffectiveSettingsOptional();
|
|
7336
|
+
const { loading: settingsLoading, getPushNotificationGate } = useEffectiveSettingsOptional();
|
|
7324
7337
|
const adminGate = getPushNotificationGate();
|
|
7325
7338
|
const [isToggling, setIsToggling] = React3.useState(false);
|
|
7339
|
+
const isBootstrapping = push.loading || settingsLoading;
|
|
7340
|
+
if (isBootstrapping && !push.supported) {
|
|
7341
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7342
|
+
"div",
|
|
7343
|
+
{
|
|
7344
|
+
className: cn(
|
|
7345
|
+
"flex items-center justify-between gap-4 rounded-xl border border-(--chat-border) bg-(--chat-surface) px-4 py-3",
|
|
7346
|
+
className
|
|
7347
|
+
),
|
|
7348
|
+
children: [
|
|
7349
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
7350
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-(--chat-text)", children: label }),
|
|
7351
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-(--chat-muted)", children: "Loading\u2026" })
|
|
7352
|
+
] }),
|
|
7353
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7354
|
+
lucideReact.Loader2,
|
|
7355
|
+
{
|
|
7356
|
+
className: "size-5 shrink-0 animate-spin text-(--chat-theme)",
|
|
7357
|
+
"aria-label": "Loading push notification settings"
|
|
7358
|
+
}
|
|
7359
|
+
)
|
|
7360
|
+
]
|
|
7361
|
+
}
|
|
7362
|
+
);
|
|
7363
|
+
}
|
|
7326
7364
|
if (!push.supported) {
|
|
7327
7365
|
return null;
|
|
7328
7366
|
}
|
|
@@ -7330,8 +7368,8 @@ var PushNotificationToggle = ({
|
|
|
7330
7368
|
const permissionBlocked = push.permission === "denied";
|
|
7331
7369
|
const adminBlocked = !adminGate.enabled;
|
|
7332
7370
|
const notConfigured = !push.configured;
|
|
7333
|
-
const showLoader = isToggling;
|
|
7334
|
-
const isDisabled = showLoader || permissionBlocked || push.loading || adminBlocked || notConfigured;
|
|
7371
|
+
const showLoader = isToggling || isBootstrapping;
|
|
7372
|
+
const isDisabled = showLoader || permissionBlocked || push.loading || settingsLoading || adminBlocked || notConfigured;
|
|
7335
7373
|
const handleChange = async (checked) => {
|
|
7336
7374
|
if (adminBlocked || notConfigured) return;
|
|
7337
7375
|
setIsToggling(true);
|
|
@@ -9001,35 +9039,37 @@ var DefaultHeaderCallActions = ({
|
|
|
9001
9039
|
showVideoCall = true,
|
|
9002
9040
|
onPhoneCall,
|
|
9003
9041
|
onVideoCall,
|
|
9042
|
+
phoneCallDisabled = false,
|
|
9043
|
+
videoCallDisabled = false,
|
|
9044
|
+
phoneCallDisabledReason = null,
|
|
9045
|
+
videoCallDisabledReason = null,
|
|
9004
9046
|
className
|
|
9005
9047
|
}) => {
|
|
9006
|
-
const { components, slotClassName
|
|
9007
|
-
const { getAudioCallGate, getVideoCallGate } = useEffectiveSettingsOptional();
|
|
9048
|
+
const { components, slotClassName } = useChatCustomizationOptional();
|
|
9008
9049
|
const PhoneSlot = components.PhoneCallButton;
|
|
9009
9050
|
const VideoSlot = components.VideoCallButton;
|
|
9010
|
-
const hideInGroups = features.hideCallActionsInGroupChats ?? false;
|
|
9011
|
-
const audioGate = getAudioCallGate();
|
|
9012
|
-
const videoGate = getVideoCallGate();
|
|
9013
|
-
if (hideInGroups && activeChannel.isGroup) return null;
|
|
9014
9051
|
if (!showPhoneCall && !showVideoCall) return null;
|
|
9015
|
-
const phoneEnabled = showPhoneCall &&
|
|
9016
|
-
const videoEnabled = showVideoCall &&
|
|
9052
|
+
const phoneEnabled = showPhoneCall && !phoneCallDisabled;
|
|
9053
|
+
const videoEnabled = showVideoCall && !videoCallDisabled;
|
|
9017
9054
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9018
9055
|
showPhoneCall && (PhoneSlot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9019
9056
|
AdminPermissionTooltip,
|
|
9020
9057
|
{
|
|
9021
|
-
disabled:
|
|
9022
|
-
message:
|
|
9058
|
+
disabled: phoneCallDisabled,
|
|
9059
|
+
message: phoneCallDisabledReason,
|
|
9060
|
+
side: "bottom",
|
|
9023
9061
|
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9024
9062
|
PhoneSlot,
|
|
9025
9063
|
{
|
|
9026
9064
|
activeChannel,
|
|
9027
9065
|
conversationId,
|
|
9028
9066
|
onPhoneCall: phoneEnabled ? onPhoneCall : void 0,
|
|
9067
|
+
disabled: phoneCallDisabled,
|
|
9068
|
+
disabledReason: phoneCallDisabledReason,
|
|
9029
9069
|
className: cn(
|
|
9030
9070
|
className,
|
|
9031
9071
|
slotClassName("phoneCallButton"),
|
|
9032
|
-
|
|
9072
|
+
phoneCallDisabled && "opacity-40"
|
|
9033
9073
|
)
|
|
9034
9074
|
}
|
|
9035
9075
|
) })
|
|
@@ -9037,8 +9077,9 @@ var DefaultHeaderCallActions = ({
|
|
|
9037
9077
|
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9038
9078
|
AdminPermissionTooltip,
|
|
9039
9079
|
{
|
|
9040
|
-
disabled:
|
|
9041
|
-
message:
|
|
9080
|
+
disabled: phoneCallDisabled,
|
|
9081
|
+
message: phoneCallDisabledReason,
|
|
9082
|
+
side: "bottom",
|
|
9042
9083
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9043
9084
|
Button,
|
|
9044
9085
|
{
|
|
@@ -9046,7 +9087,7 @@ var DefaultHeaderCallActions = ({
|
|
|
9046
9087
|
size: "icon",
|
|
9047
9088
|
className: cn(actionBtnClass, className, slotClassName("phoneCallButton")),
|
|
9048
9089
|
"aria-label": "Voice call",
|
|
9049
|
-
disabled:
|
|
9090
|
+
disabled: phoneCallDisabled,
|
|
9050
9091
|
onClick: phoneEnabled ? onPhoneCall : void 0,
|
|
9051
9092
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { size: 15 })
|
|
9052
9093
|
}
|
|
@@ -9056,18 +9097,21 @@ var DefaultHeaderCallActions = ({
|
|
|
9056
9097
|
showVideoCall && (VideoSlot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9057
9098
|
AdminPermissionTooltip,
|
|
9058
9099
|
{
|
|
9059
|
-
disabled:
|
|
9060
|
-
message:
|
|
9100
|
+
disabled: videoCallDisabled,
|
|
9101
|
+
message: videoCallDisabledReason,
|
|
9102
|
+
side: "bottom",
|
|
9061
9103
|
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9062
9104
|
VideoSlot,
|
|
9063
9105
|
{
|
|
9064
9106
|
activeChannel,
|
|
9065
9107
|
conversationId,
|
|
9066
9108
|
onVideoCall: videoEnabled ? onVideoCall : void 0,
|
|
9109
|
+
disabled: videoCallDisabled,
|
|
9110
|
+
disabledReason: videoCallDisabledReason,
|
|
9067
9111
|
className: cn(
|
|
9068
9112
|
className,
|
|
9069
9113
|
slotClassName("videoCallButton"),
|
|
9070
|
-
|
|
9114
|
+
videoCallDisabled && "opacity-40"
|
|
9071
9115
|
)
|
|
9072
9116
|
}
|
|
9073
9117
|
) })
|
|
@@ -9075,8 +9119,9 @@ var DefaultHeaderCallActions = ({
|
|
|
9075
9119
|
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9076
9120
|
AdminPermissionTooltip,
|
|
9077
9121
|
{
|
|
9078
|
-
disabled:
|
|
9079
|
-
message:
|
|
9122
|
+
disabled: videoCallDisabled,
|
|
9123
|
+
message: videoCallDisabledReason,
|
|
9124
|
+
side: "bottom",
|
|
9080
9125
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9081
9126
|
Button,
|
|
9082
9127
|
{
|
|
@@ -9084,7 +9129,7 @@ var DefaultHeaderCallActions = ({
|
|
|
9084
9129
|
size: "icon",
|
|
9085
9130
|
className: cn(actionBtnClass, className, slotClassName("videoCallButton")),
|
|
9086
9131
|
"aria-label": "Video call",
|
|
9087
|
-
disabled:
|
|
9132
|
+
disabled: videoCallDisabled,
|
|
9088
9133
|
onClick: videoEnabled ? onVideoCall : void 0,
|
|
9089
9134
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 15 })
|
|
9090
9135
|
}
|
|
@@ -9094,16 +9139,28 @@ var DefaultHeaderCallActions = ({
|
|
|
9094
9139
|
] });
|
|
9095
9140
|
};
|
|
9096
9141
|
var HeaderCallActions = (props) => {
|
|
9097
|
-
const { components, slotClassName
|
|
9142
|
+
const { components, slotClassName } = useChatCustomizationOptional();
|
|
9143
|
+
const { getAudioCallGate, getVideoCallGate } = useEffectiveSettingsOptional();
|
|
9098
9144
|
const Custom = components.HeaderCallActions;
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9145
|
+
const showPhoneCall = props.showPhoneCall ?? true;
|
|
9146
|
+
const showVideoCall = props.showVideoCall ?? true;
|
|
9147
|
+
const audioGate = getAudioCallGate();
|
|
9148
|
+
const videoGate = getVideoCallGate();
|
|
9149
|
+
if (!showPhoneCall && !showVideoCall) return null;
|
|
9150
|
+
const gatedProps = {
|
|
9151
|
+
...props,
|
|
9152
|
+
showPhoneCall,
|
|
9153
|
+
showVideoCall,
|
|
9154
|
+
onPhoneCall: audioGate.enabled ? props.onPhoneCall : void 0,
|
|
9155
|
+
onVideoCall: videoGate.enabled ? props.onVideoCall : void 0,
|
|
9156
|
+
phoneCallDisabled: !audioGate.enabled,
|
|
9157
|
+
videoCallDisabled: !videoGate.enabled,
|
|
9158
|
+
phoneCallDisabledReason: audioGate.disabledReason,
|
|
9159
|
+
videoCallDisabledReason: videoGate.disabledReason
|
|
9160
|
+
};
|
|
9161
|
+
const hasButtonSlots = components.PhoneCallButton != null || components.VideoCallButton != null;
|
|
9162
|
+
const useCustom = Custom != null && Custom !== HeaderCallActions && !hasButtonSlots;
|
|
9163
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center gap-0.5", slotClassName("headerCallActions")), children: useCustom ? /* @__PURE__ */ jsxRuntime.jsx(Custom, { ...gatedProps }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultHeaderCallActions, { ...gatedProps }) });
|
|
9107
9164
|
};
|
|
9108
9165
|
var HeaderCallActions_default = HeaderCallActions;
|
|
9109
9166
|
function OptionsMenuItem({
|
|
@@ -13501,8 +13558,8 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13501
13558
|
{ key: "onlyAdminCanSendMessage", label: "Only Admin Can Send Message", desc: "Restricts message sending to group administrators only." },
|
|
13502
13559
|
{ key: "onlyAdminCanEditInfo", label: "Only Admin Can Edit Info", desc: "Only admins can change group name, description, and avatar." },
|
|
13503
13560
|
{ key: "senderCanEditMessage", label: "Sender Can Edit Message", desc: "Allows users to edit their own sent messages." },
|
|
13504
|
-
{ key: "allowMemberAdd", label: "
|
|
13505
|
-
{ key: "allowMemberRemove", label: "
|
|
13561
|
+
{ key: "allowMemberAdd", label: "Allow Members to Add", desc: "Allows group members to add new participants." },
|
|
13562
|
+
{ key: "allowMemberRemove", label: "Allow Members to Remove", desc: "Allows group members to remove other participants." },
|
|
13506
13563
|
{ key: "moderationEnabled", label: "Moderation Enabled", desc: "Enables advanced message moderation features." }
|
|
13507
13564
|
];
|
|
13508
13565
|
return /* @__PURE__ */ jsxRuntime.jsx(Sheet, { open: isOpen, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ChatSheetContent, { showCloseButton: true, className: "sm:max-w-[380px]", children: [
|
|
@@ -13518,7 +13575,7 @@ var PermissionDrawer = ({ isOpen, onOpenChange, conversationId, conversationInfo
|
|
|
13518
13575
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1 px-0.5 text-[10px] font-semibold uppercase tracking-wide text-(--chat-muted)", children: "Permissions" }),
|
|
13519
13576
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-(--chat-border) px-3", children: permissionItems.map((item, index) => {
|
|
13520
13577
|
const locked = isGroupPermissionLocked(item.key);
|
|
13521
|
-
const checked =
|
|
13578
|
+
const checked = permissions[item.key];
|
|
13522
13579
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13523
13580
|
"div",
|
|
13524
13581
|
{
|
|
@@ -19699,11 +19756,35 @@ var packageDefaultComponents = {
|
|
|
19699
19756
|
ChatSocketStatus: ChatSocketStatus_default
|
|
19700
19757
|
};
|
|
19701
19758
|
|
|
19759
|
+
// src/hooks/useAdminFeatureGate.ts
|
|
19760
|
+
function useAdminFeatureGate(feature) {
|
|
19761
|
+
const ctx = useEffectiveSettingsOptional();
|
|
19762
|
+
switch (feature) {
|
|
19763
|
+
case "audioCall":
|
|
19764
|
+
return ctx.getAudioCallGate();
|
|
19765
|
+
case "videoCall":
|
|
19766
|
+
return ctx.getVideoCallGate();
|
|
19767
|
+
case "pushNotification":
|
|
19768
|
+
return ctx.getPushNotificationGate();
|
|
19769
|
+
case "blockUsers":
|
|
19770
|
+
return ctx.getBlockUsersGate();
|
|
19771
|
+
case "attachments":
|
|
19772
|
+
return ctx.getAttachmentsGate();
|
|
19773
|
+
case "groupCreation":
|
|
19774
|
+
return ctx.getGroupCreationGate();
|
|
19775
|
+
default: {
|
|
19776
|
+
const _exhaustive = feature;
|
|
19777
|
+
return _exhaustive;
|
|
19778
|
+
}
|
|
19779
|
+
}
|
|
19780
|
+
}
|
|
19781
|
+
|
|
19702
19782
|
// src/index.ts
|
|
19703
19783
|
init_settings_types();
|
|
19704
19784
|
init_api_service();
|
|
19705
19785
|
var index_default = ChatMain_default;
|
|
19706
19786
|
|
|
19787
|
+
exports.AdminPermissionTooltip = AdminPermissionTooltip;
|
|
19707
19788
|
exports.Calendar = Calendar;
|
|
19708
19789
|
exports.CalendarDayButton = CalendarDayButton;
|
|
19709
19790
|
exports.Channel = Channel;
|
|
@@ -19786,6 +19867,7 @@ exports.smoothScrollToMessage = smoothScrollToMessage;
|
|
|
19786
19867
|
exports.startOfLocalDay = startOfLocalDay;
|
|
19787
19868
|
exports.toDateInputValue = toDateInputValue;
|
|
19788
19869
|
exports.uniqueList = uniqueList;
|
|
19870
|
+
exports.useAdminFeatureGate = useAdminFeatureGate;
|
|
19789
19871
|
exports.useChatAlerts = useChatAlerts;
|
|
19790
19872
|
exports.useChatContext = useChatContext;
|
|
19791
19873
|
exports.useChatController = useChatController;
|