@relaya-chat/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +215 -0
- package/dist/AdminPanel-CBs17LkG.js +3159 -0
- package/dist/AdminPanel-DqI4RFt9.cjs +1 -0
- package/dist/admin.cjs +1 -0
- package/dist/admin.js +2 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +3224 -0
- package/dist/relaya.css +2 -0
- package/package.json +54 -0
|
@@ -0,0 +1,3159 @@
|
|
|
1
|
+
import { useCallback as e, useEffect as t, useRef as n, useState as r } from "react";
|
|
2
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
3
|
+
//#region ../core/dist/types.js
|
|
4
|
+
var s = {
|
|
5
|
+
READ: "chat.read",
|
|
6
|
+
POST: "chat.post",
|
|
7
|
+
EDIT_OWN: "chat.edit_own",
|
|
8
|
+
DELETE_OWN: "chat.delete_own",
|
|
9
|
+
DELETE_ANY: "chat.delete_any",
|
|
10
|
+
REPORT: "chat.report",
|
|
11
|
+
BAN_USER: "chat.ban_user",
|
|
12
|
+
MANAGE_ROLES: "chat.manage_roles",
|
|
13
|
+
MENTION_CHANNEL: "chat.mention_channel"
|
|
14
|
+
}, c = class {
|
|
15
|
+
baseUrl;
|
|
16
|
+
getToken;
|
|
17
|
+
constructor(e, t) {
|
|
18
|
+
this.baseUrl = e, this.getToken = t;
|
|
19
|
+
}
|
|
20
|
+
authHeaders() {
|
|
21
|
+
let e = this.getToken();
|
|
22
|
+
return e ? { Authorization: `Bearer ${e}` } : {};
|
|
23
|
+
}
|
|
24
|
+
async request(e, t, n) {
|
|
25
|
+
let r = `${this.baseUrl}${t}`, i = { ...this.authHeaders() };
|
|
26
|
+
n !== void 0 && (i["Content-Type"] = "application/json");
|
|
27
|
+
let a = await fetch(r, {
|
|
28
|
+
method: e,
|
|
29
|
+
headers: i,
|
|
30
|
+
body: n === void 0 ? void 0 : JSON.stringify(n)
|
|
31
|
+
});
|
|
32
|
+
if (a.status !== 204) {
|
|
33
|
+
if (!a.ok) {
|
|
34
|
+
let e = {};
|
|
35
|
+
try {
|
|
36
|
+
e = await a.json();
|
|
37
|
+
} catch {}
|
|
38
|
+
let t = e?.error;
|
|
39
|
+
throw {
|
|
40
|
+
status: a.status,
|
|
41
|
+
code: (typeof t == "object" ? t?.code : void 0) ?? "API_ERROR",
|
|
42
|
+
message: (typeof t == "object" ? t?.message : t) ?? a.statusText
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return a.json();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async parseResponse(e) {
|
|
49
|
+
if (e.status !== 204) {
|
|
50
|
+
if (!e.ok) {
|
|
51
|
+
let t = {};
|
|
52
|
+
try {
|
|
53
|
+
t = await e.json();
|
|
54
|
+
} catch {}
|
|
55
|
+
let n = t?.error;
|
|
56
|
+
throw {
|
|
57
|
+
status: e.status,
|
|
58
|
+
code: (typeof n == "object" ? n?.code : void 0) ?? "API_ERROR",
|
|
59
|
+
message: (typeof n == "object" ? n?.message : n) ?? e.statusText
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return e.json();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async login(e, t) {
|
|
66
|
+
return this.request("POST", "/auth/login", {
|
|
67
|
+
email: e,
|
|
68
|
+
stationSlug: t
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async requestCode(e, t) {
|
|
72
|
+
return this.request("POST", "/auth/request-code", {
|
|
73
|
+
email: e,
|
|
74
|
+
stationSlug: t
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async verifyCode(e, t, n) {
|
|
78
|
+
return this.request("POST", "/auth/verify-code", {
|
|
79
|
+
pendingId: e,
|
|
80
|
+
code: t,
|
|
81
|
+
stationSlug: n
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async verify(e, t) {
|
|
85
|
+
return this.request("GET", `/auth/verify?token=${encodeURIComponent(e)}&station=${encodeURIComponent(t)}`);
|
|
86
|
+
}
|
|
87
|
+
async refresh(e) {
|
|
88
|
+
return this.request("POST", "/auth/refresh", { refreshToken: e });
|
|
89
|
+
}
|
|
90
|
+
async getStation(e) {
|
|
91
|
+
return this.request("GET", `/api/chat/stations/${encodeURIComponent(e)}`);
|
|
92
|
+
}
|
|
93
|
+
async getMessages(e, t) {
|
|
94
|
+
let n = new URLSearchParams();
|
|
95
|
+
t?.before && n.set("before", t.before), t?.after && n.set("after", t.after), t?.limit !== void 0 && n.set("limit", String(t.limit));
|
|
96
|
+
let r = n.toString() ? `?${n.toString()}` : "";
|
|
97
|
+
return this.request("GET", `/api/chat/${e}/messages${r}`);
|
|
98
|
+
}
|
|
99
|
+
async deleteMessage(e, t) {
|
|
100
|
+
return this.request("DELETE", `/api/chat/${e}/messages/${t}`);
|
|
101
|
+
}
|
|
102
|
+
async editMessage(e, t, n) {
|
|
103
|
+
return this.request("PATCH", `/api/chat/${e}/messages/${t}`, { content: n });
|
|
104
|
+
}
|
|
105
|
+
async createReport(e, t, n, r) {
|
|
106
|
+
return this.request("POST", `/api/chat/${e}/messages/${t}/report`, {
|
|
107
|
+
reason: n,
|
|
108
|
+
details: r
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async getReports(e, t) {
|
|
112
|
+
let n = new URLSearchParams();
|
|
113
|
+
t?.status && n.set("status", t.status), t?.limit !== void 0 && n.set("limit", String(t.limit)), t?.offset !== void 0 && n.set("offset", String(t.offset));
|
|
114
|
+
let r = n.toString() ? `?${n.toString()}` : "";
|
|
115
|
+
return this.request("GET", `/api/chat/${e}/reports${r}`);
|
|
116
|
+
}
|
|
117
|
+
async updateReport(e, t, n) {
|
|
118
|
+
return this.request("PATCH", `/api/chat/${e}/reports/${t}`, n);
|
|
119
|
+
}
|
|
120
|
+
async createBan(e, t, n) {
|
|
121
|
+
return this.request("POST", `/api/chat/${e}/bans`, {
|
|
122
|
+
userId: t,
|
|
123
|
+
...n
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async liftBan(e, t) {
|
|
127
|
+
return this.request("DELETE", `/api/chat/${e}/bans/${t}`);
|
|
128
|
+
}
|
|
129
|
+
async getBans(e, t = !0) {
|
|
130
|
+
let n = t ? "?active=true" : "";
|
|
131
|
+
return this.request("GET", `/api/chat/${e}/bans${n}`);
|
|
132
|
+
}
|
|
133
|
+
async getMembers(e) {
|
|
134
|
+
return this.request("GET", `/api/chat/${e}/members`);
|
|
135
|
+
}
|
|
136
|
+
async getMembersAdmin(e) {
|
|
137
|
+
return this.request("GET", `/api/chat/${e}/members/admin`);
|
|
138
|
+
}
|
|
139
|
+
async updateMemberRole(e, t, n) {
|
|
140
|
+
return this.request("PATCH", `/api/chat/${e}/members/${t}/roles`, { roleId: n });
|
|
141
|
+
}
|
|
142
|
+
async patchMemberRoles(e, t, n) {
|
|
143
|
+
return this.request("PATCH", `/api/chat/${e}/members/${t}/roles`, n);
|
|
144
|
+
}
|
|
145
|
+
async getMe(e) {
|
|
146
|
+
return this.request("GET", `/api/chat/${e}/me`);
|
|
147
|
+
}
|
|
148
|
+
async updateChatName(e, t) {
|
|
149
|
+
return this.request("PATCH", `/api/chat/${e}/me`, { chatName: t });
|
|
150
|
+
}
|
|
151
|
+
async getModerationConfig(e) {
|
|
152
|
+
return this.request("GET", `/api/chat/${e}/moderation/config`);
|
|
153
|
+
}
|
|
154
|
+
async updateModerationConfig(e, t) {
|
|
155
|
+
return this.request("PATCH", `/api/chat/${e}/moderation/config`, t);
|
|
156
|
+
}
|
|
157
|
+
async getPresenceConfig(e) {
|
|
158
|
+
return this.request("GET", `/api/chat/${e}/presence/config`);
|
|
159
|
+
}
|
|
160
|
+
async updatePresenceConfig(e, t) {
|
|
161
|
+
return this.request("PATCH", `/api/chat/${e}/presence/config`, t);
|
|
162
|
+
}
|
|
163
|
+
async getStickers(e) {
|
|
164
|
+
let t = await this.request("GET", `/api/chat/${e}/stickers`);
|
|
165
|
+
return this.baseUrl && (t.stickers = t.stickers.map((e) => e.url.startsWith("/") ? {
|
|
166
|
+
...e,
|
|
167
|
+
url: `${this.baseUrl}${e.url}`
|
|
168
|
+
} : e)), t;
|
|
169
|
+
}
|
|
170
|
+
async uploadSticker(e, t, n) {
|
|
171
|
+
let r = await fetch(`${this.baseUrl}/api/chat/${e}/stickers/upload`, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: {
|
|
174
|
+
...this.authHeaders(),
|
|
175
|
+
"Content-Type": t.type || "application/octet-stream",
|
|
176
|
+
"X-Sticker-Filename": encodeURIComponent(n)
|
|
177
|
+
},
|
|
178
|
+
body: t
|
|
179
|
+
});
|
|
180
|
+
return this.parseResponse(r);
|
|
181
|
+
}
|
|
182
|
+
async updateStickerManifest(e, t) {
|
|
183
|
+
return this.request("PUT", `/api/chat/${e}/stickers/manifest`, { stickers: t });
|
|
184
|
+
}
|
|
185
|
+
async deleteSticker(e, t) {
|
|
186
|
+
return this.request("DELETE", `/api/chat/${e}/stickers/${encodeURIComponent(t)}`);
|
|
187
|
+
}
|
|
188
|
+
async getSounds(e) {
|
|
189
|
+
return this.request("GET", `/api/chat/${encodeURIComponent(e)}/sounds`);
|
|
190
|
+
}
|
|
191
|
+
async getSpaceTheme(e) {
|
|
192
|
+
return this.request("GET", `/api/chat/${e}/theme`);
|
|
193
|
+
}
|
|
194
|
+
async saveSpaceTheme(e, t) {
|
|
195
|
+
return this.request("PUT", `/api/chat/${e}/theme`, t);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/config.ts
|
|
200
|
+
function l() {
|
|
201
|
+
let e = new URLSearchParams(window.location.search), t = e.get("space") ?? e.get("station") ?? "", n = e.get("theme");
|
|
202
|
+
return {
|
|
203
|
+
spaceSlug: t,
|
|
204
|
+
theme: n === "dark" ? "dark" : n === "light" ? "light" : window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light",
|
|
205
|
+
magicLinkToken: e.get("token"),
|
|
206
|
+
embed: e.get("embed") === "true",
|
|
207
|
+
admin: e.get("admin") === "true",
|
|
208
|
+
managed: e.get("managed") === "host"
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
var u = typeof window < "u" ? l() : {
|
|
212
|
+
spaceSlug: "",
|
|
213
|
+
theme: "light",
|
|
214
|
+
magicLinkToken: null,
|
|
215
|
+
embed: !1,
|
|
216
|
+
admin: !1,
|
|
217
|
+
managed: !1
|
|
218
|
+
};
|
|
219
|
+
function d() {
|
|
220
|
+
let e = new URL(window.location.href);
|
|
221
|
+
e.searchParams.delete("token"), window.history.replaceState({}, "", e.toString());
|
|
222
|
+
}
|
|
223
|
+
function f(e, t) {
|
|
224
|
+
let n = window.location.protocol === "https:" ? "wss:" : "ws:", r = t ? `token=${encodeURIComponent(t)}&` : "";
|
|
225
|
+
return `${n}//${window.location.port === "5173" && window.location.hostname !== "localhost" ? `${window.location.hostname}:9000` : window.location.host}/ws?${r}station=${encodeURIComponent(e)}`;
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/hooks/useRelayaAuth.ts
|
|
229
|
+
var p = "relaya_refresh_token";
|
|
230
|
+
function m(e) {
|
|
231
|
+
try {
|
|
232
|
+
localStorage.setItem(p, e);
|
|
233
|
+
} catch {}
|
|
234
|
+
}
|
|
235
|
+
function h() {
|
|
236
|
+
try {
|
|
237
|
+
return localStorage.getItem(p);
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function g() {
|
|
243
|
+
try {
|
|
244
|
+
localStorage.removeItem(p);
|
|
245
|
+
} catch {}
|
|
246
|
+
}
|
|
247
|
+
function _(e) {
|
|
248
|
+
try {
|
|
249
|
+
let [, t] = e.split("."), n = atob(t.replace(/-/g, "+").replace(/_/g, "/"));
|
|
250
|
+
return JSON.parse(n).exp ?? null;
|
|
251
|
+
} catch {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
var v = null, y = /* @__PURE__ */ new Map();
|
|
256
|
+
function b(e, t) {
|
|
257
|
+
let n = `${t}:${e}`, r = y.get(n);
|
|
258
|
+
if (r) return r;
|
|
259
|
+
let i = fetch(`/auth/verify?token=${encodeURIComponent(e)}&station=${encodeURIComponent(t)}`).then((e) => e.ok ? e.json() : Promise.reject());
|
|
260
|
+
return y.set(n, i), i.finally(() => {
|
|
261
|
+
window.setTimeout(() => {
|
|
262
|
+
y.delete(n);
|
|
263
|
+
}, 300 * 1e3);
|
|
264
|
+
}).catch(() => {}), i;
|
|
265
|
+
}
|
|
266
|
+
function x(i = {}) {
|
|
267
|
+
let a = i.spaceSlug ?? u.spaceSlug, o = i.initialToken ?? null, s = i.manageOwnRefreshToken ?? !0, l = n(i.onSessionEnded);
|
|
268
|
+
l.current = i.onSessionEnded;
|
|
269
|
+
let [d, f] = r({
|
|
270
|
+
status: "loading",
|
|
271
|
+
user: null,
|
|
272
|
+
token: null,
|
|
273
|
+
station: null,
|
|
274
|
+
stationSlug: a,
|
|
275
|
+
error: null
|
|
276
|
+
}), p = n(null), y = e(() => p.current, []), x = n(null), S = n(null), C = n(new c("", y)).current, w = n(!1), T = e(() => {
|
|
277
|
+
p.current = null, x.current = null, s && g(), S.current !== null && (clearTimeout(S.current), S.current = null), f((e) => ({
|
|
278
|
+
...e,
|
|
279
|
+
status: "anonymous",
|
|
280
|
+
token: null,
|
|
281
|
+
user: null,
|
|
282
|
+
station: null,
|
|
283
|
+
stationSlug: a
|
|
284
|
+
}));
|
|
285
|
+
}, [a, s]), E = e((e) => {
|
|
286
|
+
S.current !== null && (clearTimeout(S.current), S.current = null);
|
|
287
|
+
let t = _(e);
|
|
288
|
+
if (!t) return;
|
|
289
|
+
let n = Math.max(0, t * 1e3 - Date.now() - 120 * 1e3);
|
|
290
|
+
S.current = setTimeout(() => {
|
|
291
|
+
let e = x.current;
|
|
292
|
+
e && (v ||= C.refresh(e).finally(() => {
|
|
293
|
+
v = null;
|
|
294
|
+
}), v.then((e) => {
|
|
295
|
+
p.current = e.accessToken, x.current = e.refreshToken, s && m(e.refreshToken), f((t) => t.status === "authenticated" ? {
|
|
296
|
+
...t,
|
|
297
|
+
token: e.accessToken
|
|
298
|
+
} : t), E(e.accessToken);
|
|
299
|
+
}).catch((e) => {
|
|
300
|
+
typeof e == "object" && e && "status" in e && (e.status === 401 || e.status === 403) ? (T(), l.current?.("refresh-failed")) : setTimeout(() => {
|
|
301
|
+
let e = x.current;
|
|
302
|
+
e && C.refresh(e).then((e) => {
|
|
303
|
+
p.current = e.accessToken, x.current = e.refreshToken, s && m(e.refreshToken), f((t) => t.status === "authenticated" ? {
|
|
304
|
+
...t,
|
|
305
|
+
token: e.accessToken
|
|
306
|
+
} : t), E(e.accessToken);
|
|
307
|
+
}).catch(() => {
|
|
308
|
+
T(), l.current?.("refresh-failed");
|
|
309
|
+
});
|
|
310
|
+
}, 1e4);
|
|
311
|
+
}));
|
|
312
|
+
}, n);
|
|
313
|
+
}, [
|
|
314
|
+
C,
|
|
315
|
+
T,
|
|
316
|
+
s
|
|
317
|
+
]), D = e(async (e, t) => {
|
|
318
|
+
p.current = e, x.current = t, s && m(t), E(e);
|
|
319
|
+
try {
|
|
320
|
+
let t = await C.getMe(a).catch((e) => {
|
|
321
|
+
throw process.env.NODE_ENV !== "production" && console.warn("[RelayaAuth] getMe failed during token apply", {
|
|
322
|
+
spaceSlug: a,
|
|
323
|
+
err: e
|
|
324
|
+
}), e;
|
|
325
|
+
}), n = await C.getStation(a).catch((e) => {
|
|
326
|
+
throw process.env.NODE_ENV !== "production" && console.warn("[RelayaAuth] getStation failed during token apply", {
|
|
327
|
+
spaceSlug: a,
|
|
328
|
+
err: e
|
|
329
|
+
}), e;
|
|
330
|
+
});
|
|
331
|
+
f({
|
|
332
|
+
status: "authenticated",
|
|
333
|
+
user: {
|
|
334
|
+
id: t.userId,
|
|
335
|
+
displayName: t.displayName,
|
|
336
|
+
avatarUrl: null,
|
|
337
|
+
permissions: t.permissions,
|
|
338
|
+
roles: t.roles,
|
|
339
|
+
chatName: t.chatName
|
|
340
|
+
},
|
|
341
|
+
token: e,
|
|
342
|
+
station: {
|
|
343
|
+
id: n.id,
|
|
344
|
+
name: n.name,
|
|
345
|
+
slug: n.slug
|
|
346
|
+
},
|
|
347
|
+
stationSlug: n.slug,
|
|
348
|
+
error: null
|
|
349
|
+
});
|
|
350
|
+
} catch (e) {
|
|
351
|
+
process.env.NODE_ENV !== "production" && console.warn("[RelayaAuth] Falling back to anonymous after token apply failure", {
|
|
352
|
+
spaceSlug: a,
|
|
353
|
+
err: e
|
|
354
|
+
}), T();
|
|
355
|
+
}
|
|
356
|
+
}, [
|
|
357
|
+
C,
|
|
358
|
+
a,
|
|
359
|
+
E,
|
|
360
|
+
T,
|
|
361
|
+
s
|
|
362
|
+
]), O = e((e) => {
|
|
363
|
+
D(e.accessToken, e.refreshToken);
|
|
364
|
+
}, [D]);
|
|
365
|
+
t(() => {
|
|
366
|
+
if (w.current) return;
|
|
367
|
+
w.current = !0;
|
|
368
|
+
let e = new URLSearchParams(window.location.search), t = e.get("token"), n = o ?? t, r = e.get("station") || a;
|
|
369
|
+
if (n) {
|
|
370
|
+
if (t) {
|
|
371
|
+
let e = new URL(window.location.href);
|
|
372
|
+
e.searchParams.delete("token"), window.history.replaceState({}, "", e.toString());
|
|
373
|
+
}
|
|
374
|
+
b(n, r).then((e) => {
|
|
375
|
+
D(e.accessToken, e.refreshToken);
|
|
376
|
+
}).catch(() => T());
|
|
377
|
+
} else if (s) {
|
|
378
|
+
let e = h();
|
|
379
|
+
e ? (v ||= C.refresh(e).finally(() => {
|
|
380
|
+
v = null;
|
|
381
|
+
}), v.then((e) => {
|
|
382
|
+
D(e.accessToken, e.refreshToken);
|
|
383
|
+
}).catch((e) => {
|
|
384
|
+
typeof e == "object" && e && "status" in e && (e.status === 401 || e.status === 403) ? (g(), f((e) => ({
|
|
385
|
+
...e,
|
|
386
|
+
status: "anonymous",
|
|
387
|
+
stationSlug: a
|
|
388
|
+
}))) : setTimeout(() => {
|
|
389
|
+
let e = h();
|
|
390
|
+
if (!e) {
|
|
391
|
+
f((e) => ({
|
|
392
|
+
...e,
|
|
393
|
+
status: "anonymous",
|
|
394
|
+
stationSlug: a
|
|
395
|
+
}));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
C.refresh(e).then((e) => void D(e.accessToken, e.refreshToken)).catch(() => {
|
|
399
|
+
g(), f((e) => ({
|
|
400
|
+
...e,
|
|
401
|
+
status: "anonymous",
|
|
402
|
+
stationSlug: a
|
|
403
|
+
}));
|
|
404
|
+
});
|
|
405
|
+
}, 1e4);
|
|
406
|
+
})) : f((e) => ({
|
|
407
|
+
...e,
|
|
408
|
+
status: "anonymous",
|
|
409
|
+
stationSlug: a
|
|
410
|
+
}));
|
|
411
|
+
} else f((e) => ({
|
|
412
|
+
...e,
|
|
413
|
+
status: "anonymous",
|
|
414
|
+
stationSlug: a
|
|
415
|
+
}));
|
|
416
|
+
return () => {
|
|
417
|
+
S.current !== null && clearTimeout(S.current);
|
|
418
|
+
};
|
|
419
|
+
}, []);
|
|
420
|
+
let k = e(async (e) => {
|
|
421
|
+
f((e) => ({
|
|
422
|
+
...e,
|
|
423
|
+
error: null
|
|
424
|
+
}));
|
|
425
|
+
let t = `${window.location.origin}/auth/popup?station=${encodeURIComponent(a)}`;
|
|
426
|
+
if (!window.open(t, "relaya-auth", "width=480,height=600,left=200,top=100")) {
|
|
427
|
+
f((e) => ({
|
|
428
|
+
...e,
|
|
429
|
+
error: "Please allow popups for this site to sign in."
|
|
430
|
+
}));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
let n = (e) => {
|
|
434
|
+
if (e.origin !== window.location.origin || e.data?.type !== "relaya:auth") return;
|
|
435
|
+
window.removeEventListener("message", n);
|
|
436
|
+
let { accessToken: t, refreshToken: r } = e.data;
|
|
437
|
+
D(t, r);
|
|
438
|
+
};
|
|
439
|
+
window.addEventListener("message", n);
|
|
440
|
+
}, [D, a]), A = e(() => {
|
|
441
|
+
let e = x.current ?? (s ? h() : null);
|
|
442
|
+
T(), e && fetch("/auth/logout", {
|
|
443
|
+
method: "POST",
|
|
444
|
+
headers: { "Content-Type": "application/json" },
|
|
445
|
+
body: JSON.stringify({ refreshToken: e })
|
|
446
|
+
}).catch(() => {}), l.current?.("logout");
|
|
447
|
+
}, [T, s]);
|
|
448
|
+
return {
|
|
449
|
+
...d,
|
|
450
|
+
login: k,
|
|
451
|
+
logout: A,
|
|
452
|
+
getToken: y,
|
|
453
|
+
onOtpVerified: O
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/hooks/useSpaceTheme.ts
|
|
458
|
+
var S = [
|
|
459
|
+
"--relaya-color-bg",
|
|
460
|
+
"--relaya-color-message-bg",
|
|
461
|
+
"--relaya-color-message-own-bg",
|
|
462
|
+
"--relaya-color-text",
|
|
463
|
+
"--relaya-color-text-secondary",
|
|
464
|
+
"--relaya-color-input-bg",
|
|
465
|
+
"--relaya-color-input-text",
|
|
466
|
+
"--relaya-color-btn-bg",
|
|
467
|
+
"--relaya-color-btn-text",
|
|
468
|
+
"--relaya-color-name-mod",
|
|
469
|
+
"--relaya-color-link",
|
|
470
|
+
"--relaya-color-link-active"
|
|
471
|
+
], C = {
|
|
472
|
+
light: {},
|
|
473
|
+
dark: {}
|
|
474
|
+
};
|
|
475
|
+
function w(e, t) {
|
|
476
|
+
let n = document.documentElement;
|
|
477
|
+
S.forEach((e) => n.style.removeProperty(e));
|
|
478
|
+
let r = e[t] ?? {};
|
|
479
|
+
Object.entries(r).forEach(([e, t]) => {
|
|
480
|
+
n.style.setProperty(e, t);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function T(t, i) {
|
|
484
|
+
let [a, o] = r(C), [s, l] = r(!1), [u, d] = r(!1), [f, p] = r(null), m = n(new c("", i));
|
|
485
|
+
return {
|
|
486
|
+
theme: a,
|
|
487
|
+
loading: s,
|
|
488
|
+
saving: u,
|
|
489
|
+
error: f,
|
|
490
|
+
loadTheme: e(async () => {
|
|
491
|
+
l(!0), p(null);
|
|
492
|
+
try {
|
|
493
|
+
o(await m.current.getSpaceTheme(t));
|
|
494
|
+
} catch (e) {
|
|
495
|
+
p(e?.message ?? "Failed to load theme");
|
|
496
|
+
} finally {
|
|
497
|
+
l(!1);
|
|
498
|
+
}
|
|
499
|
+
}, [t]),
|
|
500
|
+
saveTheme: e(async (e, n) => {
|
|
501
|
+
d(!0), p(null);
|
|
502
|
+
try {
|
|
503
|
+
let r = await m.current.saveSpaceTheme(t, e);
|
|
504
|
+
o(r), w(r, n);
|
|
505
|
+
} catch (e) {
|
|
506
|
+
p(e?.message ?? "Failed to save theme");
|
|
507
|
+
} finally {
|
|
508
|
+
d(!1);
|
|
509
|
+
}
|
|
510
|
+
}, [t]),
|
|
511
|
+
resetTheme: e(async (e) => {
|
|
512
|
+
d(!0), p(null);
|
|
513
|
+
try {
|
|
514
|
+
await m.current.saveSpaceTheme(t, C), o(C), w(C, e);
|
|
515
|
+
} catch (e) {
|
|
516
|
+
p(e?.message ?? "Failed to reset theme");
|
|
517
|
+
} finally {
|
|
518
|
+
d(!1);
|
|
519
|
+
}
|
|
520
|
+
}, [t])
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/hooks/useBans.ts
|
|
525
|
+
function E(t, i, a) {
|
|
526
|
+
let [o, l] = r({
|
|
527
|
+
bans: [],
|
|
528
|
+
loading: !1,
|
|
529
|
+
lifting: null,
|
|
530
|
+
error: null
|
|
531
|
+
}), u = i?.permissions.includes(s.BAN_USER) ?? !1, d = n(new c("", a)), f = e(async () => {
|
|
532
|
+
if (!(!u || !t)) {
|
|
533
|
+
l((e) => ({
|
|
534
|
+
...e,
|
|
535
|
+
loading: !0,
|
|
536
|
+
error: null
|
|
537
|
+
}));
|
|
538
|
+
try {
|
|
539
|
+
let e = (await d.current.getBans(t, !0)).bans;
|
|
540
|
+
l((t) => ({
|
|
541
|
+
...t,
|
|
542
|
+
bans: e,
|
|
543
|
+
loading: !1
|
|
544
|
+
}));
|
|
545
|
+
} catch (e) {
|
|
546
|
+
l((t) => ({
|
|
547
|
+
...t,
|
|
548
|
+
loading: !1,
|
|
549
|
+
error: e?.message ?? "Failed to load bans"
|
|
550
|
+
}));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}, [u, t]), p = e(async (e) => {
|
|
554
|
+
if (u) {
|
|
555
|
+
l((t) => ({
|
|
556
|
+
...t,
|
|
557
|
+
lifting: e,
|
|
558
|
+
error: null
|
|
559
|
+
}));
|
|
560
|
+
try {
|
|
561
|
+
await d.current.liftBan(t, e), l((e) => ({
|
|
562
|
+
...e,
|
|
563
|
+
lifting: null
|
|
564
|
+
})), await f();
|
|
565
|
+
} catch (e) {
|
|
566
|
+
throw l((t) => ({
|
|
567
|
+
...t,
|
|
568
|
+
lifting: null,
|
|
569
|
+
error: e?.message ?? "Failed to lift ban"
|
|
570
|
+
})), e;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}, [
|
|
574
|
+
u,
|
|
575
|
+
t,
|
|
576
|
+
f
|
|
577
|
+
]);
|
|
578
|
+
return {
|
|
579
|
+
...o,
|
|
580
|
+
loadBans: f,
|
|
581
|
+
liftBan: p
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region src/hooks/useModerationConfig.ts
|
|
586
|
+
function D(t, n, i) {
|
|
587
|
+
let [a, o] = r({
|
|
588
|
+
config: null,
|
|
589
|
+
loading: !1,
|
|
590
|
+
saving: !1,
|
|
591
|
+
error: null,
|
|
592
|
+
note: null
|
|
593
|
+
}), l = new c("", i), u = n?.permissions.includes(s.MANAGE_ROLES) ?? !1, d = e(async () => {
|
|
594
|
+
if (!(!u || !t)) {
|
|
595
|
+
o((e) => ({
|
|
596
|
+
...e,
|
|
597
|
+
loading: !0,
|
|
598
|
+
error: null
|
|
599
|
+
}));
|
|
600
|
+
try {
|
|
601
|
+
let e = await l.getModerationConfig(t);
|
|
602
|
+
o((t) => ({
|
|
603
|
+
...t,
|
|
604
|
+
config: e.config,
|
|
605
|
+
note: e.note,
|
|
606
|
+
loading: !1
|
|
607
|
+
}));
|
|
608
|
+
} catch (e) {
|
|
609
|
+
o((t) => ({
|
|
610
|
+
...t,
|
|
611
|
+
loading: !1,
|
|
612
|
+
error: e?.message ?? "Failed to load config"
|
|
613
|
+
}));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}, [u, t]), f = e(async (e) => {
|
|
617
|
+
if (!(!u || !t)) {
|
|
618
|
+
o((e) => ({
|
|
619
|
+
...e,
|
|
620
|
+
saving: !0,
|
|
621
|
+
error: null
|
|
622
|
+
}));
|
|
623
|
+
try {
|
|
624
|
+
let n = await l.updateModerationConfig(t, e);
|
|
625
|
+
o((e) => ({
|
|
626
|
+
...e,
|
|
627
|
+
config: n.config,
|
|
628
|
+
note: n.note,
|
|
629
|
+
saving: !1
|
|
630
|
+
}));
|
|
631
|
+
} catch (e) {
|
|
632
|
+
o((t) => ({
|
|
633
|
+
...t,
|
|
634
|
+
saving: !1,
|
|
635
|
+
error: e?.message ?? "Failed to update config"
|
|
636
|
+
}));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}, [u, t]);
|
|
640
|
+
return {
|
|
641
|
+
...a,
|
|
642
|
+
loadConfig: d,
|
|
643
|
+
updateConfig: f
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function O(t, i, a) {
|
|
647
|
+
let [o, l] = r({
|
|
648
|
+
reports: [],
|
|
649
|
+
total: 0,
|
|
650
|
+
loading: !1,
|
|
651
|
+
actioning: null,
|
|
652
|
+
error: null,
|
|
653
|
+
offset: 0
|
|
654
|
+
}), u = n(0), d = i?.permissions.includes(s.DELETE_ANY) ?? !1, f = n(new c("", a)), p = e(async (e) => {
|
|
655
|
+
if (!d || !t) return;
|
|
656
|
+
let n = e === void 0 ? u.current : e;
|
|
657
|
+
u.current = n, l((e) => ({
|
|
658
|
+
...e,
|
|
659
|
+
loading: !0,
|
|
660
|
+
error: null
|
|
661
|
+
}));
|
|
662
|
+
try {
|
|
663
|
+
let e = await f.current.getReports(t, {
|
|
664
|
+
status: "pending",
|
|
665
|
+
limit: 20,
|
|
666
|
+
offset: n
|
|
667
|
+
});
|
|
668
|
+
l((t) => ({
|
|
669
|
+
...t,
|
|
670
|
+
reports: e.reports,
|
|
671
|
+
total: e.total,
|
|
672
|
+
loading: !1,
|
|
673
|
+
offset: n
|
|
674
|
+
}));
|
|
675
|
+
} catch (e) {
|
|
676
|
+
l((t) => ({
|
|
677
|
+
...t,
|
|
678
|
+
loading: !1,
|
|
679
|
+
error: e?.message ?? "Failed to load reports"
|
|
680
|
+
}));
|
|
681
|
+
}
|
|
682
|
+
}, [d, t]), m = e(async (e) => {
|
|
683
|
+
if (d) {
|
|
684
|
+
l((t) => ({
|
|
685
|
+
...t,
|
|
686
|
+
actioning: e,
|
|
687
|
+
error: null
|
|
688
|
+
}));
|
|
689
|
+
try {
|
|
690
|
+
await f.current.updateReport(t, e, { status: "dismissed" }), l((e) => ({
|
|
691
|
+
...e,
|
|
692
|
+
actioning: null
|
|
693
|
+
})), await p();
|
|
694
|
+
} catch (e) {
|
|
695
|
+
throw l((t) => ({
|
|
696
|
+
...t,
|
|
697
|
+
actioning: null,
|
|
698
|
+
error: e?.message ?? "Failed to dismiss report"
|
|
699
|
+
})), e;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}, [
|
|
703
|
+
d,
|
|
704
|
+
t,
|
|
705
|
+
p
|
|
706
|
+
]), h = e(async (e, n) => {
|
|
707
|
+
if (d) {
|
|
708
|
+
l((t) => ({
|
|
709
|
+
...t,
|
|
710
|
+
actioning: e,
|
|
711
|
+
error: null
|
|
712
|
+
}));
|
|
713
|
+
try {
|
|
714
|
+
try {
|
|
715
|
+
await f.current.deleteMessage(t, n);
|
|
716
|
+
} catch (e) {
|
|
717
|
+
if (e.status !== 404) throw e;
|
|
718
|
+
}
|
|
719
|
+
await f.current.updateReport(t, e, { status: "reviewed" }), l((e) => ({
|
|
720
|
+
...e,
|
|
721
|
+
actioning: null
|
|
722
|
+
})), await p();
|
|
723
|
+
} catch (e) {
|
|
724
|
+
throw l((t) => ({
|
|
725
|
+
...t,
|
|
726
|
+
actioning: null,
|
|
727
|
+
error: e?.message ?? "Failed to delete message"
|
|
728
|
+
})), e;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}, [
|
|
732
|
+
d,
|
|
733
|
+
t,
|
|
734
|
+
p
|
|
735
|
+
]), g = e(async (e, n, r) => {
|
|
736
|
+
if (d) {
|
|
737
|
+
l((t) => ({
|
|
738
|
+
...t,
|
|
739
|
+
actioning: e,
|
|
740
|
+
error: null
|
|
741
|
+
}));
|
|
742
|
+
try {
|
|
743
|
+
try {
|
|
744
|
+
await f.current.createBan(t, n, r);
|
|
745
|
+
} catch (e) {
|
|
746
|
+
if (e.status !== 409) throw e;
|
|
747
|
+
}
|
|
748
|
+
await f.current.updateReport(t, e, { status: "reviewed" }), l((e) => ({
|
|
749
|
+
...e,
|
|
750
|
+
actioning: null
|
|
751
|
+
})), await p();
|
|
752
|
+
} catch (e) {
|
|
753
|
+
throw l((t) => ({
|
|
754
|
+
...t,
|
|
755
|
+
actioning: null,
|
|
756
|
+
error: e?.message ?? "Failed to ban user"
|
|
757
|
+
})), e;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}, [
|
|
761
|
+
d,
|
|
762
|
+
t,
|
|
763
|
+
p
|
|
764
|
+
]);
|
|
765
|
+
return {
|
|
766
|
+
...o,
|
|
767
|
+
loadReports: p,
|
|
768
|
+
dismissReport: m,
|
|
769
|
+
deleteMessageAndReview: h,
|
|
770
|
+
banAndReview: g
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region src/spaceThemes.ts
|
|
775
|
+
var k = "'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", A = "#7391A7", j = {
|
|
776
|
+
titleBg: A,
|
|
777
|
+
spaceNameColor: "white",
|
|
778
|
+
spaceNameFontFamily: k,
|
|
779
|
+
uiFontFamily: k,
|
|
780
|
+
onlineTextColor: "white",
|
|
781
|
+
noNameTextColor: "white",
|
|
782
|
+
buttonBorderColor: "transparent",
|
|
783
|
+
buttonBg: "white",
|
|
784
|
+
buttonTextColor: "black",
|
|
785
|
+
avatarBg: A,
|
|
786
|
+
avatarTextColor: "white",
|
|
787
|
+
ownMsgBg: A,
|
|
788
|
+
ownMsgText: "white",
|
|
789
|
+
msgFontFamily: k
|
|
790
|
+
}, M = {};
|
|
791
|
+
function N(e) {
|
|
792
|
+
return M[e] ?? j;
|
|
793
|
+
}
|
|
794
|
+
function P(e) {
|
|
795
|
+
let t = document.documentElement, n = (e, n) => {
|
|
796
|
+
n !== void 0 && t.style.setProperty(e, n);
|
|
797
|
+
};
|
|
798
|
+
n("--sp-title-bg", e.titleBg), n("--sp-name-color", e.spaceNameColor), n("--sp-name-font", e.spaceNameFontFamily), n("--sp-ui-font", e.uiFontFamily), n("--sp-online-color", e.onlineTextColor), n("--sp-no-name-color", e.noNameTextColor), n("--sp-btn-border", e.buttonBorderColor), n("--sp-btn-bg", e.buttonBg), n("--sp-btn-text", e.buttonTextColor), n("--sp-avatar-bg", e.avatarBg), n("--sp-avatar-text", e.avatarTextColor), n("--sp-other-msg-bg", e.otherMsgBg), n("--sp-other-msg-text", e.otherMsgText), n("--sp-own-msg-bg", e.ownMsgBg), n("--sp-own-msg-text", e.ownMsgText), n("--sp-msg-font", e.msgFontFamily ?? e.uiFontFamily), n("--sp-msg-font-size", e.msgFontSize), n("--sp-time-color", e.timeLabelColor), n("--sp-send-btn-bg", e.sendButtonBg), n("--sp-send-btn-text", e.sendButtonText);
|
|
799
|
+
}
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region src/stickerAdminUtils.ts
|
|
802
|
+
function F(e, t, n) {
|
|
803
|
+
let r = e.findIndex((e) => e.filename === t), i = e.findIndex((e) => e.filename === n);
|
|
804
|
+
if (r === -1 || i === -1 || r === i) return e;
|
|
805
|
+
let a = [...e], [o] = a.splice(r, 1);
|
|
806
|
+
return a.splice(i, 0, o), a.map((e, t) => ({
|
|
807
|
+
...e,
|
|
808
|
+
order: t
|
|
809
|
+
}));
|
|
810
|
+
}
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region src/components/BanModal.tsx
|
|
813
|
+
var I = [
|
|
814
|
+
{
|
|
815
|
+
value: "",
|
|
816
|
+
label: "Permanent"
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
value: "1h",
|
|
820
|
+
label: "1 hour"
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
value: "24h",
|
|
824
|
+
label: "24 hours"
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
value: "7d",
|
|
828
|
+
label: "7 days"
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
value: "30d",
|
|
832
|
+
label: "30 days"
|
|
833
|
+
}
|
|
834
|
+
];
|
|
835
|
+
function L(e) {
|
|
836
|
+
if (!e) return;
|
|
837
|
+
let t = Date.now(), n = {
|
|
838
|
+
"1h": 3600 * 1e3,
|
|
839
|
+
"24h": 1440 * 60 * 1e3,
|
|
840
|
+
"7d": 10080 * 60 * 1e3,
|
|
841
|
+
"30d": 720 * 60 * 60 * 1e3
|
|
842
|
+
}[e];
|
|
843
|
+
return n ? new Date(t + n).toISOString() : void 0;
|
|
844
|
+
}
|
|
845
|
+
function R({ userId: e, displayName: t, onBan: n, onClose: i }) {
|
|
846
|
+
let [s, c] = r(""), [l, u] = r(""), [d, f] = r(!1), [p, m] = r(null);
|
|
847
|
+
async function h() {
|
|
848
|
+
f(!0), m(null);
|
|
849
|
+
try {
|
|
850
|
+
await n(e, {
|
|
851
|
+
reason: s.trim() || void 0,
|
|
852
|
+
expiresAt: L(l)
|
|
853
|
+
}), i();
|
|
854
|
+
} catch (e) {
|
|
855
|
+
m(e?.message ?? "Failed to ban user."), f(!1);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return /* @__PURE__ */ a("div", {
|
|
859
|
+
className: "modal-overlay",
|
|
860
|
+
onClick: (e) => e.target === e.currentTarget && i(),
|
|
861
|
+
children: /* @__PURE__ */ o("div", {
|
|
862
|
+
className: "modal",
|
|
863
|
+
role: "dialog",
|
|
864
|
+
"aria-modal": "true",
|
|
865
|
+
"aria-label": "Ban user",
|
|
866
|
+
children: [
|
|
867
|
+
/* @__PURE__ */ a("h2", {
|
|
868
|
+
className: "modal__title",
|
|
869
|
+
children: "Ban user"
|
|
870
|
+
}),
|
|
871
|
+
/* @__PURE__ */ o("div", {
|
|
872
|
+
className: "modal__body",
|
|
873
|
+
children: [
|
|
874
|
+
/* @__PURE__ */ o("p", { children: [
|
|
875
|
+
"You are about to ban ",
|
|
876
|
+
/* @__PURE__ */ a("strong", { children: t }),
|
|
877
|
+
" from this station's chat. They will be immediately disconnected and will not be able to rejoin."
|
|
878
|
+
] }),
|
|
879
|
+
/* @__PURE__ */ a("label", {
|
|
880
|
+
htmlFor: "ban-duration",
|
|
881
|
+
children: "Duration"
|
|
882
|
+
}),
|
|
883
|
+
/* @__PURE__ */ a("select", {
|
|
884
|
+
id: "ban-duration",
|
|
885
|
+
value: l,
|
|
886
|
+
onChange: (e) => u(e.target.value),
|
|
887
|
+
disabled: d,
|
|
888
|
+
children: I.map((e) => /* @__PURE__ */ a("option", {
|
|
889
|
+
value: e.value,
|
|
890
|
+
children: e.label
|
|
891
|
+
}, e.value))
|
|
892
|
+
}),
|
|
893
|
+
/* @__PURE__ */ a("label", {
|
|
894
|
+
htmlFor: "ban-reason",
|
|
895
|
+
children: "Reason (optional)"
|
|
896
|
+
}),
|
|
897
|
+
/* @__PURE__ */ a("textarea", {
|
|
898
|
+
id: "ban-reason",
|
|
899
|
+
value: s,
|
|
900
|
+
onChange: (e) => c(e.target.value),
|
|
901
|
+
placeholder: "Why is this user being banned?",
|
|
902
|
+
disabled: d,
|
|
903
|
+
maxLength: 500
|
|
904
|
+
}),
|
|
905
|
+
p && /* @__PURE__ */ a("p", {
|
|
906
|
+
style: {
|
|
907
|
+
color: "var(--color-danger)",
|
|
908
|
+
marginTop: 8
|
|
909
|
+
},
|
|
910
|
+
children: p
|
|
911
|
+
})
|
|
912
|
+
]
|
|
913
|
+
}),
|
|
914
|
+
/* @__PURE__ */ o("div", {
|
|
915
|
+
className: "modal__footer",
|
|
916
|
+
children: [/* @__PURE__ */ a("button", {
|
|
917
|
+
className: "btn btn--ghost",
|
|
918
|
+
onClick: i,
|
|
919
|
+
disabled: d,
|
|
920
|
+
children: "Cancel"
|
|
921
|
+
}), /* @__PURE__ */ a("button", {
|
|
922
|
+
className: "btn btn--danger",
|
|
923
|
+
onClick: h,
|
|
924
|
+
disabled: d,
|
|
925
|
+
children: d ? "Banning…" : "Confirm ban"
|
|
926
|
+
})]
|
|
927
|
+
})
|
|
928
|
+
]
|
|
929
|
+
})
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region src/components/ReportReview.tsx
|
|
934
|
+
function z(e) {
|
|
935
|
+
return new Date(e).toLocaleString(void 0, {
|
|
936
|
+
month: "short",
|
|
937
|
+
day: "numeric",
|
|
938
|
+
hour: "2-digit",
|
|
939
|
+
minute: "2-digit"
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
function B(e) {
|
|
943
|
+
return e.replace(/_/g, " ");
|
|
944
|
+
}
|
|
945
|
+
function V({ stationSlug: e, user: n, getToken: i }) {
|
|
946
|
+
let [c, l] = r(!1), [u, d] = r(null), { reports: f, total: p, loading: m, actioning: h, error: g, offset: _, loadReports: v, dismissReport: y, deleteMessageAndReview: b, banAndReview: x } = O(e, n, i);
|
|
947
|
+
if (t(() => {
|
|
948
|
+
c && v(0);
|
|
949
|
+
}, [c]), !n.permissions.includes(s.DELETE_ANY)) return null;
|
|
950
|
+
let S = _ > 0, C = _ + 20 < p;
|
|
951
|
+
return /* @__PURE__ */ o("div", {
|
|
952
|
+
className: "report-review",
|
|
953
|
+
children: [
|
|
954
|
+
/* @__PURE__ */ o("button", {
|
|
955
|
+
className: "report-review__toggle",
|
|
956
|
+
onClick: () => l((e) => !e),
|
|
957
|
+
"aria-expanded": c,
|
|
958
|
+
children: [/* @__PURE__ */ a("span", { children: c ? "▼" : "▶" }), /* @__PURE__ */ o("span", { children: ["Report queue", p > 0 && /* @__PURE__ */ a("span", {
|
|
959
|
+
className: "report-review__badge",
|
|
960
|
+
"aria-label": `${p} pending reports`,
|
|
961
|
+
children: p
|
|
962
|
+
})] })]
|
|
963
|
+
}),
|
|
964
|
+
c && /* @__PURE__ */ o("div", {
|
|
965
|
+
className: "report-review__panel",
|
|
966
|
+
children: [
|
|
967
|
+
g && /* @__PURE__ */ a("p", {
|
|
968
|
+
className: "report-review__error",
|
|
969
|
+
children: g
|
|
970
|
+
}),
|
|
971
|
+
m && /* @__PURE__ */ a("p", {
|
|
972
|
+
className: "report-review__loading",
|
|
973
|
+
children: "Loading…"
|
|
974
|
+
}),
|
|
975
|
+
!m && f.length === 0 && /* @__PURE__ */ a("p", {
|
|
976
|
+
className: "report-review__empty",
|
|
977
|
+
children: "No pending reports. ✓"
|
|
978
|
+
}),
|
|
979
|
+
f.map((e) => {
|
|
980
|
+
let t = h === e.reportId;
|
|
981
|
+
return /* @__PURE__ */ o("div", {
|
|
982
|
+
className: "report-card",
|
|
983
|
+
children: [
|
|
984
|
+
/* @__PURE__ */ o("div", {
|
|
985
|
+
className: "report-card__meta",
|
|
986
|
+
children: [/* @__PURE__ */ a("span", {
|
|
987
|
+
className: "report-card__reason",
|
|
988
|
+
children: B(e.reason)
|
|
989
|
+
}), /* @__PURE__ */ a("span", {
|
|
990
|
+
className: "report-card__time",
|
|
991
|
+
children: z(e.createdAt)
|
|
992
|
+
})]
|
|
993
|
+
}),
|
|
994
|
+
/* @__PURE__ */ a("div", {
|
|
995
|
+
className: "report-card__message",
|
|
996
|
+
children: e.messageIsDeleted ? /* @__PURE__ */ a("em", {
|
|
997
|
+
className: "report-card__deleted",
|
|
998
|
+
children: "[message already deleted]"
|
|
999
|
+
}) : /* @__PURE__ */ a("span", { children: e.messageContent ?? "" })
|
|
1000
|
+
}),
|
|
1001
|
+
/* @__PURE__ */ o("div", {
|
|
1002
|
+
className: "report-card__details",
|
|
1003
|
+
children: [
|
|
1004
|
+
/* @__PURE__ */ o("span", { children: [
|
|
1005
|
+
/* @__PURE__ */ a("strong", { children: "Author:" }),
|
|
1006
|
+
" ",
|
|
1007
|
+
e.messageAuthor.displayName
|
|
1008
|
+
] }),
|
|
1009
|
+
/* @__PURE__ */ o("span", { children: [
|
|
1010
|
+
/* @__PURE__ */ a("strong", { children: "Reported by:" }),
|
|
1011
|
+
" ",
|
|
1012
|
+
e.reporter.displayName
|
|
1013
|
+
] }),
|
|
1014
|
+
e.details && /* @__PURE__ */ o("span", { children: [
|
|
1015
|
+
/* @__PURE__ */ a("strong", { children: "Note:" }),
|
|
1016
|
+
" ",
|
|
1017
|
+
e.details
|
|
1018
|
+
] })
|
|
1019
|
+
]
|
|
1020
|
+
}),
|
|
1021
|
+
/* @__PURE__ */ o("div", {
|
|
1022
|
+
className: "report-card__actions",
|
|
1023
|
+
children: [
|
|
1024
|
+
/* @__PURE__ */ a("button", {
|
|
1025
|
+
className: "btn btn--ghost",
|
|
1026
|
+
style: {
|
|
1027
|
+
fontSize: 12,
|
|
1028
|
+
padding: "3px 10px"
|
|
1029
|
+
},
|
|
1030
|
+
onClick: () => y(e.reportId),
|
|
1031
|
+
disabled: t,
|
|
1032
|
+
title: "Dismiss this report — no action taken",
|
|
1033
|
+
children: t ? "…" : "Dismiss"
|
|
1034
|
+
}),
|
|
1035
|
+
!e.messageIsDeleted && /* @__PURE__ */ a("button", {
|
|
1036
|
+
className: "btn btn--ghost",
|
|
1037
|
+
style: {
|
|
1038
|
+
fontSize: 12,
|
|
1039
|
+
padding: "3px 10px"
|
|
1040
|
+
},
|
|
1041
|
+
onClick: () => b(e.reportId, e.messageId),
|
|
1042
|
+
disabled: t,
|
|
1043
|
+
title: "Delete the reported message and mark this report reviewed",
|
|
1044
|
+
children: t ? "…" : "Delete message"
|
|
1045
|
+
}),
|
|
1046
|
+
/* @__PURE__ */ a("button", {
|
|
1047
|
+
className: "btn btn--danger",
|
|
1048
|
+
style: {
|
|
1049
|
+
fontSize: 12,
|
|
1050
|
+
padding: "3px 10px"
|
|
1051
|
+
},
|
|
1052
|
+
onClick: () => d({
|
|
1053
|
+
reportId: e.reportId,
|
|
1054
|
+
userId: e.messageAuthor.userId,
|
|
1055
|
+
displayName: e.messageAuthor.displayName
|
|
1056
|
+
}),
|
|
1057
|
+
disabled: t,
|
|
1058
|
+
title: "Ban this message's author and mark this report reviewed",
|
|
1059
|
+
children: "Ban author"
|
|
1060
|
+
})
|
|
1061
|
+
]
|
|
1062
|
+
})
|
|
1063
|
+
]
|
|
1064
|
+
}, e.reportId);
|
|
1065
|
+
}),
|
|
1066
|
+
(S || C) && /* @__PURE__ */ o("div", {
|
|
1067
|
+
className: "report-review__pagination",
|
|
1068
|
+
children: [
|
|
1069
|
+
/* @__PURE__ */ a("button", {
|
|
1070
|
+
className: "btn btn--ghost",
|
|
1071
|
+
style: {
|
|
1072
|
+
fontSize: 12,
|
|
1073
|
+
padding: "3px 10px"
|
|
1074
|
+
},
|
|
1075
|
+
onClick: () => v(_ - 20),
|
|
1076
|
+
disabled: !S || m,
|
|
1077
|
+
children: "← Previous"
|
|
1078
|
+
}),
|
|
1079
|
+
/* @__PURE__ */ o("span", {
|
|
1080
|
+
className: "report-review__page-info",
|
|
1081
|
+
children: [
|
|
1082
|
+
_ + 1,
|
|
1083
|
+
"–",
|
|
1084
|
+
Math.min(_ + 20, p),
|
|
1085
|
+
" of ",
|
|
1086
|
+
p
|
|
1087
|
+
]
|
|
1088
|
+
}),
|
|
1089
|
+
/* @__PURE__ */ a("button", {
|
|
1090
|
+
className: "btn btn--ghost",
|
|
1091
|
+
style: {
|
|
1092
|
+
fontSize: 12,
|
|
1093
|
+
padding: "3px 10px"
|
|
1094
|
+
},
|
|
1095
|
+
onClick: () => v(_ + 20),
|
|
1096
|
+
disabled: !C || m,
|
|
1097
|
+
children: "Next →"
|
|
1098
|
+
})
|
|
1099
|
+
]
|
|
1100
|
+
})
|
|
1101
|
+
]
|
|
1102
|
+
}),
|
|
1103
|
+
u && /* @__PURE__ */ a(R, {
|
|
1104
|
+
userId: u.userId,
|
|
1105
|
+
displayName: u.displayName,
|
|
1106
|
+
onBan: async (e, t) => {
|
|
1107
|
+
await x(u.reportId, e, t);
|
|
1108
|
+
},
|
|
1109
|
+
onClose: () => d(null)
|
|
1110
|
+
})
|
|
1111
|
+
]
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
//#endregion
|
|
1115
|
+
//#region src/components/BanManagement.tsx
|
|
1116
|
+
function H(e) {
|
|
1117
|
+
return new Date(e).toLocaleString(void 0, {
|
|
1118
|
+
month: "short",
|
|
1119
|
+
day: "numeric",
|
|
1120
|
+
hour: "2-digit",
|
|
1121
|
+
minute: "2-digit"
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
function U(e, t) {
|
|
1125
|
+
return t || !e ? "Permanent" : new Date(e) < /* @__PURE__ */ new Date() ? "Expired" : `Expires ${H(e)}`;
|
|
1126
|
+
}
|
|
1127
|
+
function W({ stationSlug: e, user: n, getToken: i }) {
|
|
1128
|
+
let [c, l] = r(!1), { bans: u, loading: d, lifting: f, error: p, loadBans: m, liftBan: h } = E(e, n, i);
|
|
1129
|
+
return t(() => {
|
|
1130
|
+
c && m();
|
|
1131
|
+
}, [c]), n.permissions.includes(s.BAN_USER) ? /* @__PURE__ */ o("div", {
|
|
1132
|
+
className: "ban-management",
|
|
1133
|
+
children: [/* @__PURE__ */ o("button", {
|
|
1134
|
+
className: "ban-management__toggle",
|
|
1135
|
+
onClick: () => l((e) => !e),
|
|
1136
|
+
"aria-expanded": c,
|
|
1137
|
+
children: [/* @__PURE__ */ a("span", { children: c ? "▼" : "▶" }), /* @__PURE__ */ o("span", { children: ["Active bans", u.length > 0 && /* @__PURE__ */ a("span", {
|
|
1138
|
+
className: "ban-management__badge",
|
|
1139
|
+
"aria-label": `${u.length} active bans`,
|
|
1140
|
+
children: u.length
|
|
1141
|
+
})] })]
|
|
1142
|
+
}), c && /* @__PURE__ */ o("div", {
|
|
1143
|
+
className: "ban-management__panel",
|
|
1144
|
+
children: [
|
|
1145
|
+
p && /* @__PURE__ */ a("p", {
|
|
1146
|
+
className: "ban-management__error",
|
|
1147
|
+
children: p
|
|
1148
|
+
}),
|
|
1149
|
+
d && /* @__PURE__ */ a("p", {
|
|
1150
|
+
className: "ban-management__loading",
|
|
1151
|
+
children: "Loading…"
|
|
1152
|
+
}),
|
|
1153
|
+
!d && u.length === 0 && /* @__PURE__ */ a("p", {
|
|
1154
|
+
className: "ban-management__empty",
|
|
1155
|
+
children: "No active bans. ✓"
|
|
1156
|
+
}),
|
|
1157
|
+
u.map((e) => {
|
|
1158
|
+
let t = f === e.banId;
|
|
1159
|
+
return /* @__PURE__ */ o("div", {
|
|
1160
|
+
className: "ban-card",
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ o("div", {
|
|
1163
|
+
className: "ban-card__header",
|
|
1164
|
+
children: [/* @__PURE__ */ a("span", {
|
|
1165
|
+
className: "ban-card__user",
|
|
1166
|
+
children: e.user.displayName
|
|
1167
|
+
}), /* @__PURE__ */ a("span", {
|
|
1168
|
+
className: `ban-card__expiry${e.isPermanent ? " ban-card__expiry--permanent" : ""}`,
|
|
1169
|
+
children: U(e.expiresAt, e.isPermanent)
|
|
1170
|
+
})]
|
|
1171
|
+
}),
|
|
1172
|
+
/* @__PURE__ */ o("div", {
|
|
1173
|
+
className: "ban-card__details",
|
|
1174
|
+
children: [
|
|
1175
|
+
e.reason && /* @__PURE__ */ o("span", { children: [
|
|
1176
|
+
/* @__PURE__ */ a("strong", { children: "Reason:" }),
|
|
1177
|
+
" ",
|
|
1178
|
+
e.reason
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ o("span", { children: [
|
|
1181
|
+
/* @__PURE__ */ a("strong", { children: "Banned by:" }),
|
|
1182
|
+
" ",
|
|
1183
|
+
e.bannedBy.displayName
|
|
1184
|
+
] }),
|
|
1185
|
+
/* @__PURE__ */ o("span", { children: [
|
|
1186
|
+
/* @__PURE__ */ a("strong", { children: "Banned at:" }),
|
|
1187
|
+
" ",
|
|
1188
|
+
H(e.createdAt)
|
|
1189
|
+
] })
|
|
1190
|
+
]
|
|
1191
|
+
}),
|
|
1192
|
+
/* @__PURE__ */ a("div", {
|
|
1193
|
+
className: "ban-card__actions",
|
|
1194
|
+
children: /* @__PURE__ */ a("button", {
|
|
1195
|
+
className: "btn btn--ghost",
|
|
1196
|
+
style: {
|
|
1197
|
+
fontSize: 12,
|
|
1198
|
+
padding: "3px 10px"
|
|
1199
|
+
},
|
|
1200
|
+
onClick: () => h(e.banId),
|
|
1201
|
+
disabled: t,
|
|
1202
|
+
title: "Remove this ban and allow the user to rejoin",
|
|
1203
|
+
children: t ? "…" : "Lift ban"
|
|
1204
|
+
})
|
|
1205
|
+
})
|
|
1206
|
+
]
|
|
1207
|
+
}, e.banId);
|
|
1208
|
+
})
|
|
1209
|
+
]
|
|
1210
|
+
})]
|
|
1211
|
+
}) : null;
|
|
1212
|
+
}
|
|
1213
|
+
//#endregion
|
|
1214
|
+
//#region src/hooks/usePresenceConfig.ts
|
|
1215
|
+
function G(t, n, i) {
|
|
1216
|
+
let [a, o] = r({
|
|
1217
|
+
config: null,
|
|
1218
|
+
loading: !1,
|
|
1219
|
+
saving: !1,
|
|
1220
|
+
error: null
|
|
1221
|
+
}), l = new c("", i), u = n?.permissions.includes(s.MANAGE_ROLES) ?? !1, d = e(async () => {
|
|
1222
|
+
if (!(!u || !t)) {
|
|
1223
|
+
o((e) => ({
|
|
1224
|
+
...e,
|
|
1225
|
+
loading: !0,
|
|
1226
|
+
error: null
|
|
1227
|
+
}));
|
|
1228
|
+
try {
|
|
1229
|
+
let e = await l.getPresenceConfig(t);
|
|
1230
|
+
o((t) => ({
|
|
1231
|
+
...t,
|
|
1232
|
+
config: e.config,
|
|
1233
|
+
loading: !1
|
|
1234
|
+
}));
|
|
1235
|
+
} catch (e) {
|
|
1236
|
+
o((t) => ({
|
|
1237
|
+
...t,
|
|
1238
|
+
loading: !1,
|
|
1239
|
+
error: e?.message ?? "Failed to load presence config"
|
|
1240
|
+
}));
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}, [u, t]), f = e(async (e) => {
|
|
1244
|
+
if (!(!u || !t)) {
|
|
1245
|
+
o((e) => ({
|
|
1246
|
+
...e,
|
|
1247
|
+
saving: !0,
|
|
1248
|
+
error: null
|
|
1249
|
+
}));
|
|
1250
|
+
try {
|
|
1251
|
+
let n = await l.updatePresenceConfig(t, e);
|
|
1252
|
+
o((e) => ({
|
|
1253
|
+
...e,
|
|
1254
|
+
config: n.config,
|
|
1255
|
+
saving: !1
|
|
1256
|
+
}));
|
|
1257
|
+
} catch (e) {
|
|
1258
|
+
o((t) => ({
|
|
1259
|
+
...t,
|
|
1260
|
+
saving: !1,
|
|
1261
|
+
error: e?.message ?? "Failed to update presence config"
|
|
1262
|
+
}));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}, [u, t]);
|
|
1266
|
+
return {
|
|
1267
|
+
...a,
|
|
1268
|
+
loadConfig: d,
|
|
1269
|
+
updateConfig: f
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
//#endregion
|
|
1273
|
+
//#region src/components/AdminSettings.tsx
|
|
1274
|
+
function K({ stationSlug: e, user: n, getToken: s, onOpenStickerAdmin: c, onOpenThemeAdmin: l }) {
|
|
1275
|
+
let [u, d] = r(!1), { config: f, loading: p, saving: m, error: h, note: g, loadConfig: _, updateConfig: v } = D(e, n, s), [y, b] = r(null), { config: x, loading: S, saving: C, error: w, loadConfig: T, updateConfig: E } = G(e, n, s), [O, k] = r("");
|
|
1276
|
+
t(() => {
|
|
1277
|
+
u && (!f && !p && _(), !x && !S && T());
|
|
1278
|
+
}, [u]), t(() => {
|
|
1279
|
+
f && !y && b({
|
|
1280
|
+
rateLimitWindowMs: String(f.rateLimitWindowMs),
|
|
1281
|
+
rateLimitMaxMessages: String(f.rateLimitMaxMessages),
|
|
1282
|
+
duplicateWindowMs: String(f.duplicateWindowMs)
|
|
1283
|
+
});
|
|
1284
|
+
}, [f]), t(() => {
|
|
1285
|
+
if (x && O === "") {
|
|
1286
|
+
let e = x.presenceGracePeriodMs / 6e4;
|
|
1287
|
+
k(String(Number(e.toFixed(1))));
|
|
1288
|
+
}
|
|
1289
|
+
}, [x]);
|
|
1290
|
+
async function A() {
|
|
1291
|
+
y && await v({
|
|
1292
|
+
rateLimitWindowMs: parseInt(y.rateLimitWindowMs, 10),
|
|
1293
|
+
rateLimitMaxMessages: parseInt(y.rateLimitMaxMessages, 10),
|
|
1294
|
+
duplicateWindowMs: parseInt(y.duplicateWindowMs, 10)
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
async function j() {
|
|
1298
|
+
let e = parseFloat(O);
|
|
1299
|
+
isNaN(e) || e < 0 || e > 15 || await E({ presenceGracePeriodMs: Math.round(e * 6e4 / 1e3) * 1e3 });
|
|
1300
|
+
}
|
|
1301
|
+
return /* @__PURE__ */ o("div", {
|
|
1302
|
+
className: "admin-settings",
|
|
1303
|
+
children: [/* @__PURE__ */ o("button", {
|
|
1304
|
+
className: "admin-settings__toggle",
|
|
1305
|
+
onClick: () => d((e) => !e),
|
|
1306
|
+
children: [/* @__PURE__ */ a("span", { children: u ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Moderation settings" })]
|
|
1307
|
+
}), u && /* @__PURE__ */ o("div", {
|
|
1308
|
+
className: "admin-settings__panel",
|
|
1309
|
+
children: [
|
|
1310
|
+
c && /* @__PURE__ */ o("div", {
|
|
1311
|
+
className: "admin-settings__section",
|
|
1312
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1313
|
+
className: "admin-settings__section-copy",
|
|
1314
|
+
children: [/* @__PURE__ */ a("strong", { children: "Sticker library" }), /* @__PURE__ */ a("span", { children: "Upload stickers, assign shortcodes, and control picker order." })]
|
|
1315
|
+
}), /* @__PURE__ */ a("button", {
|
|
1316
|
+
className: "btn btn--ghost",
|
|
1317
|
+
style: {
|
|
1318
|
+
width: "auto",
|
|
1319
|
+
padding: "6px 16px"
|
|
1320
|
+
},
|
|
1321
|
+
onClick: c,
|
|
1322
|
+
children: "Open sticker manager"
|
|
1323
|
+
})]
|
|
1324
|
+
}),
|
|
1325
|
+
l && /* @__PURE__ */ o("div", {
|
|
1326
|
+
className: "admin-settings__section",
|
|
1327
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1328
|
+
className: "admin-settings__section-copy",
|
|
1329
|
+
children: [/* @__PURE__ */ a("strong", { children: "Colour theme" }), /* @__PURE__ */ a("span", { children: "Customise the chat colours to match your brand." })]
|
|
1330
|
+
}), /* @__PURE__ */ a("button", {
|
|
1331
|
+
className: "btn btn--ghost",
|
|
1332
|
+
style: {
|
|
1333
|
+
width: "auto",
|
|
1334
|
+
padding: "6px 16px"
|
|
1335
|
+
},
|
|
1336
|
+
onClick: l,
|
|
1337
|
+
children: "Open theme editor"
|
|
1338
|
+
})]
|
|
1339
|
+
}),
|
|
1340
|
+
/* @__PURE__ */ o("div", {
|
|
1341
|
+
className: "admin-settings__notice",
|
|
1342
|
+
children: ["⚠️ Changes apply immediately but revert to environment defaults on server restart.", g && /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ a("br", {}), g] })]
|
|
1343
|
+
}),
|
|
1344
|
+
p && /* @__PURE__ */ a("p", {
|
|
1345
|
+
style: {
|
|
1346
|
+
fontSize: 13,
|
|
1347
|
+
color: "var(--color-text-muted)"
|
|
1348
|
+
},
|
|
1349
|
+
children: "Loading…"
|
|
1350
|
+
}),
|
|
1351
|
+
h && /* @__PURE__ */ a("p", {
|
|
1352
|
+
style: {
|
|
1353
|
+
fontSize: 13,
|
|
1354
|
+
color: "var(--color-danger)"
|
|
1355
|
+
},
|
|
1356
|
+
children: h
|
|
1357
|
+
}),
|
|
1358
|
+
y && /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ o("div", {
|
|
1359
|
+
className: "admin-settings__grid",
|
|
1360
|
+
children: [
|
|
1361
|
+
/* @__PURE__ */ o("div", {
|
|
1362
|
+
className: "admin-settings__field",
|
|
1363
|
+
children: [/* @__PURE__ */ a("label", {
|
|
1364
|
+
htmlFor: "rl-window",
|
|
1365
|
+
children: "Rate-limit window (ms)"
|
|
1366
|
+
}), /* @__PURE__ */ a("input", {
|
|
1367
|
+
id: "rl-window",
|
|
1368
|
+
type: "number",
|
|
1369
|
+
min: 1e3,
|
|
1370
|
+
value: y.rateLimitWindowMs,
|
|
1371
|
+
onChange: (e) => b((t) => t && {
|
|
1372
|
+
...t,
|
|
1373
|
+
rateLimitWindowMs: e.target.value
|
|
1374
|
+
})
|
|
1375
|
+
})]
|
|
1376
|
+
}),
|
|
1377
|
+
/* @__PURE__ */ o("div", {
|
|
1378
|
+
className: "admin-settings__field",
|
|
1379
|
+
children: [/* @__PURE__ */ a("label", {
|
|
1380
|
+
htmlFor: "rl-max",
|
|
1381
|
+
children: "Max messages per window"
|
|
1382
|
+
}), /* @__PURE__ */ a("input", {
|
|
1383
|
+
id: "rl-max",
|
|
1384
|
+
type: "number",
|
|
1385
|
+
min: 1,
|
|
1386
|
+
value: y.rateLimitMaxMessages,
|
|
1387
|
+
onChange: (e) => b((t) => t && {
|
|
1388
|
+
...t,
|
|
1389
|
+
rateLimitMaxMessages: e.target.value
|
|
1390
|
+
})
|
|
1391
|
+
})]
|
|
1392
|
+
}),
|
|
1393
|
+
/* @__PURE__ */ o("div", {
|
|
1394
|
+
className: "admin-settings__field",
|
|
1395
|
+
children: [/* @__PURE__ */ a("label", {
|
|
1396
|
+
htmlFor: "dup-window",
|
|
1397
|
+
children: "Duplicate burst window (ms)"
|
|
1398
|
+
}), /* @__PURE__ */ a("input", {
|
|
1399
|
+
id: "dup-window",
|
|
1400
|
+
type: "number",
|
|
1401
|
+
min: 0,
|
|
1402
|
+
value: y.duplicateWindowMs,
|
|
1403
|
+
onChange: (e) => b((t) => t && {
|
|
1404
|
+
...t,
|
|
1405
|
+
duplicateWindowMs: e.target.value
|
|
1406
|
+
})
|
|
1407
|
+
})]
|
|
1408
|
+
})
|
|
1409
|
+
]
|
|
1410
|
+
}), /* @__PURE__ */ a("button", {
|
|
1411
|
+
className: "btn btn--primary",
|
|
1412
|
+
style: {
|
|
1413
|
+
width: "auto",
|
|
1414
|
+
padding: "6px 16px"
|
|
1415
|
+
},
|
|
1416
|
+
onClick: A,
|
|
1417
|
+
disabled: m,
|
|
1418
|
+
children: m ? "Saving…" : "Apply changes"
|
|
1419
|
+
})] }),
|
|
1420
|
+
/* @__PURE__ */ o("div", {
|
|
1421
|
+
className: "admin-settings__divider",
|
|
1422
|
+
style: {
|
|
1423
|
+
marginTop: 20,
|
|
1424
|
+
borderTop: "1px solid var(--color-border)",
|
|
1425
|
+
paddingTop: 16
|
|
1426
|
+
},
|
|
1427
|
+
children: [
|
|
1428
|
+
/* @__PURE__ */ a("strong", {
|
|
1429
|
+
style: { fontSize: 13 },
|
|
1430
|
+
children: "Online status grace period"
|
|
1431
|
+
}),
|
|
1432
|
+
/* @__PURE__ */ a("p", {
|
|
1433
|
+
style: {
|
|
1434
|
+
fontSize: 12,
|
|
1435
|
+
color: "var(--color-text-muted)",
|
|
1436
|
+
marginTop: 4,
|
|
1437
|
+
marginBottom: 10
|
|
1438
|
+
},
|
|
1439
|
+
children: "How long a user stays visible in the online list after their browser disconnects. Use 0 for immediate removal. Saved to the database — persists across restarts."
|
|
1440
|
+
}),
|
|
1441
|
+
S && /* @__PURE__ */ a("p", {
|
|
1442
|
+
style: {
|
|
1443
|
+
fontSize: 13,
|
|
1444
|
+
color: "var(--color-text-muted)"
|
|
1445
|
+
},
|
|
1446
|
+
children: "Loading…"
|
|
1447
|
+
}),
|
|
1448
|
+
w && /* @__PURE__ */ a("p", {
|
|
1449
|
+
style: {
|
|
1450
|
+
fontSize: 13,
|
|
1451
|
+
color: "var(--color-danger)"
|
|
1452
|
+
},
|
|
1453
|
+
children: w
|
|
1454
|
+
}),
|
|
1455
|
+
x !== null && /* @__PURE__ */ o("div", {
|
|
1456
|
+
style: {
|
|
1457
|
+
display: "flex",
|
|
1458
|
+
alignItems: "center",
|
|
1459
|
+
gap: 8
|
|
1460
|
+
},
|
|
1461
|
+
children: [
|
|
1462
|
+
/* @__PURE__ */ a("input", {
|
|
1463
|
+
id: "presence-grace",
|
|
1464
|
+
type: "number",
|
|
1465
|
+
min: 0,
|
|
1466
|
+
max: 15,
|
|
1467
|
+
step: .5,
|
|
1468
|
+
value: O,
|
|
1469
|
+
onChange: (e) => k(e.target.value),
|
|
1470
|
+
style: { width: 80 }
|
|
1471
|
+
}),
|
|
1472
|
+
/* @__PURE__ */ a("label", {
|
|
1473
|
+
htmlFor: "presence-grace",
|
|
1474
|
+
style: { fontSize: 13 },
|
|
1475
|
+
children: "minutes"
|
|
1476
|
+
}),
|
|
1477
|
+
/* @__PURE__ */ a("button", {
|
|
1478
|
+
className: "btn btn--primary",
|
|
1479
|
+
style: {
|
|
1480
|
+
width: "auto",
|
|
1481
|
+
padding: "6px 16px"
|
|
1482
|
+
},
|
|
1483
|
+
onClick: j,
|
|
1484
|
+
disabled: C,
|
|
1485
|
+
children: C ? "Saving…" : "Save"
|
|
1486
|
+
})
|
|
1487
|
+
]
|
|
1488
|
+
})
|
|
1489
|
+
]
|
|
1490
|
+
})
|
|
1491
|
+
]
|
|
1492
|
+
})]
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region src/components/StickerAdminPage.tsx
|
|
1497
|
+
var q = 2 * 1024 * 1024, J = 310;
|
|
1498
|
+
function ee(e) {
|
|
1499
|
+
return e.shortcode ? `:${e.shortcode}:` : e.filename;
|
|
1500
|
+
}
|
|
1501
|
+
async function te(e) {
|
|
1502
|
+
let t = URL.createObjectURL(e);
|
|
1503
|
+
try {
|
|
1504
|
+
let e = await new Promise((e, n) => {
|
|
1505
|
+
let r = new Image();
|
|
1506
|
+
r.onload = () => e(r), r.onerror = () => n(/* @__PURE__ */ Error("Unable to read image dimensions")), r.src = t;
|
|
1507
|
+
});
|
|
1508
|
+
return {
|
|
1509
|
+
width: e.naturalWidth,
|
|
1510
|
+
height: e.naturalHeight
|
|
1511
|
+
};
|
|
1512
|
+
} finally {
|
|
1513
|
+
URL.revokeObjectURL(t);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
function Y({ stationSlug: e, user: i, getToken: l, onClose: u, onLibraryChanged: d }) {
|
|
1517
|
+
let f = n(new c("", l)).current, p = n(null), [m, h] = r([]), [g, _] = r(null), [v, y] = r(!0), [b, x] = r(!1), [S, C] = r(!1), [w, T] = r(null), [E, D] = r(!1), [O, k] = r(null), [A, j] = r(null), [M, N] = r(null), [P, I] = r(null), L = i.permissions.includes(s.MANAGE_ROLES);
|
|
1518
|
+
async function R() {
|
|
1519
|
+
y(!0), k(null), j(null);
|
|
1520
|
+
try {
|
|
1521
|
+
let t = await f.getStickers(e);
|
|
1522
|
+
h(t.stickers ?? []), _(t.quota ?? null), D(!1);
|
|
1523
|
+
} catch (e) {
|
|
1524
|
+
k(e?.message ?? "Failed to load sticker library.");
|
|
1525
|
+
} finally {
|
|
1526
|
+
y(!1);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
if (t(() => {
|
|
1530
|
+
L && R().catch(() => void 0);
|
|
1531
|
+
}, [L, e]), t(() => {
|
|
1532
|
+
if (A !== "Sticker order saved.") return;
|
|
1533
|
+
let e = window.setTimeout(() => {
|
|
1534
|
+
j((e) => e === "Sticker order saved." ? null : e);
|
|
1535
|
+
}, 1800);
|
|
1536
|
+
return () => window.clearTimeout(e);
|
|
1537
|
+
}, [A]), !L) return null;
|
|
1538
|
+
function z() {
|
|
1539
|
+
N(null), I(null);
|
|
1540
|
+
}
|
|
1541
|
+
function B(e, t) {
|
|
1542
|
+
if (b || S || w) {
|
|
1543
|
+
e.preventDefault();
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
N(t), e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", t);
|
|
1547
|
+
}
|
|
1548
|
+
function V(e, t) {
|
|
1549
|
+
!M || M === t || (e.preventDefault(), e.dataTransfer.dropEffect = "move", P !== t && I(t));
|
|
1550
|
+
}
|
|
1551
|
+
function H(e, t) {
|
|
1552
|
+
e.preventDefault();
|
|
1553
|
+
let n = M || e.dataTransfer.getData("text/plain");
|
|
1554
|
+
if (!n || n === t) {
|
|
1555
|
+
z();
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
let r = F(m, n, t);
|
|
1559
|
+
if (r === m) {
|
|
1560
|
+
z();
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
h(r), D(!0), j(null), z(), W(r, "Sticker order saved.");
|
|
1564
|
+
}
|
|
1565
|
+
function U(e, t) {
|
|
1566
|
+
h((n) => n.map((n) => n.filename === e ? {
|
|
1567
|
+
...n,
|
|
1568
|
+
shortcode: t
|
|
1569
|
+
} : n)), D(!0), j(null);
|
|
1570
|
+
}
|
|
1571
|
+
async function W(t, n) {
|
|
1572
|
+
x(!0), k(null), j(null);
|
|
1573
|
+
try {
|
|
1574
|
+
let r = await f.updateStickerManifest(e, t.map((e) => ({
|
|
1575
|
+
filename: e.filename,
|
|
1576
|
+
shortcode: e.shortcode?.trim() || null
|
|
1577
|
+
})));
|
|
1578
|
+
return h(r.stickers ?? []), D(!1), j(n), await d?.(), r.stickers ?? [];
|
|
1579
|
+
} catch (e) {
|
|
1580
|
+
return k(e?.message ?? "Failed to save sticker metadata."), D(!0), null;
|
|
1581
|
+
} finally {
|
|
1582
|
+
x(!1);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
async function G() {
|
|
1586
|
+
await W(m, "Sticker metadata saved.");
|
|
1587
|
+
}
|
|
1588
|
+
async function K(t) {
|
|
1589
|
+
let n = t.target.files?.[0];
|
|
1590
|
+
if (t.target.value = "", n) {
|
|
1591
|
+
if (E) {
|
|
1592
|
+
k("Save or discard your existing sticker edits before uploading a new file.");
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
if (n.size > q) {
|
|
1596
|
+
k("Sticker files must be 2MB or smaller.");
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
try {
|
|
1600
|
+
let e = await te(n);
|
|
1601
|
+
if (Math.max(e.width, e.height) > J) {
|
|
1602
|
+
k(`Sticker dimensions must be ${J}px or smaller on the longest side.`);
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
} catch {
|
|
1606
|
+
k("Could not read the selected image.");
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
C(!0), k(null), j(null);
|
|
1610
|
+
try {
|
|
1611
|
+
let t = await f.uploadSticker(e, n, n.name);
|
|
1612
|
+
h((e) => [...e, t.sticker].sort((e, t) => e.order - t.order || e.filename.localeCompare(t.filename))), _(t.quota ?? null), j("Sticker uploaded. Assign a shortcode and save when you are ready."), await d?.();
|
|
1613
|
+
} catch (e) {
|
|
1614
|
+
k(e?.message ?? "Failed to upload sticker.");
|
|
1615
|
+
} finally {
|
|
1616
|
+
C(!1);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
async function Y(t) {
|
|
1621
|
+
if (E) {
|
|
1622
|
+
k("Save or discard your existing sticker edits before deleting a file.");
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (window.confirm(`Delete ${t}? This removes the sticker file and its shortcode mapping.`)) {
|
|
1626
|
+
T(t), k(null), j(null);
|
|
1627
|
+
try {
|
|
1628
|
+
await f.deleteSticker(e, t), await R(), j("Sticker deleted."), await d?.();
|
|
1629
|
+
} catch (e) {
|
|
1630
|
+
k(e?.message ?? "Failed to delete sticker.");
|
|
1631
|
+
} finally {
|
|
1632
|
+
T(null);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
return /* @__PURE__ */ o("div", {
|
|
1637
|
+
className: "sticker-admin-page",
|
|
1638
|
+
children: [
|
|
1639
|
+
/* @__PURE__ */ o("div", {
|
|
1640
|
+
className: "sticker-admin-page__topbar",
|
|
1641
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1642
|
+
className: "sticker-admin-page__title-group",
|
|
1643
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1644
|
+
className: "sticker-admin-page__title-row",
|
|
1645
|
+
children: [
|
|
1646
|
+
u && /* @__PURE__ */ a("button", {
|
|
1647
|
+
className: "btn btn--ghost sticker-admin-page__back",
|
|
1648
|
+
onClick: u,
|
|
1649
|
+
children: "← Back"
|
|
1650
|
+
}),
|
|
1651
|
+
/* @__PURE__ */ a("h2", {
|
|
1652
|
+
className: "sticker-admin-page__title",
|
|
1653
|
+
children: "Sticker library"
|
|
1654
|
+
}),
|
|
1655
|
+
/* @__PURE__ */ a("span", {
|
|
1656
|
+
className: "sticker-admin-page__count",
|
|
1657
|
+
children: g ? `${g.used} sticker${g.used === 1 ? "" : "s"} of ${g.limit >= 9999 ? "unlimited" : g.limit} slots available` : `${m.length} sticker${m.length === 1 ? "" : "s"}`
|
|
1658
|
+
}),
|
|
1659
|
+
A && /* @__PURE__ */ a("div", {
|
|
1660
|
+
className: "sticker-admin-page__status-inline",
|
|
1661
|
+
children: A
|
|
1662
|
+
})
|
|
1663
|
+
]
|
|
1664
|
+
}), /* @__PURE__ */ a("p", {
|
|
1665
|
+
className: "sticker-admin-page__subtitle",
|
|
1666
|
+
children: "Upload stickers, assign shortcodes, and order the picker."
|
|
1667
|
+
})]
|
|
1668
|
+
}), /* @__PURE__ */ o("div", {
|
|
1669
|
+
className: "sticker-admin-page__header-actions",
|
|
1670
|
+
children: [
|
|
1671
|
+
/* @__PURE__ */ a("button", {
|
|
1672
|
+
className: "btn btn--ghost",
|
|
1673
|
+
onClick: () => p.current?.click(),
|
|
1674
|
+
disabled: S || b || v,
|
|
1675
|
+
children: S ? "Uploading…" : "Upload"
|
|
1676
|
+
}),
|
|
1677
|
+
/* @__PURE__ */ a("button", {
|
|
1678
|
+
className: "btn btn--ghost",
|
|
1679
|
+
onClick: () => R(),
|
|
1680
|
+
disabled: v || b || S,
|
|
1681
|
+
children: "Reload"
|
|
1682
|
+
}),
|
|
1683
|
+
/* @__PURE__ */ a("button", {
|
|
1684
|
+
className: "btn btn--primary",
|
|
1685
|
+
onClick: G,
|
|
1686
|
+
disabled: !E || b || v || S,
|
|
1687
|
+
children: b ? "Saving…" : "Save shortcodes"
|
|
1688
|
+
})
|
|
1689
|
+
]
|
|
1690
|
+
})]
|
|
1691
|
+
}),
|
|
1692
|
+
/* @__PURE__ */ o("div", {
|
|
1693
|
+
className: "sticker-admin-page__notice",
|
|
1694
|
+
children: [/* @__PURE__ */ a("strong", { children: "Rules:" }), " unique per station, max 12 chars, lowercase letters/numbers/hyphens/underscores only. Blank shortcode fields auto-generate from the filename. Uploads ≤2MB and ≤310px. Drag tiles to reorder."]
|
|
1695
|
+
}),
|
|
1696
|
+
O && /* @__PURE__ */ a("div", {
|
|
1697
|
+
className: "sticker-admin-page__error",
|
|
1698
|
+
children: O
|
|
1699
|
+
}),
|
|
1700
|
+
/* @__PURE__ */ a("input", {
|
|
1701
|
+
ref: p,
|
|
1702
|
+
type: "file",
|
|
1703
|
+
accept: "image/gif,image/png,image/jpeg,image/webp",
|
|
1704
|
+
hidden: !0,
|
|
1705
|
+
onChange: K
|
|
1706
|
+
}),
|
|
1707
|
+
v ? /* @__PURE__ */ a("div", {
|
|
1708
|
+
className: "sticker-admin-page__state",
|
|
1709
|
+
children: "Loading sticker library…"
|
|
1710
|
+
}) : m.length === 0 ? /* @__PURE__ */ a("div", {
|
|
1711
|
+
className: "sticker-admin-page__state",
|
|
1712
|
+
children: "No stickers uploaded yet."
|
|
1713
|
+
}) : /* @__PURE__ */ a("div", {
|
|
1714
|
+
className: "sticker-admin-grid",
|
|
1715
|
+
children: m.map((e, t) => {
|
|
1716
|
+
let n = w === e.filename, r = M === e.filename, i = P === e.filename && M !== e.filename;
|
|
1717
|
+
return /* @__PURE__ */ o("div", {
|
|
1718
|
+
className: [
|
|
1719
|
+
"sticker-admin-card",
|
|
1720
|
+
r ? "sticker-admin-card--dragging" : "",
|
|
1721
|
+
i ? "sticker-admin-card--drop-target" : ""
|
|
1722
|
+
].filter(Boolean).join(" "),
|
|
1723
|
+
onDragOver: (t) => V(t, e.filename),
|
|
1724
|
+
onDrop: (t) => H(t, e.filename),
|
|
1725
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1726
|
+
className: "sticker-admin-card__preview sticker-admin-card__preview--draggable",
|
|
1727
|
+
draggable: !b && !S && !w,
|
|
1728
|
+
onDragStart: (t) => B(t, e.filename),
|
|
1729
|
+
onDragEnd: z,
|
|
1730
|
+
title: "Drag to reorder",
|
|
1731
|
+
children: [/* @__PURE__ */ a("img", {
|
|
1732
|
+
src: e.url,
|
|
1733
|
+
alt: ee(e),
|
|
1734
|
+
loading: "lazy",
|
|
1735
|
+
draggable: !1
|
|
1736
|
+
}), /* @__PURE__ */ o("span", {
|
|
1737
|
+
className: "sticker-admin-card__drag-badge",
|
|
1738
|
+
"aria-hidden": "true",
|
|
1739
|
+
children: [/* @__PURE__ */ a("svg", {
|
|
1740
|
+
viewBox: "0 0 24 24",
|
|
1741
|
+
focusable: "false",
|
|
1742
|
+
children: /* @__PURE__ */ a("path", {
|
|
1743
|
+
fill: "currentColor",
|
|
1744
|
+
d: "M11 23v-3.17l-1.41 1.41-1.42-1.41L12 16l3.83 3.83-1.42 1.41L13 19.83V23h-2Zm-3-7-3.83-3.83 3.83-3.83 1.42 1.41L8.01 11H11v2H8.01l1.41 1.41L8 16Zm8 0-1.41-1.41L15.99 13H13v-2h2.99l-1.4-1.42L16 8.17 19.83 12 16 15.83ZM12 8 8.17 4.17l1.42-1.41L11 4.17V1h2v3.17l1.41-1.41 1.42 1.41L12 8Z"
|
|
1745
|
+
})
|
|
1746
|
+
}), /* @__PURE__ */ a("span", { children: "Drag to reorder" })]
|
|
1747
|
+
})]
|
|
1748
|
+
}), /* @__PURE__ */ o("div", {
|
|
1749
|
+
className: "sticker-admin-card__body",
|
|
1750
|
+
children: [
|
|
1751
|
+
/* @__PURE__ */ a("div", {
|
|
1752
|
+
className: "sticker-admin-card__filename",
|
|
1753
|
+
title: e.filename,
|
|
1754
|
+
children: e.filename
|
|
1755
|
+
}),
|
|
1756
|
+
/* @__PURE__ */ a("label", {
|
|
1757
|
+
className: "sticker-admin-card__field",
|
|
1758
|
+
children: /* @__PURE__ */ o("div", {
|
|
1759
|
+
className: "sticker-admin-card__shortcode-input",
|
|
1760
|
+
children: [
|
|
1761
|
+
/* @__PURE__ */ a("span", { children: ":" }),
|
|
1762
|
+
/* @__PURE__ */ a("input", {
|
|
1763
|
+
type: "text",
|
|
1764
|
+
value: e.shortcode ?? "",
|
|
1765
|
+
placeholder: "viking",
|
|
1766
|
+
maxLength: 12,
|
|
1767
|
+
"aria-label": `Shortcode for ${e.filename}`,
|
|
1768
|
+
onChange: (t) => U(e.filename, t.target.value.toLowerCase())
|
|
1769
|
+
}),
|
|
1770
|
+
/* @__PURE__ */ a("span", { children: ":" })
|
|
1771
|
+
]
|
|
1772
|
+
})
|
|
1773
|
+
}),
|
|
1774
|
+
/* @__PURE__ */ a("div", {
|
|
1775
|
+
className: "sticker-admin-card__actions",
|
|
1776
|
+
children: /* @__PURE__ */ a("button", {
|
|
1777
|
+
className: "btn btn--danger",
|
|
1778
|
+
onClick: () => Y(e.filename),
|
|
1779
|
+
disabled: n || b || S,
|
|
1780
|
+
children: n ? "Deleting…" : "Delete"
|
|
1781
|
+
})
|
|
1782
|
+
})
|
|
1783
|
+
]
|
|
1784
|
+
})]
|
|
1785
|
+
}, e.filename);
|
|
1786
|
+
})
|
|
1787
|
+
})
|
|
1788
|
+
]
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
//#endregion
|
|
1792
|
+
//#region src/components/ThemeAdminPage.tsx
|
|
1793
|
+
var ne = [
|
|
1794
|
+
{
|
|
1795
|
+
key: "--relaya-color-bg",
|
|
1796
|
+
label: "Chat background",
|
|
1797
|
+
hint: "Outermost container background",
|
|
1798
|
+
lightDefault: "#f0f2f5",
|
|
1799
|
+
darkDefault: "#0d1117"
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
key: "--relaya-color-message-bg",
|
|
1803
|
+
label: "Others' message bubbles",
|
|
1804
|
+
hint: "Bubble background for received messages",
|
|
1805
|
+
lightDefault: "#e9ecef",
|
|
1806
|
+
darkDefault: "#21262d"
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
key: "--relaya-color-message-own-bg",
|
|
1810
|
+
label: "Your message bubbles",
|
|
1811
|
+
hint: "Bubble background for sent messages",
|
|
1812
|
+
lightDefault: "#007aff",
|
|
1813
|
+
darkDefault: "#1f6feb"
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
key: "--relaya-color-text",
|
|
1817
|
+
label: "Body text",
|
|
1818
|
+
hint: "Primary text colour",
|
|
1819
|
+
lightDefault: "#1a1a2e",
|
|
1820
|
+
darkDefault: "#e6edf3"
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
key: "--relaya-color-text-secondary",
|
|
1824
|
+
label: "Secondary / muted text",
|
|
1825
|
+
hint: "Timestamps, labels",
|
|
1826
|
+
lightDefault: "#6c757d",
|
|
1827
|
+
darkDefault: "#8b949e"
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
key: "--relaya-color-input-bg",
|
|
1831
|
+
label: "Message input background",
|
|
1832
|
+
hint: "Text-entry field background",
|
|
1833
|
+
lightDefault: "#f8f9fa",
|
|
1834
|
+
darkDefault: "#21262d"
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
key: "--relaya-color-input-text",
|
|
1838
|
+
label: "Message input text",
|
|
1839
|
+
hint: "Text typed in the input",
|
|
1840
|
+
lightDefault: "#1a1a2e",
|
|
1841
|
+
darkDefault: "#e6edf3"
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
key: "--relaya-color-btn-bg",
|
|
1845
|
+
label: "Send button background",
|
|
1846
|
+
hint: "Primary action button background",
|
|
1847
|
+
lightDefault: "#007aff",
|
|
1848
|
+
darkDefault: "#1f6feb"
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
key: "--relaya-color-btn-text",
|
|
1852
|
+
label: "Send button icon",
|
|
1853
|
+
hint: "Icon colour on the send button",
|
|
1854
|
+
lightDefault: "#ffffff",
|
|
1855
|
+
darkDefault: "#ffffff"
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
key: "--relaya-color-name-mod",
|
|
1859
|
+
label: "Moderator name colour",
|
|
1860
|
+
hint: "Display name colour for moderators",
|
|
1861
|
+
lightDefault: "#007aff",
|
|
1862
|
+
darkDefault: "#58a6ff"
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
key: "--relaya-color-link",
|
|
1866
|
+
label: "Link colour",
|
|
1867
|
+
hint: "Hyperlink default state",
|
|
1868
|
+
lightDefault: "#007aff",
|
|
1869
|
+
darkDefault: "#58a6ff"
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
key: "--relaya-color-link-active",
|
|
1873
|
+
label: "Link hover/active colour",
|
|
1874
|
+
hint: "Hyperlink hover/active state",
|
|
1875
|
+
lightDefault: "#0062cc",
|
|
1876
|
+
darkDefault: "#79b8ff"
|
|
1877
|
+
}
|
|
1878
|
+
], re = {};
|
|
1879
|
+
function X(e) {
|
|
1880
|
+
return /^#[0-9a-fA-F]{6}$/.test(e.trim());
|
|
1881
|
+
}
|
|
1882
|
+
function ie(e) {
|
|
1883
|
+
let t = e.trim();
|
|
1884
|
+
return /^[0-9a-fA-F]{6}$/.test(t) ? `#${t}` : /^#[0-9a-fA-F]{3}$/.test(t) ? `#${t[1]}${t[1]}${t[2]}${t[2]}${t[3]}${t[3]}` : t;
|
|
1885
|
+
}
|
|
1886
|
+
function ae({ stationSlug: e, getToken: n, onClose: i }) {
|
|
1887
|
+
let { theme: s, loading: c, saving: l, error: d, loadTheme: f, saveTheme: p, resetTheme: m } = T(e, n), [h, g] = r(u.theme), [_, v] = r({
|
|
1888
|
+
light: {},
|
|
1889
|
+
dark: {}
|
|
1890
|
+
}), [y, b] = r(!1), [x, S] = r(null), [C, w] = r({});
|
|
1891
|
+
t(() => {
|
|
1892
|
+
let e = document.createElement("div");
|
|
1893
|
+
e.style.display = "none", document.documentElement.appendChild(e);
|
|
1894
|
+
let t = {};
|
|
1895
|
+
for (let { key: n } of ne) {
|
|
1896
|
+
e.style.setProperty("background-color", `var(${n})`);
|
|
1897
|
+
let r = getComputedStyle(e).backgroundColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
|
1898
|
+
r && (t[n] = "#" + [
|
|
1899
|
+
r[1],
|
|
1900
|
+
r[2],
|
|
1901
|
+
r[3]
|
|
1902
|
+
].map((e) => parseInt(e).toString(16).padStart(2, "0")).join(""));
|
|
1903
|
+
}
|
|
1904
|
+
document.documentElement.removeChild(e), w(t);
|
|
1905
|
+
}, []), t(() => {
|
|
1906
|
+
f();
|
|
1907
|
+
}, []), t(() => {
|
|
1908
|
+
v({
|
|
1909
|
+
light: { ...s.light },
|
|
1910
|
+
dark: { ...s.dark }
|
|
1911
|
+
}), b(!1);
|
|
1912
|
+
}, [s]);
|
|
1913
|
+
function E(e, t) {
|
|
1914
|
+
let n = ie(t);
|
|
1915
|
+
v((t) => ({
|
|
1916
|
+
...t,
|
|
1917
|
+
[h]: {
|
|
1918
|
+
...t[h],
|
|
1919
|
+
[e]: n
|
|
1920
|
+
}
|
|
1921
|
+
})), b(!0), S(null);
|
|
1922
|
+
}
|
|
1923
|
+
async function D() {
|
|
1924
|
+
let e = {
|
|
1925
|
+
light: {},
|
|
1926
|
+
dark: {}
|
|
1927
|
+
};
|
|
1928
|
+
for (let t of ["light", "dark"]) for (let [n, r] of Object.entries(_[t])) r && X(r) && (e[t][n] = r);
|
|
1929
|
+
await p(e, h), b(!1), S("Theme saved.");
|
|
1930
|
+
}
|
|
1931
|
+
async function O() {
|
|
1932
|
+
confirm("Remove all saved colour overrides and revert to the default theme?") && (await m(h), b(!1), S("Theme reset to defaults."));
|
|
1933
|
+
}
|
|
1934
|
+
let k = _[h] ?? re;
|
|
1935
|
+
return /* @__PURE__ */ o("div", {
|
|
1936
|
+
className: "theme-admin-page",
|
|
1937
|
+
children: [
|
|
1938
|
+
/* @__PURE__ */ o("div", {
|
|
1939
|
+
className: "theme-admin-page__topbar",
|
|
1940
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1941
|
+
className: "theme-admin-page__title-group",
|
|
1942
|
+
children: [/* @__PURE__ */ o("div", {
|
|
1943
|
+
className: "theme-admin-page__title-row",
|
|
1944
|
+
children: [
|
|
1945
|
+
i && /* @__PURE__ */ a("button", {
|
|
1946
|
+
className: "btn btn--ghost theme-admin-page__back",
|
|
1947
|
+
onClick: i,
|
|
1948
|
+
children: "← Back"
|
|
1949
|
+
}),
|
|
1950
|
+
/* @__PURE__ */ a("h2", {
|
|
1951
|
+
className: "theme-admin-page__title",
|
|
1952
|
+
children: "Colour theme"
|
|
1953
|
+
}),
|
|
1954
|
+
x && /* @__PURE__ */ a("div", {
|
|
1955
|
+
className: "theme-admin-page__status-inline",
|
|
1956
|
+
children: x
|
|
1957
|
+
})
|
|
1958
|
+
]
|
|
1959
|
+
}), /* @__PURE__ */ a("p", {
|
|
1960
|
+
className: "theme-admin-page__subtitle",
|
|
1961
|
+
children: "Override the default colours for this space. Only saved values are stored; unset fields fall back to the default theme."
|
|
1962
|
+
})]
|
|
1963
|
+
}), /* @__PURE__ */ o("div", {
|
|
1964
|
+
className: "theme-admin-page__header-actions",
|
|
1965
|
+
children: [/* @__PURE__ */ a("button", {
|
|
1966
|
+
className: "btn btn--ghost",
|
|
1967
|
+
onClick: O,
|
|
1968
|
+
disabled: l || c,
|
|
1969
|
+
children: "Reset to defaults"
|
|
1970
|
+
}), /* @__PURE__ */ a("button", {
|
|
1971
|
+
className: "btn btn--primary",
|
|
1972
|
+
onClick: D,
|
|
1973
|
+
disabled: l || c || !y,
|
|
1974
|
+
children: l ? "Saving…" : "Save colours"
|
|
1975
|
+
})]
|
|
1976
|
+
})]
|
|
1977
|
+
}),
|
|
1978
|
+
d && /* @__PURE__ */ a("div", {
|
|
1979
|
+
className: "theme-admin-page__error",
|
|
1980
|
+
children: d
|
|
1981
|
+
}),
|
|
1982
|
+
/* @__PURE__ */ o("div", {
|
|
1983
|
+
className: "theme-admin-page__tabs",
|
|
1984
|
+
children: [/* @__PURE__ */ a("button", {
|
|
1985
|
+
className: `theme-admin-page__tab${h === "light" ? " theme-admin-page__tab--active" : ""}`,
|
|
1986
|
+
onClick: () => g("light"),
|
|
1987
|
+
children: "☀ Light"
|
|
1988
|
+
}), /* @__PURE__ */ a("button", {
|
|
1989
|
+
className: `theme-admin-page__tab${h === "dark" ? " theme-admin-page__tab--active" : ""}`,
|
|
1990
|
+
onClick: () => g("dark"),
|
|
1991
|
+
children: "☾ Dark"
|
|
1992
|
+
})]
|
|
1993
|
+
}),
|
|
1994
|
+
c ? /* @__PURE__ */ a("div", {
|
|
1995
|
+
className: "theme-admin-page__state",
|
|
1996
|
+
children: "Loading theme…"
|
|
1997
|
+
}) : /* @__PURE__ */ a("div", {
|
|
1998
|
+
className: "theme-admin-list",
|
|
1999
|
+
children: ne.map(({ key: e, label: t, hint: n, lightDefault: r, darkDefault: i }) => {
|
|
2000
|
+
let s = h === u.theme ? C[e] ?? (h === "light" ? r : i) : h === "light" ? r : i, c = k[e] ?? "", l = c ? ie(c) : s, d = c !== "" && !X(c);
|
|
2001
|
+
return /* @__PURE__ */ o("div", {
|
|
2002
|
+
className: "theme-admin-row",
|
|
2003
|
+
title: n,
|
|
2004
|
+
children: [
|
|
2005
|
+
/* @__PURE__ */ a("input", {
|
|
2006
|
+
type: "color",
|
|
2007
|
+
className: "theme-admin-row__swatch",
|
|
2008
|
+
value: X(l) ? l : s,
|
|
2009
|
+
onChange: (t) => E(e, t.target.value),
|
|
2010
|
+
title: t
|
|
2011
|
+
}),
|
|
2012
|
+
/* @__PURE__ */ a("label", {
|
|
2013
|
+
className: "theme-admin-row__label",
|
|
2014
|
+
children: t
|
|
2015
|
+
}),
|
|
2016
|
+
/* @__PURE__ */ a("input", {
|
|
2017
|
+
type: "text",
|
|
2018
|
+
className: `theme-admin-row__hex${d ? " theme-admin-row__hex--invalid" : ""}`,
|
|
2019
|
+
value: c,
|
|
2020
|
+
placeholder: s,
|
|
2021
|
+
maxLength: 9,
|
|
2022
|
+
onChange: (t) => E(e, t.target.value)
|
|
2023
|
+
})
|
|
2024
|
+
]
|
|
2025
|
+
}, e);
|
|
2026
|
+
})
|
|
2027
|
+
})
|
|
2028
|
+
]
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
//#endregion
|
|
2032
|
+
//#region src/components/ExportAdminPage.tsx
|
|
2033
|
+
function Z(e) {
|
|
2034
|
+
return e.toISOString().slice(0, 10);
|
|
2035
|
+
}
|
|
2036
|
+
function oe() {
|
|
2037
|
+
let e = /* @__PURE__ */ new Date();
|
|
2038
|
+
return {
|
|
2039
|
+
from: Z(/* @__PURE__ */ new Date(e.getTime() - 90 * 864e5)),
|
|
2040
|
+
to: Z(e),
|
|
2041
|
+
excludeReportedBefore: Z(/* @__PURE__ */ new Date(e.getTime() - 365 * 864e5))
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
var se = "A date is required — reported messages are kept indefinitely for compliance. Choose a date to limit how far back they appear in this export.";
|
|
2045
|
+
function ce({ stationSlug: e, getToken: t }) {
|
|
2046
|
+
let n = oe(), [i, s] = r(n.from), [c, l] = r(n.to), [u, d] = r(n.excludeReportedBefore), [f, p] = r(null), [m, h] = r(!1), [g, _] = r(null), [v, y] = r(!1);
|
|
2047
|
+
function b() {
|
|
2048
|
+
p(u ? null : se);
|
|
2049
|
+
}
|
|
2050
|
+
async function x() {
|
|
2051
|
+
if (!u) {
|
|
2052
|
+
p(se);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
p(null), _(null), h(!0);
|
|
2056
|
+
try {
|
|
2057
|
+
let n = new URLSearchParams();
|
|
2058
|
+
i && n.set("from", i + "T00:00:00Z"), c && n.set("to", c + "T23:59:59Z"), n.set("excludeReportedBefore", u + "T00:00:00Z");
|
|
2059
|
+
let r = `/api/chat/${e}/export/messages?${n.toString()}`, a = t(), o = {};
|
|
2060
|
+
a && (o.Authorization = `Bearer ${a}`);
|
|
2061
|
+
let s = await fetch(r, {
|
|
2062
|
+
credentials: "include",
|
|
2063
|
+
headers: o
|
|
2064
|
+
});
|
|
2065
|
+
if (s.ok) {
|
|
2066
|
+
let t = await s.blob(), n = (s.headers.get("Content-Disposition") ?? "").match(/filename="([^"]+)"/), r = n ? n[1] : `${e}-export.csv`, i = document.createElement("a");
|
|
2067
|
+
i.href = URL.createObjectURL(t), i.download = r, document.body.appendChild(i), i.click(), i.remove(), URL.revokeObjectURL(i.href);
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
let l = {};
|
|
2071
|
+
try {
|
|
2072
|
+
l = await s.json();
|
|
2073
|
+
} catch {}
|
|
2074
|
+
if (s.status === 403) {
|
|
2075
|
+
let e = l.error;
|
|
2076
|
+
if (typeof e == "object" && e?.code === "TIER_LIMIT") {
|
|
2077
|
+
y(!0);
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
_("You do not have permission to export chat history for this space.");
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
if (s.status === 429) {
|
|
2084
|
+
_(`Export rate limit reached — you can export once per hour. Try again in ${Math.ceil((l.retryAfter ?? 3600) / 60)} minutes.`);
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
if (s.status === 413) {
|
|
2088
|
+
_("Too many messages in this date range. Please narrow the date range and try again.");
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
_((typeof l.error == "object" ? l.error?.message : typeof l.error == "string" ? l.error : void 0) ?? "Export failed. Please try again.");
|
|
2092
|
+
} catch {
|
|
2093
|
+
_("Export failed due to a network error. Please try again.");
|
|
2094
|
+
} finally {
|
|
2095
|
+
h(!1);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
return v ? /* @__PURE__ */ a("div", {
|
|
2099
|
+
className: "export-admin-page",
|
|
2100
|
+
children: /* @__PURE__ */ a("p", {
|
|
2101
|
+
className: "export-admin-page__unavailable",
|
|
2102
|
+
children: "Chat history export is not available on your current plan."
|
|
2103
|
+
})
|
|
2104
|
+
}) : /* @__PURE__ */ o("div", {
|
|
2105
|
+
className: "export-admin-page",
|
|
2106
|
+
children: [
|
|
2107
|
+
/* @__PURE__ */ a("p", {
|
|
2108
|
+
className: "export-admin-page__notice",
|
|
2109
|
+
children: "Exports contain user display names and message content. Handle in accordance with your privacy policy."
|
|
2110
|
+
}),
|
|
2111
|
+
g && /* @__PURE__ */ a("div", {
|
|
2112
|
+
className: "export-admin-page__error",
|
|
2113
|
+
children: g
|
|
2114
|
+
}),
|
|
2115
|
+
/* @__PURE__ */ o("div", {
|
|
2116
|
+
className: "export-admin-page__form",
|
|
2117
|
+
children: [
|
|
2118
|
+
/* @__PURE__ */ o("div", {
|
|
2119
|
+
className: "export-admin-page__date-row",
|
|
2120
|
+
children: [/* @__PURE__ */ o("label", {
|
|
2121
|
+
className: "export-admin-page__label",
|
|
2122
|
+
children: ["From:", /* @__PURE__ */ a("input", {
|
|
2123
|
+
type: "date",
|
|
2124
|
+
className: "export-admin-page__date-input",
|
|
2125
|
+
value: i,
|
|
2126
|
+
onChange: (e) => s(e.target.value)
|
|
2127
|
+
})]
|
|
2128
|
+
}), /* @__PURE__ */ o("label", {
|
|
2129
|
+
className: "export-admin-page__label",
|
|
2130
|
+
children: ["To:", /* @__PURE__ */ a("input", {
|
|
2131
|
+
type: "date",
|
|
2132
|
+
className: "export-admin-page__date-input",
|
|
2133
|
+
value: c,
|
|
2134
|
+
onChange: (e) => l(e.target.value)
|
|
2135
|
+
})]
|
|
2136
|
+
})]
|
|
2137
|
+
}),
|
|
2138
|
+
/* @__PURE__ */ o("div", {
|
|
2139
|
+
className: "export-admin-page__exclude-row",
|
|
2140
|
+
children: [/* @__PURE__ */ o("label", {
|
|
2141
|
+
className: "export-admin-page__label",
|
|
2142
|
+
children: ["Exclude reported messages older than:", /* @__PURE__ */ a("input", {
|
|
2143
|
+
type: "date",
|
|
2144
|
+
className: "export-admin-page__date-input",
|
|
2145
|
+
value: u,
|
|
2146
|
+
onChange: (e) => {
|
|
2147
|
+
d(e.target.value), p(null);
|
|
2148
|
+
},
|
|
2149
|
+
onBlur: b,
|
|
2150
|
+
required: !0
|
|
2151
|
+
})]
|
|
2152
|
+
}), f && /* @__PURE__ */ a("p", {
|
|
2153
|
+
className: "export-admin-page__field-error",
|
|
2154
|
+
children: f
|
|
2155
|
+
})]
|
|
2156
|
+
}),
|
|
2157
|
+
/* @__PURE__ */ a("button", {
|
|
2158
|
+
className: "btn btn--primary export-admin-page__btn",
|
|
2159
|
+
onClick: x,
|
|
2160
|
+
disabled: m,
|
|
2161
|
+
children: m ? "Preparing…" : "Download CSV →"
|
|
2162
|
+
}),
|
|
2163
|
+
/* @__PURE__ */ a("p", {
|
|
2164
|
+
className: "export-admin-page__hint",
|
|
2165
|
+
children: "Export is limited to the space’s retention window. Rate limited: 1 export per hour."
|
|
2166
|
+
})
|
|
2167
|
+
]
|
|
2168
|
+
})
|
|
2169
|
+
]
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
//#endregion
|
|
2173
|
+
//#region src/components/countryNames.ts
|
|
2174
|
+
var Q = {
|
|
2175
|
+
AF: "Afghanistan",
|
|
2176
|
+
AX: "Åland Islands",
|
|
2177
|
+
AL: "Albania",
|
|
2178
|
+
DZ: "Algeria",
|
|
2179
|
+
AS: "American Samoa",
|
|
2180
|
+
AD: "Andorra",
|
|
2181
|
+
AO: "Angola",
|
|
2182
|
+
AI: "Anguilla",
|
|
2183
|
+
AQ: "Antarctica",
|
|
2184
|
+
AG: "Antigua and Barbuda",
|
|
2185
|
+
AR: "Argentina",
|
|
2186
|
+
AM: "Armenia",
|
|
2187
|
+
AW: "Aruba",
|
|
2188
|
+
AU: "Australia",
|
|
2189
|
+
AT: "Austria",
|
|
2190
|
+
AZ: "Azerbaijan",
|
|
2191
|
+
BS: "Bahamas",
|
|
2192
|
+
BH: "Bahrain",
|
|
2193
|
+
BD: "Bangladesh",
|
|
2194
|
+
BB: "Barbados",
|
|
2195
|
+
BY: "Belarus",
|
|
2196
|
+
BE: "Belgium",
|
|
2197
|
+
BZ: "Belize",
|
|
2198
|
+
BJ: "Benin",
|
|
2199
|
+
BM: "Bermuda",
|
|
2200
|
+
BT: "Bhutan",
|
|
2201
|
+
BO: "Bolivia",
|
|
2202
|
+
BQ: "Bonaire, Sint Eustatius and Saba",
|
|
2203
|
+
BA: "Bosnia and Herzegovina",
|
|
2204
|
+
BW: "Botswana",
|
|
2205
|
+
BV: "Bouvet Island",
|
|
2206
|
+
BR: "Brazil",
|
|
2207
|
+
IO: "British Indian Ocean Territory",
|
|
2208
|
+
BN: "Brunei",
|
|
2209
|
+
BG: "Bulgaria",
|
|
2210
|
+
BF: "Burkina Faso",
|
|
2211
|
+
BI: "Burundi",
|
|
2212
|
+
CV: "Cabo Verde",
|
|
2213
|
+
KH: "Cambodia",
|
|
2214
|
+
CM: "Cameroon",
|
|
2215
|
+
CA: "Canada",
|
|
2216
|
+
KY: "Cayman Islands",
|
|
2217
|
+
CF: "Central African Republic",
|
|
2218
|
+
TD: "Chad",
|
|
2219
|
+
CL: "Chile",
|
|
2220
|
+
CN: "China",
|
|
2221
|
+
CX: "Christmas Island",
|
|
2222
|
+
CC: "Cocos Islands",
|
|
2223
|
+
CO: "Colombia",
|
|
2224
|
+
KM: "Comoros",
|
|
2225
|
+
CG: "Congo",
|
|
2226
|
+
CD: "Congo (DRC)",
|
|
2227
|
+
CK: "Cook Islands",
|
|
2228
|
+
CR: "Costa Rica",
|
|
2229
|
+
CI: "Côte d'Ivoire",
|
|
2230
|
+
HR: "Croatia",
|
|
2231
|
+
CU: "Cuba",
|
|
2232
|
+
CW: "Curaçao",
|
|
2233
|
+
CY: "Cyprus",
|
|
2234
|
+
CZ: "Czechia",
|
|
2235
|
+
DK: "Denmark",
|
|
2236
|
+
DJ: "Djibouti",
|
|
2237
|
+
DM: "Dominica",
|
|
2238
|
+
DO: "Dominican Republic",
|
|
2239
|
+
EC: "Ecuador",
|
|
2240
|
+
EG: "Egypt",
|
|
2241
|
+
SV: "El Salvador",
|
|
2242
|
+
GQ: "Equatorial Guinea",
|
|
2243
|
+
ER: "Eritrea",
|
|
2244
|
+
EE: "Estonia",
|
|
2245
|
+
SZ: "Eswatini",
|
|
2246
|
+
ET: "Ethiopia",
|
|
2247
|
+
FK: "Falkland Islands",
|
|
2248
|
+
FO: "Faroe Islands",
|
|
2249
|
+
FJ: "Fiji",
|
|
2250
|
+
FI: "Finland",
|
|
2251
|
+
FR: "France",
|
|
2252
|
+
GF: "French Guiana",
|
|
2253
|
+
PF: "French Polynesia",
|
|
2254
|
+
TF: "French Southern Territories",
|
|
2255
|
+
GA: "Gabon",
|
|
2256
|
+
GM: "Gambia",
|
|
2257
|
+
GE: "Georgia",
|
|
2258
|
+
DE: "Germany",
|
|
2259
|
+
GH: "Ghana",
|
|
2260
|
+
GI: "Gibraltar",
|
|
2261
|
+
GR: "Greece",
|
|
2262
|
+
GL: "Greenland",
|
|
2263
|
+
GD: "Grenada",
|
|
2264
|
+
GP: "Guadeloupe",
|
|
2265
|
+
GU: "Guam",
|
|
2266
|
+
GT: "Guatemala",
|
|
2267
|
+
GG: "Guernsey",
|
|
2268
|
+
GN: "Guinea",
|
|
2269
|
+
GW: "Guinea-Bissau",
|
|
2270
|
+
GY: "Guyana",
|
|
2271
|
+
HT: "Haiti",
|
|
2272
|
+
HM: "Heard Island and McDonald Islands",
|
|
2273
|
+
VA: "Holy See",
|
|
2274
|
+
HN: "Honduras",
|
|
2275
|
+
HK: "Hong Kong",
|
|
2276
|
+
HU: "Hungary",
|
|
2277
|
+
IS: "Iceland",
|
|
2278
|
+
IN: "India",
|
|
2279
|
+
ID: "Indonesia",
|
|
2280
|
+
IR: "Iran",
|
|
2281
|
+
IQ: "Iraq",
|
|
2282
|
+
IE: "Ireland",
|
|
2283
|
+
IM: "Isle of Man",
|
|
2284
|
+
IL: "Israel",
|
|
2285
|
+
IT: "Italy",
|
|
2286
|
+
JM: "Jamaica",
|
|
2287
|
+
JP: "Japan",
|
|
2288
|
+
JE: "Jersey",
|
|
2289
|
+
JO: "Jordan",
|
|
2290
|
+
KZ: "Kazakhstan",
|
|
2291
|
+
KE: "Kenya",
|
|
2292
|
+
KI: "Kiribati",
|
|
2293
|
+
KP: "North Korea",
|
|
2294
|
+
KR: "South Korea",
|
|
2295
|
+
KW: "Kuwait",
|
|
2296
|
+
KG: "Kyrgyzstan",
|
|
2297
|
+
LA: "Laos",
|
|
2298
|
+
LV: "Latvia",
|
|
2299
|
+
LB: "Lebanon",
|
|
2300
|
+
LS: "Lesotho",
|
|
2301
|
+
LR: "Liberia",
|
|
2302
|
+
LY: "Libya",
|
|
2303
|
+
LI: "Liechtenstein",
|
|
2304
|
+
LT: "Lithuania",
|
|
2305
|
+
LU: "Luxembourg",
|
|
2306
|
+
MO: "Macao",
|
|
2307
|
+
MG: "Madagascar",
|
|
2308
|
+
MW: "Malawi",
|
|
2309
|
+
MY: "Malaysia",
|
|
2310
|
+
MV: "Maldives",
|
|
2311
|
+
ML: "Mali",
|
|
2312
|
+
MT: "Malta",
|
|
2313
|
+
MH: "Marshall Islands",
|
|
2314
|
+
MQ: "Martinique",
|
|
2315
|
+
MR: "Mauritania",
|
|
2316
|
+
MU: "Mauritius",
|
|
2317
|
+
YT: "Mayotte",
|
|
2318
|
+
MX: "Mexico",
|
|
2319
|
+
FM: "Micronesia",
|
|
2320
|
+
MD: "Moldova",
|
|
2321
|
+
MC: "Monaco",
|
|
2322
|
+
MN: "Mongolia",
|
|
2323
|
+
ME: "Montenegro",
|
|
2324
|
+
MS: "Montserrat",
|
|
2325
|
+
MA: "Morocco",
|
|
2326
|
+
MZ: "Mozambique",
|
|
2327
|
+
MM: "Myanmar",
|
|
2328
|
+
NA: "Namibia",
|
|
2329
|
+
NR: "Nauru",
|
|
2330
|
+
NP: "Nepal",
|
|
2331
|
+
NL: "Netherlands",
|
|
2332
|
+
NC: "New Caledonia",
|
|
2333
|
+
NZ: "New Zealand",
|
|
2334
|
+
NI: "Nicaragua",
|
|
2335
|
+
NE: "Niger",
|
|
2336
|
+
NG: "Nigeria",
|
|
2337
|
+
NU: "Niue",
|
|
2338
|
+
NF: "Norfolk Island",
|
|
2339
|
+
MK: "North Macedonia",
|
|
2340
|
+
MP: "Northern Mariana Islands",
|
|
2341
|
+
NO: "Norway",
|
|
2342
|
+
OM: "Oman",
|
|
2343
|
+
PK: "Pakistan",
|
|
2344
|
+
PW: "Palau",
|
|
2345
|
+
PS: "Palestine",
|
|
2346
|
+
PA: "Panama",
|
|
2347
|
+
PG: "Papua New Guinea",
|
|
2348
|
+
PY: "Paraguay",
|
|
2349
|
+
PE: "Peru",
|
|
2350
|
+
PH: "Philippines",
|
|
2351
|
+
PN: "Pitcairn",
|
|
2352
|
+
PL: "Poland",
|
|
2353
|
+
PT: "Portugal",
|
|
2354
|
+
PR: "Puerto Rico",
|
|
2355
|
+
QA: "Qatar",
|
|
2356
|
+
RE: "Réunion",
|
|
2357
|
+
RO: "Romania",
|
|
2358
|
+
RU: "Russia",
|
|
2359
|
+
RW: "Rwanda",
|
|
2360
|
+
BL: "Saint Barthélemy",
|
|
2361
|
+
SH: "Saint Helena",
|
|
2362
|
+
KN: "Saint Kitts and Nevis",
|
|
2363
|
+
LC: "Saint Lucia",
|
|
2364
|
+
MF: "Saint Martin",
|
|
2365
|
+
PM: "Saint Pierre and Miquelon",
|
|
2366
|
+
VC: "Saint Vincent and the Grenadines",
|
|
2367
|
+
WS: "Samoa",
|
|
2368
|
+
SM: "San Marino",
|
|
2369
|
+
ST: "Sao Tome and Principe",
|
|
2370
|
+
SA: "Saudi Arabia",
|
|
2371
|
+
SN: "Senegal",
|
|
2372
|
+
RS: "Serbia",
|
|
2373
|
+
SC: "Seychelles",
|
|
2374
|
+
SL: "Sierra Leone",
|
|
2375
|
+
SG: "Singapore",
|
|
2376
|
+
SX: "Sint Maarten",
|
|
2377
|
+
SK: "Slovakia",
|
|
2378
|
+
SI: "Slovenia",
|
|
2379
|
+
SB: "Solomon Islands",
|
|
2380
|
+
SO: "Somalia",
|
|
2381
|
+
ZA: "South Africa",
|
|
2382
|
+
GS: "South Georgia",
|
|
2383
|
+
SS: "South Sudan",
|
|
2384
|
+
ES: "Spain",
|
|
2385
|
+
LK: "Sri Lanka",
|
|
2386
|
+
SD: "Sudan",
|
|
2387
|
+
SR: "Suriname",
|
|
2388
|
+
SJ: "Svalbard and Jan Mayen",
|
|
2389
|
+
SE: "Sweden",
|
|
2390
|
+
CH: "Switzerland",
|
|
2391
|
+
SY: "Syria",
|
|
2392
|
+
TW: "Taiwan",
|
|
2393
|
+
TJ: "Tajikistan",
|
|
2394
|
+
TZ: "Tanzania",
|
|
2395
|
+
TH: "Thailand",
|
|
2396
|
+
TL: "Timor-Leste",
|
|
2397
|
+
TG: "Togo",
|
|
2398
|
+
TK: "Tokelau",
|
|
2399
|
+
TO: "Tonga",
|
|
2400
|
+
TT: "Trinidad and Tobago",
|
|
2401
|
+
TN: "Tunisia",
|
|
2402
|
+
TR: "Turkey",
|
|
2403
|
+
TM: "Turkmenistan",
|
|
2404
|
+
TC: "Turks and Caicos Islands",
|
|
2405
|
+
TV: "Tuvalu",
|
|
2406
|
+
UG: "Uganda",
|
|
2407
|
+
UA: "Ukraine",
|
|
2408
|
+
AE: "United Arab Emirates",
|
|
2409
|
+
GB: "United Kingdom",
|
|
2410
|
+
US: "United States",
|
|
2411
|
+
UM: "US Minor Outlying Islands",
|
|
2412
|
+
UY: "Uruguay",
|
|
2413
|
+
UZ: "Uzbekistan",
|
|
2414
|
+
VU: "Vanuatu",
|
|
2415
|
+
VE: "Venezuela",
|
|
2416
|
+
VN: "Vietnam",
|
|
2417
|
+
VG: "British Virgin Islands",
|
|
2418
|
+
VI: "US Virgin Islands",
|
|
2419
|
+
WF: "Wallis and Futuna",
|
|
2420
|
+
EH: "Western Sahara",
|
|
2421
|
+
YE: "Yemen",
|
|
2422
|
+
ZM: "Zambia",
|
|
2423
|
+
ZW: "Zimbabwe"
|
|
2424
|
+
};
|
|
2425
|
+
function le(e) {
|
|
2426
|
+
let t = Q[e.toUpperCase()];
|
|
2427
|
+
return t ? `${t} (${e.toUpperCase()})` : e.toUpperCase();
|
|
2428
|
+
}
|
|
2429
|
+
function ue(e) {
|
|
2430
|
+
let t = Q[e.toUpperCase()];
|
|
2431
|
+
return t ? `${t} \u00b7 ${e.toUpperCase()}` : e.toUpperCase();
|
|
2432
|
+
}
|
|
2433
|
+
var de = Object.entries(Q).map(([e, t]) => ({
|
|
2434
|
+
code: e,
|
|
2435
|
+
name: t
|
|
2436
|
+
})).sort((e, t) => e.name.localeCompare(t.name));
|
|
2437
|
+
//#endregion
|
|
2438
|
+
//#region src/components/GeoRestrictionsAdmin.tsx
|
|
2439
|
+
function $(e) {
|
|
2440
|
+
let t = e();
|
|
2441
|
+
return t ? { Authorization: `Bearer ${t}` } : {};
|
|
2442
|
+
}
|
|
2443
|
+
function fe(e) {
|
|
2444
|
+
if (e.isPermanent || !e.expiresAt) return "Permanent";
|
|
2445
|
+
let t = new Date(e.expiresAt);
|
|
2446
|
+
return t < /* @__PURE__ */ new Date() ? "Expired" : `Until ${t.toLocaleDateString(void 0, {
|
|
2447
|
+
month: "short",
|
|
2448
|
+
day: "numeric",
|
|
2449
|
+
year: "numeric"
|
|
2450
|
+
})}`;
|
|
2451
|
+
}
|
|
2452
|
+
function pe({ stationSlug: e, getToken: s }) {
|
|
2453
|
+
let c = `/api/chat/${e}`, [l, u] = r(!1), [d, f] = r(!1), [p, m] = r(null), [h, g] = r({
|
|
2454
|
+
mode: null,
|
|
2455
|
+
countries: []
|
|
2456
|
+
}), [_, v] = r([]), [y, b] = r(""), [x, S] = r(!1), C = n(null), [w, T] = r(""), [E, D] = r(!1), [O, k] = r(""), [A, j] = r(""), [M, N] = r(""), [P, F] = r(!1), [I, L] = r(null);
|
|
2457
|
+
t(() => {
|
|
2458
|
+
(async () => {
|
|
2459
|
+
u(!0), m(null);
|
|
2460
|
+
try {
|
|
2461
|
+
let e = {
|
|
2462
|
+
...$(s),
|
|
2463
|
+
"Content-Type": "application/json"
|
|
2464
|
+
}, [t, n] = await Promise.all([fetch(`${c}/geo/config`, {
|
|
2465
|
+
credentials: "include",
|
|
2466
|
+
headers: e
|
|
2467
|
+
}), fetch(`${c}/ip-bans`, {
|
|
2468
|
+
credentials: "include",
|
|
2469
|
+
headers: e
|
|
2470
|
+
})]);
|
|
2471
|
+
if (t.status === 403) {
|
|
2472
|
+
let e = {};
|
|
2473
|
+
try {
|
|
2474
|
+
e = await t.json();
|
|
2475
|
+
} catch {}
|
|
2476
|
+
if (typeof e.error == "object" && e.error?.code === "TIER_LIMIT") {
|
|
2477
|
+
f(!0), u(!1);
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (!t.ok) throw Error("Failed to load geo config");
|
|
2482
|
+
if (!n.ok) throw Error("Failed to load IP bans");
|
|
2483
|
+
let r = await t.json(), i = await n.json();
|
|
2484
|
+
g({
|
|
2485
|
+
mode: r.mode ?? null,
|
|
2486
|
+
countries: r.countries ?? []
|
|
2487
|
+
}), v(i.bans ?? []);
|
|
2488
|
+
} catch (e) {
|
|
2489
|
+
m(e?.message ?? "Failed to load geo restriction settings");
|
|
2490
|
+
} finally {
|
|
2491
|
+
u(!1);
|
|
2492
|
+
}
|
|
2493
|
+
})();
|
|
2494
|
+
}, []), t(() => {
|
|
2495
|
+
function e(e) {
|
|
2496
|
+
C.current && !C.current.contains(e.target) && S(!1);
|
|
2497
|
+
}
|
|
2498
|
+
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
2499
|
+
}, []);
|
|
2500
|
+
async function R(e, t) {
|
|
2501
|
+
let n = await fetch(`${c}${e}`, {
|
|
2502
|
+
method: "POST",
|
|
2503
|
+
credentials: "include",
|
|
2504
|
+
headers: {
|
|
2505
|
+
...$(s),
|
|
2506
|
+
"Content-Type": "application/json"
|
|
2507
|
+
},
|
|
2508
|
+
body: JSON.stringify(t)
|
|
2509
|
+
});
|
|
2510
|
+
if (!n.ok) {
|
|
2511
|
+
let e = "Request failed";
|
|
2512
|
+
try {
|
|
2513
|
+
let t = await n.json();
|
|
2514
|
+
e = t?.error?.message ?? t?.error ?? e;
|
|
2515
|
+
} catch {}
|
|
2516
|
+
throw Error(typeof e == "string" ? e : "Request failed");
|
|
2517
|
+
}
|
|
2518
|
+
return n.json();
|
|
2519
|
+
}
|
|
2520
|
+
async function z(e) {
|
|
2521
|
+
let t = await fetch(`${c}${e}`, {
|
|
2522
|
+
method: "DELETE",
|
|
2523
|
+
credentials: "include",
|
|
2524
|
+
headers: $(s)
|
|
2525
|
+
});
|
|
2526
|
+
if (!t.ok) {
|
|
2527
|
+
let e = "Request failed";
|
|
2528
|
+
try {
|
|
2529
|
+
let n = await t.json();
|
|
2530
|
+
e = n?.error?.message ?? n?.error ?? e;
|
|
2531
|
+
} catch {}
|
|
2532
|
+
throw Error(typeof e == "string" ? e : "Request failed");
|
|
2533
|
+
}
|
|
2534
|
+
return t.json();
|
|
2535
|
+
}
|
|
2536
|
+
async function B(e) {
|
|
2537
|
+
L(null);
|
|
2538
|
+
let t = e === "none" ? null : e;
|
|
2539
|
+
try {
|
|
2540
|
+
let e = await R("/geo/config", { mode: t });
|
|
2541
|
+
g({
|
|
2542
|
+
mode: e.mode ?? null,
|
|
2543
|
+
countries: e.countries ?? []
|
|
2544
|
+
});
|
|
2545
|
+
} catch (e) {
|
|
2546
|
+
L(e?.message ?? "Failed to update mode");
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
async function V(e) {
|
|
2550
|
+
L(null), b(""), S(!1);
|
|
2551
|
+
try {
|
|
2552
|
+
let t = await R("/geo/countries", { countryCode: e });
|
|
2553
|
+
g({
|
|
2554
|
+
mode: t.mode ?? null,
|
|
2555
|
+
countries: t.countries ?? []
|
|
2556
|
+
});
|
|
2557
|
+
} catch (e) {
|
|
2558
|
+
L(e?.message ?? "Failed to add country");
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
async function H(e) {
|
|
2562
|
+
L(null);
|
|
2563
|
+
try {
|
|
2564
|
+
let t = await z(`/geo/countries/${e}`);
|
|
2565
|
+
g({
|
|
2566
|
+
mode: t.mode ?? null,
|
|
2567
|
+
countries: t.countries ?? []
|
|
2568
|
+
});
|
|
2569
|
+
} catch (e) {
|
|
2570
|
+
L(e?.message ?? "Failed to remove country");
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
async function U(e) {
|
|
2574
|
+
L(null);
|
|
2575
|
+
try {
|
|
2576
|
+
await z(`/ip-bans/${e}`), v((t) => t.filter((t) => t.id !== e));
|
|
2577
|
+
} catch (e) {
|
|
2578
|
+
L(e?.message ?? "Failed to lift ban");
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
async function W(e) {
|
|
2582
|
+
if (e.preventDefault(), O.trim()) {
|
|
2583
|
+
L(null), F(!0);
|
|
2584
|
+
try {
|
|
2585
|
+
let e = { ipAddress: O.trim() };
|
|
2586
|
+
if (A.trim() && (e.reason = A.trim()), M.trim()) {
|
|
2587
|
+
let t = parseInt(M, 10);
|
|
2588
|
+
!isNaN(t) && t > 0 && (e.durationMinutes = t);
|
|
2589
|
+
}
|
|
2590
|
+
let t = await R("/ip-bans", e);
|
|
2591
|
+
v((e) => [{
|
|
2592
|
+
id: t.id,
|
|
2593
|
+
ipAddress: t.ipAddress,
|
|
2594
|
+
reason: t.reason ?? null,
|
|
2595
|
+
expiresAt: t.expiresAt ?? null,
|
|
2596
|
+
isPermanent: t.isPermanent,
|
|
2597
|
+
createdAt: t.createdAt
|
|
2598
|
+
}, ...e]), k(""), j(""), N(""), D(!1);
|
|
2599
|
+
} catch (e) {
|
|
2600
|
+
L(e?.message ?? "Failed to add IP ban");
|
|
2601
|
+
} finally {
|
|
2602
|
+
F(!1);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
if (l) return /* @__PURE__ */ a("div", {
|
|
2607
|
+
className: "geo-admin__status",
|
|
2608
|
+
children: "Loading…"
|
|
2609
|
+
});
|
|
2610
|
+
if (d) return /* @__PURE__ */ a("div", {
|
|
2611
|
+
className: "geo-admin__status geo-admin__status--unavailable",
|
|
2612
|
+
children: "Geo restrictions and IP bans are available on the Community plan and above."
|
|
2613
|
+
});
|
|
2614
|
+
if (p) return /* @__PURE__ */ a("div", {
|
|
2615
|
+
className: "geo-admin__status geo-admin__status--error",
|
|
2616
|
+
children: p
|
|
2617
|
+
});
|
|
2618
|
+
let G = y.trim().toLowerCase(), K = (G ? de.filter((e) => e.name.toLowerCase().includes(G) || e.code.toLowerCase().includes(G)).slice(0, 12) : de.slice(0, 12)).filter((e) => !h.countries.includes(e.code)), q = w.trim() ? _.filter((e) => e.ipAddress.includes(w.trim()) || (e.reason ?? "").toLowerCase().includes(w.trim().toLowerCase())) : _;
|
|
2619
|
+
return /* @__PURE__ */ o("div", {
|
|
2620
|
+
className: "geo-admin",
|
|
2621
|
+
children: [
|
|
2622
|
+
I && /* @__PURE__ */ a("div", {
|
|
2623
|
+
className: "geo-admin__action-error",
|
|
2624
|
+
children: I
|
|
2625
|
+
}),
|
|
2626
|
+
/* @__PURE__ */ o("div", {
|
|
2627
|
+
className: "geo-admin__section",
|
|
2628
|
+
children: [
|
|
2629
|
+
/* @__PURE__ */ a("h4", {
|
|
2630
|
+
className: "geo-admin__section-title",
|
|
2631
|
+
children: "Country Restrictions"
|
|
2632
|
+
}),
|
|
2633
|
+
/* @__PURE__ */ o("div", {
|
|
2634
|
+
className: "geo-admin__field",
|
|
2635
|
+
children: [/* @__PURE__ */ a("label", {
|
|
2636
|
+
className: "geo-admin__label",
|
|
2637
|
+
children: "Restriction mode"
|
|
2638
|
+
}), /* @__PURE__ */ o("select", {
|
|
2639
|
+
className: "geo-admin__select",
|
|
2640
|
+
value: h.mode ?? "none",
|
|
2641
|
+
onChange: (e) => B(e.target.value),
|
|
2642
|
+
children: [
|
|
2643
|
+
/* @__PURE__ */ a("option", {
|
|
2644
|
+
value: "none",
|
|
2645
|
+
children: "None (unrestricted)"
|
|
2646
|
+
}),
|
|
2647
|
+
/* @__PURE__ */ a("option", {
|
|
2648
|
+
value: "blocklist",
|
|
2649
|
+
children: "Blocklist — block listed countries"
|
|
2650
|
+
}),
|
|
2651
|
+
/* @__PURE__ */ a("option", {
|
|
2652
|
+
value: "allowlist",
|
|
2653
|
+
children: "Allowlist — only listed countries"
|
|
2654
|
+
})
|
|
2655
|
+
]
|
|
2656
|
+
})]
|
|
2657
|
+
}),
|
|
2658
|
+
h.mode && /* @__PURE__ */ o(i, { children: [
|
|
2659
|
+
/* @__PURE__ */ a("p", {
|
|
2660
|
+
className: "geo-admin__hint",
|
|
2661
|
+
children: h.mode === "blocklist" ? "Users from these countries will be blocked from posting." : "Only users from these countries can post."
|
|
2662
|
+
}),
|
|
2663
|
+
h.countries.length > 0 ? /* @__PURE__ */ a("div", {
|
|
2664
|
+
className: "geo-admin__chips",
|
|
2665
|
+
children: h.countries.map((e) => /* @__PURE__ */ o("span", {
|
|
2666
|
+
className: "geo-admin__chip",
|
|
2667
|
+
children: [ue(e), /* @__PURE__ */ a("button", {
|
|
2668
|
+
className: "geo-admin__chip-remove",
|
|
2669
|
+
onClick: () => H(e),
|
|
2670
|
+
title: `Remove ${e}`,
|
|
2671
|
+
"aria-label": `Remove ${e}`,
|
|
2672
|
+
children: "×"
|
|
2673
|
+
})]
|
|
2674
|
+
}, e))
|
|
2675
|
+
}) : /* @__PURE__ */ a("p", {
|
|
2676
|
+
className: "geo-admin__hint geo-admin__hint--empty",
|
|
2677
|
+
children: "No countries in list yet."
|
|
2678
|
+
}),
|
|
2679
|
+
/* @__PURE__ */ o("div", {
|
|
2680
|
+
className: "geo-admin__combobox",
|
|
2681
|
+
ref: C,
|
|
2682
|
+
children: [
|
|
2683
|
+
/* @__PURE__ */ a("input", {
|
|
2684
|
+
className: "geo-admin__combobox-input",
|
|
2685
|
+
type: "text",
|
|
2686
|
+
placeholder: "Search country to add…",
|
|
2687
|
+
value: y,
|
|
2688
|
+
onChange: (e) => {
|
|
2689
|
+
b(e.target.value), S(!0);
|
|
2690
|
+
},
|
|
2691
|
+
onFocus: () => S(!0),
|
|
2692
|
+
onKeyDown: (e) => {
|
|
2693
|
+
e.key === "Escape" && (S(!1), b("")), e.key === "Enter" && K.length === 1 && V(K[0].code);
|
|
2694
|
+
}
|
|
2695
|
+
}),
|
|
2696
|
+
x && K.length > 0 && /* @__PURE__ */ a("ul", {
|
|
2697
|
+
className: "geo-admin__combobox-list",
|
|
2698
|
+
children: K.map((e) => /* @__PURE__ */ a("li", { children: /* @__PURE__ */ a("button", {
|
|
2699
|
+
className: "geo-admin__combobox-item",
|
|
2700
|
+
onMouseDown: (t) => {
|
|
2701
|
+
t.preventDefault(), V(e.code);
|
|
2702
|
+
},
|
|
2703
|
+
children: le(e.code)
|
|
2704
|
+
}) }, e.code))
|
|
2705
|
+
}),
|
|
2706
|
+
x && y.trim() && K.length === 0 && /* @__PURE__ */ a("div", {
|
|
2707
|
+
className: "geo-admin__combobox-empty",
|
|
2708
|
+
children: "No matching countries"
|
|
2709
|
+
})
|
|
2710
|
+
]
|
|
2711
|
+
})
|
|
2712
|
+
] })
|
|
2713
|
+
]
|
|
2714
|
+
}),
|
|
2715
|
+
/* @__PURE__ */ o("div", {
|
|
2716
|
+
className: "geo-admin__section",
|
|
2717
|
+
children: [
|
|
2718
|
+
/* @__PURE__ */ o("h4", {
|
|
2719
|
+
className: "geo-admin__section-title",
|
|
2720
|
+
children: ["IP Bans", _.length > 0 && /* @__PURE__ */ a("span", {
|
|
2721
|
+
className: "geo-admin__badge",
|
|
2722
|
+
children: _.length
|
|
2723
|
+
})]
|
|
2724
|
+
}),
|
|
2725
|
+
_.length > 4 && /* @__PURE__ */ a("input", {
|
|
2726
|
+
className: "geo-admin__filter-input",
|
|
2727
|
+
type: "text",
|
|
2728
|
+
placeholder: "Filter by IP or reason…",
|
|
2729
|
+
value: w,
|
|
2730
|
+
onChange: (e) => T(e.target.value)
|
|
2731
|
+
}),
|
|
2732
|
+
q.length === 0 && _.length === 0 ? /* @__PURE__ */ a("p", {
|
|
2733
|
+
className: "geo-admin__hint",
|
|
2734
|
+
children: "No active IP bans."
|
|
2735
|
+
}) : q.length === 0 ? /* @__PURE__ */ a("p", {
|
|
2736
|
+
className: "geo-admin__hint",
|
|
2737
|
+
children: "No bans match your filter."
|
|
2738
|
+
}) : /* @__PURE__ */ a("div", {
|
|
2739
|
+
className: "geo-admin__ban-list",
|
|
2740
|
+
children: q.map((e) => /* @__PURE__ */ o("div", {
|
|
2741
|
+
className: "geo-admin__ban-row",
|
|
2742
|
+
children: [/* @__PURE__ */ o("div", {
|
|
2743
|
+
className: "geo-admin__ban-info",
|
|
2744
|
+
children: [
|
|
2745
|
+
/* @__PURE__ */ a("span", {
|
|
2746
|
+
className: "geo-admin__ban-ip",
|
|
2747
|
+
children: e.ipAddress
|
|
2748
|
+
}),
|
|
2749
|
+
e.reason && /* @__PURE__ */ a("span", {
|
|
2750
|
+
className: "geo-admin__ban-reason",
|
|
2751
|
+
children: e.reason
|
|
2752
|
+
}),
|
|
2753
|
+
/* @__PURE__ */ a("span", {
|
|
2754
|
+
className: "geo-admin__ban-expiry",
|
|
2755
|
+
children: fe(e)
|
|
2756
|
+
})
|
|
2757
|
+
]
|
|
2758
|
+
}), /* @__PURE__ */ a("button", {
|
|
2759
|
+
className: "btn btn--ghost geo-admin__lift-btn",
|
|
2760
|
+
onClick: () => U(e.id),
|
|
2761
|
+
children: "Lift ban"
|
|
2762
|
+
})]
|
|
2763
|
+
}, e.id))
|
|
2764
|
+
}),
|
|
2765
|
+
/* @__PURE__ */ o("button", {
|
|
2766
|
+
className: "geo-admin__add-toggle",
|
|
2767
|
+
onClick: () => D((e) => !e),
|
|
2768
|
+
children: [E ? "▼" : "▶", " Add IP ban"]
|
|
2769
|
+
}),
|
|
2770
|
+
E && /* @__PURE__ */ o("form", {
|
|
2771
|
+
className: "geo-admin__add-ban-form",
|
|
2772
|
+
onSubmit: W,
|
|
2773
|
+
children: [
|
|
2774
|
+
/* @__PURE__ */ o("div", {
|
|
2775
|
+
className: "geo-admin__field",
|
|
2776
|
+
children: [/* @__PURE__ */ a("label", {
|
|
2777
|
+
className: "geo-admin__label",
|
|
2778
|
+
children: "IP address or CIDR range *"
|
|
2779
|
+
}), /* @__PURE__ */ a("input", {
|
|
2780
|
+
className: "geo-admin__input",
|
|
2781
|
+
type: "text",
|
|
2782
|
+
placeholder: "e.g. 1.2.3.4 or 1.2.0.0/16",
|
|
2783
|
+
value: O,
|
|
2784
|
+
onChange: (e) => k(e.target.value),
|
|
2785
|
+
required: !0
|
|
2786
|
+
})]
|
|
2787
|
+
}),
|
|
2788
|
+
/* @__PURE__ */ o("div", {
|
|
2789
|
+
className: "geo-admin__field",
|
|
2790
|
+
children: [/* @__PURE__ */ a("label", {
|
|
2791
|
+
className: "geo-admin__label",
|
|
2792
|
+
children: "Reason (optional)"
|
|
2793
|
+
}), /* @__PURE__ */ a("input", {
|
|
2794
|
+
className: "geo-admin__input",
|
|
2795
|
+
type: "text",
|
|
2796
|
+
placeholder: "Reason for ban",
|
|
2797
|
+
value: A,
|
|
2798
|
+
onChange: (e) => j(e.target.value)
|
|
2799
|
+
})]
|
|
2800
|
+
}),
|
|
2801
|
+
/* @__PURE__ */ o("div", {
|
|
2802
|
+
className: "geo-admin__field",
|
|
2803
|
+
children: [/* @__PURE__ */ a("label", {
|
|
2804
|
+
className: "geo-admin__label",
|
|
2805
|
+
children: "Duration in minutes (blank = permanent)"
|
|
2806
|
+
}), /* @__PURE__ */ a("input", {
|
|
2807
|
+
className: "geo-admin__input",
|
|
2808
|
+
type: "number",
|
|
2809
|
+
min: "1",
|
|
2810
|
+
placeholder: "e.g. 1440 for 24 hours",
|
|
2811
|
+
value: M,
|
|
2812
|
+
onChange: (e) => N(e.target.value)
|
|
2813
|
+
})]
|
|
2814
|
+
}),
|
|
2815
|
+
/* @__PURE__ */ a("button", {
|
|
2816
|
+
className: "btn btn--primary",
|
|
2817
|
+
type: "submit",
|
|
2818
|
+
disabled: P || !O.trim(),
|
|
2819
|
+
children: P ? "Adding…" : "Add IP ban"
|
|
2820
|
+
})
|
|
2821
|
+
]
|
|
2822
|
+
})
|
|
2823
|
+
]
|
|
2824
|
+
})
|
|
2825
|
+
]
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
//#endregion
|
|
2829
|
+
//#region src/components/ModeratorAdminPage.tsx
|
|
2830
|
+
function me(e) {
|
|
2831
|
+
return e.email ? `${e.displayName} (${e.email})` : e.displayName;
|
|
2832
|
+
}
|
|
2833
|
+
function he(e, t) {
|
|
2834
|
+
return t === null ? `${e} moderator${e === 1 ? "" : "s"} — unlimited slots` : `${e} of ${t} moderator slot${t === 1 ? "" : "s"} used`;
|
|
2835
|
+
}
|
|
2836
|
+
function ge({ stationSlug: e, user: l, getToken: u }) {
|
|
2837
|
+
let d = n(new c("", u)).current, [f, p] = r([]), [m, h] = r(null), [g, _] = r(!0), [v, y] = r(null), [b, x] = r(""), [S, C] = r(null), [w, T] = r(null), E = l.permissions.includes(s.MANAGE_ROLES);
|
|
2838
|
+
async function D() {
|
|
2839
|
+
_(!0), y(null);
|
|
2840
|
+
try {
|
|
2841
|
+
let t = await d.getMembersAdmin(e);
|
|
2842
|
+
p(t.members), h(t.quota);
|
|
2843
|
+
} catch (e) {
|
|
2844
|
+
y(e?.message ?? "Failed to load moderators.");
|
|
2845
|
+
} finally {
|
|
2846
|
+
_(!1);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
if (t(() => {
|
|
2850
|
+
E && D().catch(() => void 0);
|
|
2851
|
+
}, [E, e]), !E) return null;
|
|
2852
|
+
let O = f.filter((e) => e.roles.includes("moderator")), k = f.filter((e) => !e.roles.includes("moderator") && !e.roles.includes("station_admin")), A = b.trim().toLowerCase(), j = A === "" ? k : k.filter((e) => e.displayName.toLowerCase().includes(A) || (e.email ?? "").toLowerCase().includes(A)), M = m !== null && m.limit !== null && m.used >= m.limit, N = S !== null || w !== null;
|
|
2853
|
+
async function P(t) {
|
|
2854
|
+
C(t), y(null);
|
|
2855
|
+
try {
|
|
2856
|
+
await d.patchMemberRoles(e, t, { add: ["moderator"] }), await D();
|
|
2857
|
+
} catch (e) {
|
|
2858
|
+
y(e?.message ?? "Failed to promote member.");
|
|
2859
|
+
} finally {
|
|
2860
|
+
C(null);
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
async function F(t) {
|
|
2864
|
+
T(t), y(null);
|
|
2865
|
+
try {
|
|
2866
|
+
await d.patchMemberRoles(e, t, { remove: ["moderator"] }), await D();
|
|
2867
|
+
} catch (e) {
|
|
2868
|
+
y(e?.message ?? "Failed to demote moderator.");
|
|
2869
|
+
} finally {
|
|
2870
|
+
T(null);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
return /* @__PURE__ */ o("div", {
|
|
2874
|
+
className: "moderator-admin-page",
|
|
2875
|
+
children: [
|
|
2876
|
+
/* @__PURE__ */ a("div", {
|
|
2877
|
+
className: "moderator-admin-page__topbar",
|
|
2878
|
+
children: /* @__PURE__ */ o("div", {
|
|
2879
|
+
className: "moderator-admin-page__title-row",
|
|
2880
|
+
children: [/* @__PURE__ */ a("h2", {
|
|
2881
|
+
className: "moderator-admin-page__title",
|
|
2882
|
+
children: "Moderators"
|
|
2883
|
+
}), m && /* @__PURE__ */ a("span", {
|
|
2884
|
+
className: "sticker-admin-page__count",
|
|
2885
|
+
children: he(m.used, m.limit)
|
|
2886
|
+
})]
|
|
2887
|
+
})
|
|
2888
|
+
}),
|
|
2889
|
+
v && /* @__PURE__ */ a("div", {
|
|
2890
|
+
className: "sticker-admin-page__error",
|
|
2891
|
+
children: v
|
|
2892
|
+
}),
|
|
2893
|
+
g ? /* @__PURE__ */ a("div", {
|
|
2894
|
+
className: "sticker-admin-page__state",
|
|
2895
|
+
children: "Loading moderators…"
|
|
2896
|
+
}) : /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ o("div", {
|
|
2897
|
+
className: "moderator-admin-page__section",
|
|
2898
|
+
children: [
|
|
2899
|
+
/* @__PURE__ */ a("h3", {
|
|
2900
|
+
className: "moderator-admin-page__section-title",
|
|
2901
|
+
children: "Current moderators"
|
|
2902
|
+
}),
|
|
2903
|
+
/* @__PURE__ */ a("hr", { className: "moderator-admin-page__divider" }),
|
|
2904
|
+
O.length === 0 ? /* @__PURE__ */ a("div", {
|
|
2905
|
+
className: "sticker-admin-page__state",
|
|
2906
|
+
children: "No moderators assigned yet."
|
|
2907
|
+
}) : /* @__PURE__ */ a("ul", {
|
|
2908
|
+
className: "moderator-admin-page__list",
|
|
2909
|
+
children: O.map((e) => /* @__PURE__ */ o("li", {
|
|
2910
|
+
className: "moderator-admin-page__item",
|
|
2911
|
+
children: [/* @__PURE__ */ a("button", {
|
|
2912
|
+
className: "moderator-admin-page__demote-link",
|
|
2913
|
+
onClick: () => F(e.userId),
|
|
2914
|
+
disabled: N,
|
|
2915
|
+
children: w === e.userId ? "Demoting…" : "Demote"
|
|
2916
|
+
}), /* @__PURE__ */ a("span", {
|
|
2917
|
+
className: "moderator-admin-page__member-label",
|
|
2918
|
+
children: me(e)
|
|
2919
|
+
})]
|
|
2920
|
+
}, e.userId))
|
|
2921
|
+
})
|
|
2922
|
+
]
|
|
2923
|
+
}), /* @__PURE__ */ o("div", {
|
|
2924
|
+
className: "moderator-admin-page__section",
|
|
2925
|
+
children: [
|
|
2926
|
+
/* @__PURE__ */ a("h3", {
|
|
2927
|
+
className: "moderator-admin-page__section-title",
|
|
2928
|
+
children: "Add a moderator"
|
|
2929
|
+
}),
|
|
2930
|
+
/* @__PURE__ */ a("hr", { className: "moderator-admin-page__divider" }),
|
|
2931
|
+
/* @__PURE__ */ a("input", {
|
|
2932
|
+
className: "moderator-admin-page__filter",
|
|
2933
|
+
type: "text",
|
|
2934
|
+
placeholder: "Filter by name or email",
|
|
2935
|
+
value: b,
|
|
2936
|
+
onChange: (e) => x(e.target.value)
|
|
2937
|
+
}),
|
|
2938
|
+
/* @__PURE__ */ a("p", {
|
|
2939
|
+
className: "moderator-admin-page__list-hint",
|
|
2940
|
+
children: "Recent users"
|
|
2941
|
+
}),
|
|
2942
|
+
j.length === 0 ? /* @__PURE__ */ a("div", {
|
|
2943
|
+
className: "sticker-admin-page__state",
|
|
2944
|
+
children: b ? "No members match that filter." : "No members available to promote."
|
|
2945
|
+
}) : /* @__PURE__ */ a("ul", {
|
|
2946
|
+
className: "moderator-admin-page__list",
|
|
2947
|
+
children: j.map((e) => /* @__PURE__ */ o("li", {
|
|
2948
|
+
className: "moderator-admin-page__item",
|
|
2949
|
+
children: [/* @__PURE__ */ a("button", {
|
|
2950
|
+
className: "moderator-admin-page__promote-link",
|
|
2951
|
+
onClick: () => P(e.userId),
|
|
2952
|
+
disabled: M || N,
|
|
2953
|
+
title: M ? "Moderator limit reached for your plan" : void 0,
|
|
2954
|
+
children: S === e.userId ? "Promoting…" : "Promote"
|
|
2955
|
+
}), /* @__PURE__ */ a("span", {
|
|
2956
|
+
className: "moderator-admin-page__member-label",
|
|
2957
|
+
children: me(e)
|
|
2958
|
+
})]
|
|
2959
|
+
}, e.userId))
|
|
2960
|
+
})
|
|
2961
|
+
]
|
|
2962
|
+
})] })
|
|
2963
|
+
]
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
//#endregion
|
|
2967
|
+
//#region src/AdminPanel.tsx
|
|
2968
|
+
function _e({ className: e, spaceSlug: n, serverUrl: i = "", token: c, manageOwnRefreshToken: l, onSessionEnded: d }) {
|
|
2969
|
+
let f = l ?? !u.managed, p = x({
|
|
2970
|
+
spaceSlug: n,
|
|
2971
|
+
initialToken: c ?? null,
|
|
2972
|
+
manageOwnRefreshToken: f,
|
|
2973
|
+
onSessionEnded: d
|
|
2974
|
+
}), { user: m, stationSlug: h, getToken: g } = p;
|
|
2975
|
+
t(() => {
|
|
2976
|
+
if (!h) return;
|
|
2977
|
+
document.documentElement.setAttribute("data-theme", u.theme);
|
|
2978
|
+
let e = N(h);
|
|
2979
|
+
e && P(e);
|
|
2980
|
+
let t = `${i}/api/chat/${h}/theme`;
|
|
2981
|
+
fetch(t).then((e) => e.ok ? e.json() : {
|
|
2982
|
+
light: {},
|
|
2983
|
+
dark: {}
|
|
2984
|
+
}).then((e) => {
|
|
2985
|
+
(Object.keys(e?.light ?? {}).length > 0 || Object.keys(e?.dark ?? {}).length > 0) && w({
|
|
2986
|
+
light: e.light ?? {},
|
|
2987
|
+
dark: e.dark ?? {}
|
|
2988
|
+
}, u.theme);
|
|
2989
|
+
}).catch(() => {});
|
|
2990
|
+
}, [h, i]);
|
|
2991
|
+
let [_, v] = r(!1), [y, b] = r(!1), [S, C] = r(!1), [T, E] = r(!1), [D, O] = r(!1), k = e ? `relaya-root ${e}` : "relaya-root";
|
|
2992
|
+
if (p.status === "loading") return /* @__PURE__ */ a("div", {
|
|
2993
|
+
className: k,
|
|
2994
|
+
children: /* @__PURE__ */ a("div", {
|
|
2995
|
+
className: "admin-panel",
|
|
2996
|
+
children: /* @__PURE__ */ o("div", {
|
|
2997
|
+
className: "admin-panel__loading",
|
|
2998
|
+
children: [/* @__PURE__ */ a("div", { className: "connection-spinner" }), /* @__PURE__ */ a("span", { children: "Loading…" })]
|
|
2999
|
+
})
|
|
3000
|
+
})
|
|
3001
|
+
});
|
|
3002
|
+
if (p.status !== "authenticated" || !m) return /* @__PURE__ */ a("div", {
|
|
3003
|
+
className: k,
|
|
3004
|
+
children: /* @__PURE__ */ a("div", {
|
|
3005
|
+
className: "admin-panel",
|
|
3006
|
+
children: /* @__PURE__ */ o("div", {
|
|
3007
|
+
className: "admin-panel__unauthorized",
|
|
3008
|
+
children: [/* @__PURE__ */ a("h2", { children: "Sign in required" }), /* @__PURE__ */ a("p", { children: "You must be signed in to access the admin panel." })]
|
|
3009
|
+
})
|
|
3010
|
+
})
|
|
3011
|
+
});
|
|
3012
|
+
let A = m.permissions.includes(s.DELETE_ANY), j = m.permissions.includes(s.MANAGE_ROLES);
|
|
3013
|
+
return !A && !j ? /* @__PURE__ */ a("div", {
|
|
3014
|
+
className: k,
|
|
3015
|
+
children: /* @__PURE__ */ a("div", {
|
|
3016
|
+
className: "admin-panel",
|
|
3017
|
+
children: /* @__PURE__ */ o("div", {
|
|
3018
|
+
className: "admin-panel__unauthorized",
|
|
3019
|
+
children: [/* @__PURE__ */ a("h2", { children: "Access denied" }), /* @__PURE__ */ a("p", { children: "You do not have admin or moderation permissions for this space." })]
|
|
3020
|
+
})
|
|
3021
|
+
})
|
|
3022
|
+
}) : /* @__PURE__ */ a("div", {
|
|
3023
|
+
className: k,
|
|
3024
|
+
children: /* @__PURE__ */ o("div", {
|
|
3025
|
+
className: "admin-panel",
|
|
3026
|
+
children: [/* @__PURE__ */ o("div", {
|
|
3027
|
+
className: "admin-panel__header",
|
|
3028
|
+
children: [/* @__PURE__ */ o("h1", {
|
|
3029
|
+
className: "admin-panel__title",
|
|
3030
|
+
children: [
|
|
3031
|
+
h.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" "),
|
|
3032
|
+
" ",
|
|
3033
|
+
"— Admin"
|
|
3034
|
+
]
|
|
3035
|
+
}), /* @__PURE__ */ a("p", {
|
|
3036
|
+
className: "admin-panel__subtitle",
|
|
3037
|
+
children: "Moderation and administration for this space."
|
|
3038
|
+
})]
|
|
3039
|
+
}), /* @__PURE__ */ o("div", {
|
|
3040
|
+
className: "admin-panel__sections",
|
|
3041
|
+
children: [
|
|
3042
|
+
j && /* @__PURE__ */ a("section", {
|
|
3043
|
+
className: "admin-panel__section",
|
|
3044
|
+
children: /* @__PURE__ */ o("div", {
|
|
3045
|
+
className: "admin-settings",
|
|
3046
|
+
children: [/* @__PURE__ */ o("button", {
|
|
3047
|
+
className: "admin-settings__toggle",
|
|
3048
|
+
onClick: () => v((e) => !e),
|
|
3049
|
+
children: [/* @__PURE__ */ a("span", { children: _ ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Sticker library" })]
|
|
3050
|
+
}), _ && /* @__PURE__ */ a("div", {
|
|
3051
|
+
className: "admin-settings__panel",
|
|
3052
|
+
children: /* @__PURE__ */ a(Y, {
|
|
3053
|
+
stationSlug: h,
|
|
3054
|
+
user: m,
|
|
3055
|
+
getToken: g
|
|
3056
|
+
})
|
|
3057
|
+
})]
|
|
3058
|
+
})
|
|
3059
|
+
}),
|
|
3060
|
+
j && /* @__PURE__ */ a("section", {
|
|
3061
|
+
className: "admin-panel__section",
|
|
3062
|
+
children: /* @__PURE__ */ o("div", {
|
|
3063
|
+
className: "admin-settings",
|
|
3064
|
+
children: [/* @__PURE__ */ o("button", {
|
|
3065
|
+
className: "admin-settings__toggle",
|
|
3066
|
+
onClick: () => b((e) => !e),
|
|
3067
|
+
children: [/* @__PURE__ */ a("span", { children: y ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Color theme" })]
|
|
3068
|
+
}), y && /* @__PURE__ */ a("div", {
|
|
3069
|
+
className: "admin-settings__panel",
|
|
3070
|
+
children: /* @__PURE__ */ a(ae, {
|
|
3071
|
+
stationSlug: h,
|
|
3072
|
+
getToken: g
|
|
3073
|
+
})
|
|
3074
|
+
})]
|
|
3075
|
+
})
|
|
3076
|
+
}),
|
|
3077
|
+
A && /* @__PURE__ */ a("section", {
|
|
3078
|
+
className: "admin-panel__section",
|
|
3079
|
+
children: /* @__PURE__ */ a(V, {
|
|
3080
|
+
stationSlug: h,
|
|
3081
|
+
user: m,
|
|
3082
|
+
getToken: g
|
|
3083
|
+
})
|
|
3084
|
+
}),
|
|
3085
|
+
A && /* @__PURE__ */ a("section", {
|
|
3086
|
+
className: "admin-panel__section",
|
|
3087
|
+
children: /* @__PURE__ */ a(W, {
|
|
3088
|
+
stationSlug: h,
|
|
3089
|
+
user: m,
|
|
3090
|
+
getToken: g
|
|
3091
|
+
})
|
|
3092
|
+
}),
|
|
3093
|
+
j && /* @__PURE__ */ a("section", {
|
|
3094
|
+
className: "admin-panel__section",
|
|
3095
|
+
children: /* @__PURE__ */ o("div", {
|
|
3096
|
+
className: "admin-settings",
|
|
3097
|
+
children: [/* @__PURE__ */ o("button", {
|
|
3098
|
+
className: "admin-settings__toggle",
|
|
3099
|
+
onClick: () => C((e) => !e),
|
|
3100
|
+
children: [/* @__PURE__ */ a("span", { children: S ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Moderator management" })]
|
|
3101
|
+
}), S && /* @__PURE__ */ a("div", {
|
|
3102
|
+
className: "admin-settings__panel",
|
|
3103
|
+
children: /* @__PURE__ */ a(ge, {
|
|
3104
|
+
stationSlug: h,
|
|
3105
|
+
user: m,
|
|
3106
|
+
getToken: g
|
|
3107
|
+
})
|
|
3108
|
+
})]
|
|
3109
|
+
})
|
|
3110
|
+
}),
|
|
3111
|
+
j && /* @__PURE__ */ a("section", {
|
|
3112
|
+
className: "admin-panel__section",
|
|
3113
|
+
children: /* @__PURE__ */ a(K, {
|
|
3114
|
+
stationSlug: h,
|
|
3115
|
+
user: m,
|
|
3116
|
+
getToken: g
|
|
3117
|
+
})
|
|
3118
|
+
}),
|
|
3119
|
+
j && /* @__PURE__ */ a("section", {
|
|
3120
|
+
className: "admin-panel__section",
|
|
3121
|
+
children: /* @__PURE__ */ o("div", {
|
|
3122
|
+
className: "admin-settings",
|
|
3123
|
+
children: [/* @__PURE__ */ o("button", {
|
|
3124
|
+
className: "admin-settings__toggle",
|
|
3125
|
+
onClick: () => O((e) => !e),
|
|
3126
|
+
children: [/* @__PURE__ */ a("span", { children: D ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Geo restrictions & IP bans" })]
|
|
3127
|
+
}), D && /* @__PURE__ */ a("div", {
|
|
3128
|
+
className: "admin-settings__panel",
|
|
3129
|
+
children: /* @__PURE__ */ a(pe, {
|
|
3130
|
+
stationSlug: h,
|
|
3131
|
+
getToken: g
|
|
3132
|
+
})
|
|
3133
|
+
})]
|
|
3134
|
+
})
|
|
3135
|
+
}),
|
|
3136
|
+
j && /* @__PURE__ */ a("section", {
|
|
3137
|
+
className: "admin-panel__section",
|
|
3138
|
+
children: /* @__PURE__ */ o("div", {
|
|
3139
|
+
className: "admin-settings",
|
|
3140
|
+
children: [/* @__PURE__ */ o("button", {
|
|
3141
|
+
className: "admin-settings__toggle",
|
|
3142
|
+
onClick: () => E((e) => !e),
|
|
3143
|
+
children: [/* @__PURE__ */ a("span", { children: T ? "▼" : "▶" }), /* @__PURE__ */ a("span", { children: "Chat history export" })]
|
|
3144
|
+
}), T && /* @__PURE__ */ a("div", {
|
|
3145
|
+
className: "admin-settings__panel",
|
|
3146
|
+
children: /* @__PURE__ */ a(ce, {
|
|
3147
|
+
stationSlug: h,
|
|
3148
|
+
getToken: g
|
|
3149
|
+
})
|
|
3150
|
+
})]
|
|
3151
|
+
})
|
|
3152
|
+
})
|
|
3153
|
+
]
|
|
3154
|
+
})]
|
|
3155
|
+
})
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
//#endregion
|
|
3159
|
+
export { s as S, u as _, W as a, l as b, F as c, O as d, D as f, x as g, T as h, K as i, P as l, w as m, ae as n, V as o, E as p, Y as r, R as s, _e as t, N as u, f as v, c as x, d as y };
|