memi-board 0.45.0 → 0.46.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/README.md +29 -5
- package/dist/composables/boardLookup.d.ts +2 -0
- package/dist/composables/useMemiBoardAuth.d.ts +5 -1
- package/dist/composables/useMemiBoardComments.d.ts +2 -3
- package/dist/config.d.ts +2 -0
- package/dist/{en-BVUBLkxc.js → en-CaPfPNDy.js} +8 -2
- package/dist/en-CaPfPNDy.js.map +1 -0
- package/dist/index.js +831 -768
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/CategorySettings.vue +15 -6
- package/dist/runtime/components/CommentThread.vue +1 -1
- package/dist/runtime/components/Editor.vue +1 -0
- package/dist/runtime/components/Settings.vue +8 -1
- package/dist/runtime/components/Users.vue +4 -3
- package/dist/{slugify-6bG6GSO_.js → slugify-DJ82ifCd.js} +14 -5
- package/dist/slugify-DJ82ifCd.js.map +1 -0
- package/dist/storage.js +62 -49
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/utils/boardAccess.d.ts +3 -0
- package/dist/utils/deletePostCascade.d.ts +0 -2
- package/dist/utils/findReadablePost.d.ts +4 -0
- package/dist/utils/saveBoardVisibility.d.ts +3 -0
- package/dist/utils/section.d.ts +1 -1
- package/dist/utils/subscribeCommentRange.d.ts +9 -0
- package/docs/firebase-setup.md +48 -1
- package/docs/firestore.rules.example +59 -14
- package/docs/roles-migration.md +32 -0
- package/docs/storage.rules.example +27 -16
- package/package.json +1 -1
- package/dist/en-BVUBLkxc.js.map +0 -1
- package/dist/slugify-6bG6GSO_.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { A as e, B as t, C as n, D as r, E as i, F as a, G as o, H as s, I as c, L as l, M as u, N as d, O as f, P as p, R as m, S as h, T as g, U as _, V as v, W as y, _ as b, a as x, b as S, c as C, d as w, f as T, g as E, h as D, i as O, j as k, k as ee, l as
|
|
2
|
-
import { computed as
|
|
3
|
-
import { deleteDoc as
|
|
4
|
-
import { useCollection as ye, useCurrentUser as be, useDocument as xe, useFirebaseApp as Se, useFirebaseAuth as Ce, useFirestore as
|
|
1
|
+
import { A as e, B as t, C as n, D as r, E as i, F as a, G as o, H as s, I as c, L as l, M as u, N as d, O as f, P as p, R as m, S as h, T as g, U as _, V as v, W as y, _ as b, a as x, b as S, c as C, d as w, f as T, g as E, h as D, i as O, j as k, k as ee, l as A, m as j, n as te, o as ne, p as re, r as ie, s as ae, u as oe, v as M, w as se, x as ce, y as le, z as N } from "./slugify-DJ82ifCd.js";
|
|
2
|
+
import { computed as P, onScopeDispose as F, reactive as ue, ref as I, toValue as L, watch as R } from "vue";
|
|
3
|
+
import { deleteDoc as de, deleteField as fe, doc as pe, documentId as z, endAt as me, endBefore as he, getDoc as B, getDocs as V, increment as H, limit as U, limitToLast as ge, onSnapshot as W, orderBy as G, query as K, runTransaction as _e, serverTimestamp as q, setDoc as J, startAfter as Y, updateDoc as X, where as Z, writeBatch as ve } from "firebase/firestore";
|
|
4
|
+
import { useCollection as ye, useCurrentUser as be, useDocument as xe, useFirebaseApp as Se, useFirebaseAuth as Ce, useFirestore as Q } from "vuefire";
|
|
5
5
|
import { GoogleAuthProvider as we, OAuthProvider as Te, createUserWithEmailAndPassword as Ee, signInWithEmailAndPassword as De, signInWithPopup as Oe, signOut as ke, updateProfile as Ae } from "firebase/auth";
|
|
6
6
|
import { deleteObject as je, getStorage as Me, listAll as Ne, ref as Pe } from "firebase/storage";
|
|
7
|
-
import { GoogleAIBackend as Fe, Schema as
|
|
7
|
+
import { GoogleAIBackend as Fe, Schema as Ie, getAI as Le, getGenerativeModel as Re } from "firebase/ai";
|
|
8
8
|
//#region src/utils/boardSeo.ts
|
|
9
|
-
function
|
|
9
|
+
function ze(e, t) {
|
|
10
10
|
return !e || e === "전체" || e === w(t, "seo.allCategory");
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function Be(e) {
|
|
13
13
|
if (typeof e != "string") return null;
|
|
14
14
|
let t = e.trim();
|
|
15
15
|
return /^https?:\/\//i.test(t) ? t : null;
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
let t =
|
|
17
|
+
function Ve(e) {
|
|
18
|
+
let t = Be(e);
|
|
19
19
|
if (!t) return null;
|
|
20
20
|
if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
|
|
21
21
|
try {
|
|
@@ -32,45 +32,45 @@ function Be(e) {
|
|
|
32
32
|
return t;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function He(e, t, n = "") {
|
|
36
36
|
let r = t.replace(/\/$/, ""), i = e.trim();
|
|
37
|
-
return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ?
|
|
37
|
+
return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? He(n, t) : r;
|
|
38
38
|
}
|
|
39
|
-
function
|
|
39
|
+
function Ue(e, t, n, r, i) {
|
|
40
40
|
let a = r?.trim().slice(0, 40), o = e.trim() || a || w(i, "seo.photo"), s = t?.trim(), c = n.trim() || "Board";
|
|
41
41
|
return s ? `${o} · ${s} | ${c}` : `${o} | ${c}`;
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function We(e, t) {
|
|
44
44
|
let n = [];
|
|
45
45
|
return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(w(t, "seo.postDescriptionFallback")), e.categoryLabel?.trim() && n.push(e.categoryLabel.trim()), e.authorName?.trim() && n.push(e.authorName.trim()), n.join(" · ").slice(0, 200);
|
|
46
46
|
}
|
|
47
|
-
function
|
|
47
|
+
function Ge(e, t, n) {
|
|
48
48
|
let r = t.trim() || "Board", i = e?.trim();
|
|
49
|
-
return
|
|
49
|
+
return ze(i, n) ? w(n, "seo.listTitleAll", { site: r }) : w(n, "seo.listTitle", {
|
|
50
50
|
name: i,
|
|
51
51
|
site: r
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
|
-
function
|
|
54
|
+
function Ke(e, t) {
|
|
55
55
|
if (e.description?.trim()) return e.description.trim().slice(0, 200);
|
|
56
|
-
let n = e.categoryLabel?.trim(), r =
|
|
56
|
+
let n = e.categoryLabel?.trim(), r = ze(n, t) ? w(t, "seo.listDescriptionAll") : w(t, "seo.listDescription", { name: n }), i = (e.recentTitles || []).filter(Boolean).slice(0, 3);
|
|
57
57
|
return i.length ? `${r} ${i.map((e) => `「${e}」`).join(" ")}`.slice(0, 200) : r.slice(0, 200);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function qe(e) {
|
|
60
60
|
let t = (e || "/board").trim() || "/board";
|
|
61
61
|
return (t.startsWith("/") ? t : `/${t}`).replace(/\/$/, "") || "/board";
|
|
62
62
|
}
|
|
63
63
|
//#endregion
|
|
64
64
|
//#region src/composables/fetchPublicSeo.ts
|
|
65
|
-
function
|
|
65
|
+
function Je() {
|
|
66
66
|
return {
|
|
67
|
-
db:
|
|
68
|
-
paths:
|
|
67
|
+
db: Q(),
|
|
68
|
+
paths: M()
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
-
async function
|
|
71
|
+
async function Ye(e, t) {
|
|
72
72
|
try {
|
|
73
|
-
let n = await
|
|
73
|
+
let n = await B(o(e.db, e.paths, t));
|
|
74
74
|
if (!n.exists()) return {
|
|
75
75
|
label: t,
|
|
76
76
|
description: "",
|
|
@@ -90,7 +90,7 @@ async function Je(e, t) {
|
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
function
|
|
93
|
+
function Xe(e) {
|
|
94
94
|
if (e && typeof e == "object" && "toDate" in e) try {
|
|
95
95
|
let t = e.toDate();
|
|
96
96
|
return t instanceof Date ? t.toISOString() : null;
|
|
@@ -99,15 +99,15 @@ function Ye(e) {
|
|
|
99
99
|
}
|
|
100
100
|
return null;
|
|
101
101
|
}
|
|
102
|
-
async function
|
|
102
|
+
async function Ze(e, t, n) {
|
|
103
103
|
let r = e.trim(), i = t.trim();
|
|
104
104
|
if (!r || !i) return null;
|
|
105
|
-
let a = n ??
|
|
105
|
+
let a = n ?? Je();
|
|
106
106
|
a.paths = _(a.paths);
|
|
107
|
-
let o = await
|
|
107
|
+
let o = await Ye(a, r);
|
|
108
108
|
if (o.hidden) return null;
|
|
109
109
|
try {
|
|
110
|
-
let e = (await
|
|
110
|
+
let e = (await V(K(N(a.db, a.paths), Z("boardId", "==", r), Z("slug", "==", i), Z("listed", "==", !0), Z("isPublished", "==", !0), U(1)))).docs[0];
|
|
111
111
|
if (!e) return null;
|
|
112
112
|
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title.trim() : "";
|
|
113
113
|
return n ? {
|
|
@@ -117,19 +117,19 @@ async function Xe(e, t, n) {
|
|
|
117
117
|
categoryLabel: o.label,
|
|
118
118
|
title: n,
|
|
119
119
|
summary: typeof t.summary == "string" ? t.summary.trim().slice(0, 200) : "",
|
|
120
|
-
previewImage:
|
|
120
|
+
previewImage: Ve(t.previewImage),
|
|
121
121
|
authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
|
|
122
|
-
createdAt:
|
|
122
|
+
createdAt: Xe(t.createdAt)
|
|
123
123
|
} : null;
|
|
124
124
|
} catch (e) {
|
|
125
125
|
return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
async function
|
|
129
|
-
n && await
|
|
130
|
-
let r = e?.trim() || null, i = t ??
|
|
128
|
+
async function Qe(e, t, n) {
|
|
129
|
+
n && await oe(n);
|
|
130
|
+
let r = e?.trim() || null, i = t ?? Je();
|
|
131
131
|
if (i.paths = _(i.paths), r) {
|
|
132
|
-
let e = await
|
|
132
|
+
let e = await Ye(i, r);
|
|
133
133
|
if (e.hidden) return {
|
|
134
134
|
kind: "category",
|
|
135
135
|
category: r,
|
|
@@ -138,7 +138,7 @@ async function Ze(e, t, n) {
|
|
|
138
138
|
ogImage: null,
|
|
139
139
|
recentTitles: []
|
|
140
140
|
};
|
|
141
|
-
let { recentTitles: t, ogImage: a } = await
|
|
141
|
+
let { recentTitles: t, ogImage: a } = await $e(i, r);
|
|
142
142
|
return {
|
|
143
143
|
kind: "category",
|
|
144
144
|
category: r,
|
|
@@ -157,13 +157,13 @@ async function Ze(e, t, n) {
|
|
|
157
157
|
recentTitles: []
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
|
-
async function
|
|
160
|
+
async function $e(e, t) {
|
|
161
161
|
let n = [], r = null;
|
|
162
162
|
try {
|
|
163
|
-
let i = await
|
|
163
|
+
let i = await V(K(N(e.db, e.paths), Z("boardId", "==", t), Z("listed", "==", !0), Z("isPublished", "==", !0), G("createdAt", "desc"), U(12)));
|
|
164
164
|
for (let e of i.docs) {
|
|
165
165
|
let t = e.data() ?? {}, i = typeof t.title == "string" ? t.title.trim() : "";
|
|
166
|
-
i && n.length < 5 && n.push(i), r ||=
|
|
166
|
+
i && n.length < 5 && n.push(i), r ||= Ve(t.previewImage);
|
|
167
167
|
}
|
|
168
168
|
} catch (e) {
|
|
169
169
|
import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
|
|
@@ -175,31 +175,31 @@ async function Qe(e, t) {
|
|
|
175
175
|
}
|
|
176
176
|
//#endregion
|
|
177
177
|
//#region src/utils/boardAccess.ts
|
|
178
|
-
function
|
|
179
|
-
return !e || !t ? !1 :
|
|
180
|
-
}
|
|
181
|
-
function et(e, t, n, r) {
|
|
182
|
-
return e ? t ? !0 : n ? $e(e, r) : !1 : !1;
|
|
178
|
+
function et(e, t) {
|
|
179
|
+
return !e || !t ? !1 : [...t.allowedStaffUids ?? [], ...t.allowedManagerUids ?? []].includes(e);
|
|
183
180
|
}
|
|
184
181
|
function tt(e, t, n, r) {
|
|
182
|
+
return e ? t ? !0 : n ? et(e, r) : !1 : !1;
|
|
183
|
+
}
|
|
184
|
+
function nt(e, t, n, r) {
|
|
185
185
|
if (!e) return !1;
|
|
186
|
-
if (r?.visibility === "hidden") return
|
|
186
|
+
if (r?.visibility === "hidden") return tt(e, t, n, r);
|
|
187
187
|
let i = r?.commentWriteRole ?? "user";
|
|
188
|
-
return i === "admin" ? t : i !== "staff" ||
|
|
188
|
+
return i === "admin" ? t : i !== "staff" || tt(e, t, n, r);
|
|
189
189
|
}
|
|
190
190
|
//#endregion
|
|
191
191
|
//#region src/composables/boardLookup.ts
|
|
192
|
-
var
|
|
193
|
-
function rt(e) {
|
|
194
|
-
nt = e;
|
|
195
|
-
}
|
|
192
|
+
var rt;
|
|
196
193
|
function it(e) {
|
|
197
|
-
|
|
194
|
+
rt = e;
|
|
195
|
+
}
|
|
196
|
+
function at(e) {
|
|
197
|
+
return rt?.(e);
|
|
198
198
|
}
|
|
199
199
|
//#endregion
|
|
200
200
|
//#region src/utils/moderation-strike.ts
|
|
201
|
-
var
|
|
202
|
-
function
|
|
201
|
+
var ot = 10, st = 864e5;
|
|
202
|
+
function ct(e, t, n = Date.now(), r = st) {
|
|
203
203
|
let i = Math.max(0, Math.floor(Number(e) || 0));
|
|
204
204
|
if (i <= 0) return 0;
|
|
205
205
|
let a = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -207,7 +207,7 @@ function st(e, t, n = Date.now(), r = ot) {
|
|
|
207
207
|
let o = Math.floor((n - a) / r);
|
|
208
208
|
return Math.max(0, i - o);
|
|
209
209
|
}
|
|
210
|
-
function
|
|
210
|
+
function lt(e, t, n = Date.now(), r = 10, i = st) {
|
|
211
211
|
let a = Math.max(0, Math.floor(Number(e) || 0));
|
|
212
212
|
if (a < r) return null;
|
|
213
213
|
let o = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -215,10 +215,10 @@ function ct(e, t, n = Date.now(), r = 10, i = ot) {
|
|
|
215
215
|
let s = o + (a - (r - 1)) * i;
|
|
216
216
|
return s > n ? s : null;
|
|
217
217
|
}
|
|
218
|
-
function
|
|
219
|
-
return
|
|
218
|
+
function ut(e, t, n = Date.now(), r = 10, i = st) {
|
|
219
|
+
return lt(e, t, n, r, i) != null;
|
|
220
220
|
}
|
|
221
|
-
function
|
|
221
|
+
function dt(e, t = Date.now(), n) {
|
|
222
222
|
let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
|
|
223
223
|
if (i <= 0) return w(n, "moderationStrike.liftSoon");
|
|
224
224
|
if (i < 24) return w(n, "moderationStrike.liftInHours", { hours: i });
|
|
@@ -228,7 +228,7 @@ function ut(e, t = Date.now(), n) {
|
|
|
228
228
|
hours: o
|
|
229
229
|
});
|
|
230
230
|
}
|
|
231
|
-
function
|
|
231
|
+
function ft(e) {
|
|
232
232
|
if (e == null) return null;
|
|
233
233
|
if (typeof e == "number" && Number.isFinite(e)) return e;
|
|
234
234
|
if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
|
|
@@ -240,55 +240,63 @@ function dt(e) {
|
|
|
240
240
|
}
|
|
241
241
|
//#endregion
|
|
242
242
|
//#region src/composables/useMemiBoardAuth.ts
|
|
243
|
-
var
|
|
244
|
-
function
|
|
243
|
+
var pt = "__MEMI_BOARD_AUTH_STATE__";
|
|
244
|
+
function mt() {
|
|
245
245
|
let e = globalThis;
|
|
246
|
-
return e[
|
|
247
|
-
role:
|
|
248
|
-
rolePending:
|
|
249
|
-
rawBlockCount:
|
|
250
|
-
blockAtMs:
|
|
246
|
+
return e[pt] || (e[pt] = {
|
|
247
|
+
role: I("user"),
|
|
248
|
+
rolePending: I(!0),
|
|
249
|
+
rawBlockCount: I(0),
|
|
250
|
+
blockAtMs: I(null),
|
|
251
251
|
watchStarted: !1
|
|
252
|
-
}), e[
|
|
252
|
+
}), e[pt];
|
|
253
253
|
}
|
|
254
|
-
function
|
|
255
|
-
let e =
|
|
254
|
+
function ht() {
|
|
255
|
+
let e = le(), { t, locale: n } = O(), r = mt(), i = Q(), a = Ce(), o = be(), { role: s, rolePending: c, rawBlockCount: l, blockAtMs: u } = r;
|
|
256
256
|
function d() {
|
|
257
257
|
let t = e.moderation?.blockBanThreshold;
|
|
258
258
|
return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
|
|
259
259
|
}
|
|
260
260
|
function p() {
|
|
261
261
|
let t = e.moderation?.blockBanDecayMs;
|
|
262
|
-
return typeof t == "number" && t > 0 ? t :
|
|
262
|
+
return typeof t == "number" && t > 0 ? t : st;
|
|
263
263
|
}
|
|
264
264
|
function m(e) {
|
|
265
|
-
return f(i,
|
|
265
|
+
return f(i, M(), e);
|
|
266
266
|
}
|
|
267
267
|
function h(e) {
|
|
268
|
-
if (
|
|
268
|
+
if (s.value = [
|
|
269
|
+
"owner",
|
|
270
|
+
"admin",
|
|
271
|
+
"manager",
|
|
272
|
+
"staff",
|
|
273
|
+
"user"
|
|
274
|
+
].includes(String(e?.role)) ? e?.role : "user", !e) {
|
|
269
275
|
l.value = 0, u.value = null;
|
|
270
276
|
return;
|
|
271
277
|
}
|
|
272
|
-
l.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), u.value =
|
|
278
|
+
l.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), u.value = ft(e.moderationBlockAt);
|
|
273
279
|
}
|
|
274
280
|
async function g(e) {
|
|
275
|
-
let t = m(e.uid), n = ee(i,
|
|
276
|
-
if (!r.exists()) return await Promise.all([
|
|
281
|
+
let t = m(e.uid), n = ee(i, M(), e.uid), r = await B(t);
|
|
282
|
+
if (!r.exists()) return await Promise.all([J(t, {
|
|
277
283
|
role: "user",
|
|
278
284
|
displayName: e.displayName,
|
|
279
285
|
photoURL: e.photoURL,
|
|
280
|
-
joinedAt:
|
|
281
|
-
lastVisitAt:
|
|
282
|
-
updatedAt:
|
|
283
|
-
}),
|
|
286
|
+
joinedAt: q(),
|
|
287
|
+
lastVisitAt: q(),
|
|
288
|
+
updatedAt: q()
|
|
289
|
+
}), J(n, { email: e.email })]), h({ moderationBlockCount: 0 }), "user";
|
|
284
290
|
let a = r.data();
|
|
285
291
|
h(a);
|
|
286
292
|
let o = typeof a.displayName == "string" && a.displayName.trim(), s = typeof a.photoURL == "string" && a.photoURL.trim(), c = {
|
|
287
|
-
lastVisitAt:
|
|
288
|
-
updatedAt:
|
|
293
|
+
lastVisitAt: q(),
|
|
294
|
+
updatedAt: q()
|
|
289
295
|
};
|
|
290
|
-
return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([
|
|
296
|
+
return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([X(t, c), J(n, { email: e.email }, { merge: !0 })]), [
|
|
297
|
+
"owner",
|
|
291
298
|
"admin",
|
|
299
|
+
"manager",
|
|
292
300
|
"staff",
|
|
293
301
|
"user"
|
|
294
302
|
].includes(String(a.role)) ? a.role : "user";
|
|
@@ -299,10 +307,10 @@ function mt() {
|
|
|
299
307
|
h(void 0);
|
|
300
308
|
return;
|
|
301
309
|
}
|
|
302
|
-
let t = await
|
|
310
|
+
let t = await B(m(e));
|
|
303
311
|
h(t.exists() ? t.data() : void 0);
|
|
304
312
|
}
|
|
305
|
-
r.watchStarted || (r.watchStarted = !0,
|
|
313
|
+
r.watchStarted || (r.watchStarted = !0, R(o, async (e) => {
|
|
306
314
|
if (c.value = !0, !e) {
|
|
307
315
|
s.value = "user", h(void 0), c.value = !1;
|
|
308
316
|
return;
|
|
@@ -315,46 +323,46 @@ function mt() {
|
|
|
315
323
|
c.value = !1;
|
|
316
324
|
}
|
|
317
325
|
}, { immediate: !0 }));
|
|
318
|
-
let v =
|
|
326
|
+
let v = P(() => !!o.value), y = P(() => s.value === "owner"), b = P(() => s.value === "manager"), x = P(() => y.value || s.value === "admin"), S = P(() => b.value || s.value === "staff"), C = P(() => x.value || S.value), w = P(() => ct(l.value, u.value, Date.now(), p())), T = P(() => {
|
|
319
327
|
let e = d();
|
|
320
|
-
return e <= 0 ? !1 :
|
|
321
|
-
}),
|
|
328
|
+
return e <= 0 ? !1 : ut(l.value, u.value, Date.now(), e, p());
|
|
329
|
+
}), E = P(() => {
|
|
322
330
|
let e = d();
|
|
323
331
|
if (e <= 0) return null;
|
|
324
|
-
let t =
|
|
325
|
-
return t == null ? null :
|
|
326
|
-
}),
|
|
327
|
-
if (!
|
|
328
|
-
let e =
|
|
332
|
+
let t = lt(l.value, u.value, Date.now(), e, p());
|
|
333
|
+
return t == null ? null : dt(t, Date.now(), n.value);
|
|
334
|
+
}), D = P(() => {
|
|
335
|
+
if (!T.value) return null;
|
|
336
|
+
let e = E.value;
|
|
329
337
|
return e ? t("auth.writeRestrictedUntil", { until: e }) : t("auth.writeRestricted");
|
|
330
338
|
});
|
|
331
|
-
async function
|
|
339
|
+
async function k() {
|
|
332
340
|
let e = o.value?.uid, r = d();
|
|
333
341
|
if (!e || r <= 0) return {
|
|
334
342
|
effectiveCount: 0,
|
|
335
343
|
restricted: !1,
|
|
336
344
|
messageSuffix: ""
|
|
337
345
|
};
|
|
338
|
-
let i = m(e), a = await
|
|
339
|
-
a.exists() ? await
|
|
340
|
-
moderationBlockCount:
|
|
341
|
-
moderationBlockAt:
|
|
342
|
-
updatedAt:
|
|
343
|
-
}) : await
|
|
346
|
+
let i = m(e), a = await B(i);
|
|
347
|
+
a.exists() ? await X(i, {
|
|
348
|
+
moderationBlockCount: H(1),
|
|
349
|
+
moderationBlockAt: q(),
|
|
350
|
+
updatedAt: q()
|
|
351
|
+
}) : await J(i, {
|
|
344
352
|
role: "user",
|
|
345
353
|
displayName: o.value?.displayName ?? null,
|
|
346
354
|
moderationBlockCount: 1,
|
|
347
|
-
moderationBlockAt:
|
|
348
|
-
updatedAt:
|
|
355
|
+
moderationBlockAt: q(),
|
|
356
|
+
updatedAt: q()
|
|
349
357
|
});
|
|
350
358
|
let s = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
|
|
351
359
|
l.value = s, u.value = c;
|
|
352
|
-
let f =
|
|
360
|
+
let f = ct(s, c, c, p()), h = ut(s, c, c, r, p()), g = t("auth.strikeSuffix", {
|
|
353
361
|
count: f,
|
|
354
362
|
threshold: r
|
|
355
363
|
});
|
|
356
364
|
if (h) {
|
|
357
|
-
let e =
|
|
365
|
+
let e = lt(s, c, c, r, p()), i = e == null ? null : dt(e, c, n.value);
|
|
358
366
|
g = i ? t("auth.strikeRestrictedUntil", {
|
|
359
367
|
count: f,
|
|
360
368
|
until: i
|
|
@@ -366,88 +374,101 @@ function mt() {
|
|
|
366
374
|
messageSuffix: g
|
|
367
375
|
};
|
|
368
376
|
}
|
|
369
|
-
function
|
|
377
|
+
function A() {
|
|
370
378
|
if (!a) throw Error(t("auth.notInitialized"));
|
|
371
379
|
return a;
|
|
372
380
|
}
|
|
373
|
-
async function
|
|
374
|
-
return Oe(
|
|
381
|
+
async function j() {
|
|
382
|
+
return Oe(A(), new we());
|
|
375
383
|
}
|
|
376
384
|
async function te() {
|
|
377
385
|
let e = new Te("apple.com");
|
|
378
|
-
return e.addScope("email"), e.addScope("name"), Oe(
|
|
386
|
+
return e.addScope("email"), e.addScope("name"), Oe(A(), e);
|
|
379
387
|
}
|
|
380
388
|
async function ne(e, t) {
|
|
381
|
-
return De(
|
|
389
|
+
return De(A(), e, t);
|
|
382
390
|
}
|
|
383
391
|
async function re(e, t, n) {
|
|
384
|
-
let r = await Ee(
|
|
392
|
+
let r = await Ee(A(), e, t);
|
|
385
393
|
return n && await Ae(r.user, { displayName: n }), r;
|
|
386
394
|
}
|
|
387
395
|
async function ie() {
|
|
388
|
-
await ke(
|
|
396
|
+
await ke(A());
|
|
389
397
|
}
|
|
390
398
|
function ae(e) {
|
|
391
|
-
return
|
|
399
|
+
return tt(o.value?.uid, x.value, S.value, e ? at(e) : void 0);
|
|
392
400
|
}
|
|
393
401
|
function oe(e) {
|
|
394
|
-
return
|
|
402
|
+
return o.value ? x.value ? !0 : b.value && !!e && (at(e)?.allowedManagerUids ?? []).includes(o.value.uid) : !1;
|
|
395
403
|
}
|
|
396
404
|
function se(e) {
|
|
397
|
-
return v.value && o.value?.uid === e.authorUid;
|
|
405
|
+
return v.value && (o.value?.uid === e.authorUid || ae(e.boardId));
|
|
398
406
|
}
|
|
399
407
|
function ce(e) {
|
|
408
|
+
return v.value && o.value?.uid === e.authorUid;
|
|
409
|
+
}
|
|
410
|
+
function N(e) {
|
|
400
411
|
return v.value && (o.value?.uid === e.authorUid || ae(e.boardId));
|
|
401
412
|
}
|
|
402
413
|
return {
|
|
403
414
|
user: o,
|
|
415
|
+
role: s,
|
|
416
|
+
isOwner: y,
|
|
417
|
+
isManager: b,
|
|
418
|
+
canConfigureBoard: oe,
|
|
404
419
|
isSignedIn: v,
|
|
405
|
-
isAdmin:
|
|
406
|
-
isStaff:
|
|
407
|
-
canManageContent:
|
|
420
|
+
isAdmin: x,
|
|
421
|
+
isStaff: S,
|
|
422
|
+
canManageContent: C,
|
|
408
423
|
canManageBoard: ae,
|
|
409
424
|
rolePending: c,
|
|
410
|
-
moderationBlockCount:
|
|
411
|
-
isWriteRestricted:
|
|
412
|
-
restrictedUntilLabel:
|
|
413
|
-
restrictedMessage:
|
|
414
|
-
signInWithGoogle:
|
|
425
|
+
moderationBlockCount: w,
|
|
426
|
+
isWriteRestricted: T,
|
|
427
|
+
restrictedUntilLabel: E,
|
|
428
|
+
restrictedMessage: D,
|
|
429
|
+
signInWithGoogle: j,
|
|
415
430
|
signInWithApple: te,
|
|
416
431
|
signInWithEmail: ne,
|
|
417
432
|
signUpWithEmail: re,
|
|
418
433
|
signOut: ie,
|
|
419
|
-
canEdit:
|
|
420
|
-
canDelete:
|
|
421
|
-
canEditComment:
|
|
422
|
-
canDeleteComment:
|
|
423
|
-
recordContentModerationBlock:
|
|
434
|
+
canEdit: se,
|
|
435
|
+
canDelete: se,
|
|
436
|
+
canEditComment: ce,
|
|
437
|
+
canDeleteComment: N,
|
|
438
|
+
recordContentModerationBlock: k,
|
|
424
439
|
refreshBoardUser: _
|
|
425
440
|
};
|
|
426
441
|
}
|
|
427
442
|
//#endregion
|
|
428
443
|
//#region src/utils/imeSafeSubmit.ts
|
|
429
|
-
var
|
|
430
|
-
function
|
|
444
|
+
var gt = /* @__PURE__ */ new WeakMap();
|
|
445
|
+
function _t(e) {
|
|
431
446
|
let t = e.currentTarget;
|
|
432
447
|
return t instanceof HTMLButtonElement ? t : t instanceof Element ? t.closest("button") : null;
|
|
433
448
|
}
|
|
434
|
-
function
|
|
449
|
+
function vt(e) {
|
|
435
450
|
if (e.pointerType !== "mouse" || e.button !== 0) return;
|
|
436
|
-
let t =
|
|
451
|
+
let t = _t(e), n = t?.form;
|
|
437
452
|
if (!t || !n || t.disabled) return;
|
|
438
|
-
|
|
453
|
+
gt.set(t, Date.now() + 1e3);
|
|
439
454
|
let r = document.activeElement;
|
|
440
455
|
r instanceof HTMLElement && r.blur(), setTimeout(() => n.requestSubmit(t), 0);
|
|
441
456
|
}
|
|
442
|
-
function
|
|
443
|
-
let t =
|
|
457
|
+
function yt(e) {
|
|
458
|
+
let t = _t(e);
|
|
444
459
|
if (!t) return;
|
|
445
|
-
let n =
|
|
446
|
-
n && Date.now() < n && (e.preventDefault(),
|
|
460
|
+
let n = gt.get(t);
|
|
461
|
+
n && Date.now() < n && (e.preventDefault(), gt.delete(t));
|
|
462
|
+
}
|
|
463
|
+
//#endregion
|
|
464
|
+
//#region src/utils/findReadablePost.ts
|
|
465
|
+
async function bt(e, t, n, r, i, a) {
|
|
466
|
+
let o = [Z("boardId", "==", n), Z("slug", "==", r)];
|
|
467
|
+
return a ? (await V(K(N(e, t), ...o, U(1)))).docs[0] : (await Promise.all([V(K(N(e, t), ...o, Z("listed", "==", !0), Z("isPublished", "==", !0), U(1))), ...i ? [V(K(N(e, t), ...o, Z("authorUid", "==", i), U(1)))] : []])).flatMap((e) => e.docs)[0];
|
|
447
468
|
}
|
|
448
469
|
//#endregion
|
|
449
470
|
//#region src/utils/extractEditorImageUrls.ts
|
|
450
|
-
function
|
|
471
|
+
function xt(e) {
|
|
451
472
|
if (!e) return [];
|
|
452
473
|
let t = /* @__PURE__ */ new Set();
|
|
453
474
|
for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&", "&"));
|
|
@@ -456,7 +477,7 @@ function yt(e) {
|
|
|
456
477
|
}
|
|
457
478
|
//#endregion
|
|
458
479
|
//#region src/utils/storagePath.ts
|
|
459
|
-
function
|
|
480
|
+
function St(e) {
|
|
460
481
|
try {
|
|
461
482
|
let t = new URL(e), n = t.hostname.toLowerCase();
|
|
462
483
|
if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
|
|
@@ -466,109 +487,106 @@ function bt(e) {
|
|
|
466
487
|
return null;
|
|
467
488
|
}
|
|
468
489
|
}
|
|
469
|
-
function
|
|
490
|
+
function Ct(e) {
|
|
470
491
|
return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
|
|
471
492
|
}
|
|
472
493
|
//#endregion
|
|
473
494
|
//#region src/utils/deletePostCascade.ts
|
|
474
|
-
async function
|
|
495
|
+
async function wt(e) {
|
|
475
496
|
try {
|
|
476
497
|
let t = await Ne(e);
|
|
477
|
-
await Promise.all([...t.items.map((e) => je(e).catch(() => {})), ...t.prefixes.map((e) =>
|
|
498
|
+
await Promise.all([...t.items.map((e) => je(e).catch(() => {})), ...t.prefixes.map((e) => wt(e))]);
|
|
478
499
|
} catch {}
|
|
479
500
|
}
|
|
480
|
-
async function
|
|
481
|
-
await
|
|
501
|
+
async function Tt(e, t) {
|
|
502
|
+
await wt(Pe(e, `${t}/images`)), await wt(Pe(e, `${t}/attachments`));
|
|
482
503
|
}
|
|
483
|
-
async function
|
|
504
|
+
async function Et(e, t) {
|
|
484
505
|
let n = [...new Set([...t].filter(Boolean))];
|
|
485
506
|
await Promise.all(n.map((t) => je(Pe(e, t)).catch(() => {})));
|
|
486
507
|
}
|
|
487
|
-
var
|
|
488
|
-
async function
|
|
489
|
-
let n = await
|
|
490
|
-
for (let t = 0; t < n.docs.length; t +=
|
|
491
|
-
let r =
|
|
492
|
-
n.docs.slice(t, t +
|
|
508
|
+
var Dt = 450;
|
|
509
|
+
async function Ot(e, t) {
|
|
510
|
+
let n = await V(t);
|
|
511
|
+
for (let t = 0; t < n.docs.length; t += Dt) {
|
|
512
|
+
let r = ve(e);
|
|
513
|
+
n.docs.slice(t, t + Dt).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
493
514
|
}
|
|
494
515
|
}
|
|
495
|
-
async function Dt(e, t, n) {
|
|
496
|
-
await Et(e, W(a(e, t), Y("postId", "==", n)));
|
|
497
|
-
}
|
|
498
|
-
async function Ot(e, t, n) {
|
|
499
|
-
await Et(e, W(s(e, t), Y("postId", "==", n)));
|
|
500
|
-
}
|
|
501
516
|
async function kt(e, t, n) {
|
|
502
|
-
await
|
|
517
|
+
await Ot(e, K(a(e, t), Z("postId", "==", n)));
|
|
503
518
|
}
|
|
504
|
-
async function At(e, t, n
|
|
505
|
-
|
|
519
|
+
async function At(e, t, n) {
|
|
520
|
+
await Ot(e, K(a(e, t), Z("commentId", "==", n), Z("target", "==", "comment")));
|
|
521
|
+
}
|
|
522
|
+
async function jt(e, t, n, r) {
|
|
523
|
+
let i = l(e, n, r), a = c(e, n, r), [o, s] = await Promise.all([B(i), B(a)]);
|
|
506
524
|
if (!o.exists()) return;
|
|
507
525
|
let d = o.data(), f = s.exists() ? String(s.data().content ?? "") : "";
|
|
508
|
-
await
|
|
526
|
+
await Ot(e, K(u(e, n), Z("postId", "==", r))), await kt(e, n, r);
|
|
509
527
|
let p = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set();
|
|
510
528
|
for (let e of d.attachments ?? []) {
|
|
511
529
|
if (!e?.path) continue;
|
|
512
530
|
p.add(e.path);
|
|
513
|
-
let t =
|
|
531
|
+
let t = Ct(e.path);
|
|
514
532
|
t && t !== r && h.add(t);
|
|
515
533
|
}
|
|
516
|
-
for (let e of
|
|
517
|
-
let t =
|
|
534
|
+
for (let e of xt(f)) {
|
|
535
|
+
let t = St(e);
|
|
518
536
|
if (!t) continue;
|
|
519
537
|
p.add(t);
|
|
520
538
|
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
521
539
|
n !== t && n.includes("/images/thumbnails/") && p.add(n);
|
|
522
540
|
let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
523
541
|
i && !t.includes("/thumbnails/") && p.add(`${i[1]}thumbnails/${i[2]}.jpg`);
|
|
524
|
-
let a =
|
|
542
|
+
let a = Ct(t);
|
|
525
543
|
a && a !== r && h.add(a);
|
|
526
544
|
}
|
|
527
|
-
await
|
|
528
|
-
for (let e of h) await
|
|
529
|
-
await
|
|
545
|
+
await de(a), await Tt(t, m(n, r));
|
|
546
|
+
for (let e of h) await Tt(t, m(n, e));
|
|
547
|
+
await Et(t, p), await de(i);
|
|
530
548
|
}
|
|
531
549
|
//#endregion
|
|
532
550
|
//#region src/utils/postBody.ts
|
|
533
|
-
function
|
|
551
|
+
function Mt(e) {
|
|
534
552
|
return String(e || "").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<img\b[^>]*>/gi, " ").replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, " ").replace(/<video\b[\s\S]*?<\/video>/gi, " ").replace(/<audio\b[\s\S]*?<\/audio>/gi, " ").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/ /g, " ").replace(/&[a-z]+;/gi, " ").replace(/&#\d+;/g, " ").replace(/[\u200B-\u200D\uFEFF\u00AD]/g, "").replace(/\s+/g, " ").trim();
|
|
535
553
|
}
|
|
536
|
-
function
|
|
537
|
-
let n =
|
|
554
|
+
function Nt(e, t = 40) {
|
|
555
|
+
let n = Mt(e);
|
|
538
556
|
if (!n) return "";
|
|
539
557
|
if (n.length <= t) return n;
|
|
540
558
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
541
559
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
542
560
|
}
|
|
543
|
-
function
|
|
544
|
-
return
|
|
561
|
+
function Pt(e) {
|
|
562
|
+
return Mt(e).length > 0;
|
|
545
563
|
}
|
|
546
|
-
function
|
|
564
|
+
function Ft(e, t) {
|
|
547
565
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
548
566
|
}
|
|
549
|
-
function
|
|
550
|
-
return !
|
|
567
|
+
function It(e) {
|
|
568
|
+
return !Pt(e);
|
|
551
569
|
}
|
|
552
570
|
//#endregion
|
|
553
571
|
//#region src/utils/postPreview.ts
|
|
554
|
-
function
|
|
572
|
+
function Lt(e) {
|
|
555
573
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
556
574
|
}
|
|
557
|
-
function
|
|
575
|
+
function Rt(e) {
|
|
558
576
|
if (e) try {
|
|
559
|
-
let t = new URL(
|
|
577
|
+
let t = new URL(Lt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
560
578
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
561
579
|
if (n.endsWith("youtube.com") || n === "youtube-nocookie.com") return t.pathname.startsWith("/embed/") || t.pathname.startsWith("/shorts/") ? t.pathname.split("/")[2] : t.searchParams.get("v") || void 0;
|
|
562
580
|
} catch {
|
|
563
581
|
return;
|
|
564
582
|
}
|
|
565
583
|
}
|
|
566
|
-
function
|
|
567
|
-
let t =
|
|
584
|
+
function zt(e) {
|
|
585
|
+
let t = Rt(e.videoUrl);
|
|
568
586
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
569
587
|
}
|
|
570
|
-
function
|
|
571
|
-
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ?
|
|
588
|
+
function Bt(e, t = []) {
|
|
589
|
+
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? Lt(r[1]) : a, s = o ? Rt(o) : void 0, c = n ? Lt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
572
590
|
return {
|
|
573
591
|
summary: e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|blockquote)>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/[^\S\n]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{2,}/g, "\n").trim().slice(0, 160),
|
|
574
592
|
...c ? { previewImage: c } : {},
|
|
@@ -576,8 +594,45 @@ function zt(e, t = []) {
|
|
|
576
594
|
};
|
|
577
595
|
}
|
|
578
596
|
//#endregion
|
|
597
|
+
//#region src/utils/saveBoardVisibility.ts
|
|
598
|
+
function Vt(e) {
|
|
599
|
+
return e === "hidden" ? "hidden" : "public";
|
|
600
|
+
}
|
|
601
|
+
async function Ht(e, t, n, r) {
|
|
602
|
+
let i;
|
|
603
|
+
for (;;) {
|
|
604
|
+
let a = await V(K(t, Z("boardId", "==", n), G("__name__"), ...i ? [Y(i)] : [], U(400)));
|
|
605
|
+
if (a.empty) break;
|
|
606
|
+
let o = ve(e), s = 0;
|
|
607
|
+
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
608
|
+
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async function Ut(e, t, n, r) {
|
|
612
|
+
await Ht(e, N(e, t), n, r), await Ht(e, u(e, t), n, r);
|
|
613
|
+
}
|
|
614
|
+
async function Wt(e, t, n, r) {
|
|
615
|
+
let i = o(e, t, n), a = Vt(r.visibility);
|
|
616
|
+
await _e(e, async (e) => {
|
|
617
|
+
let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
|
|
618
|
+
if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
|
|
619
|
+
return !t.exists() || !o && Vt(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
|
|
620
|
+
...r,
|
|
621
|
+
visibility: a === "public" ? "public" : Vt(n?.visibility),
|
|
622
|
+
visibilityTransition: a
|
|
623
|
+
}, { merge: !0 }), !0);
|
|
624
|
+
}) && (await Ut(e, t, n, a !== "hidden"), await _e(e, async (e) => {
|
|
625
|
+
if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
|
|
626
|
+
e.update(i, {
|
|
627
|
+
visibility: a,
|
|
628
|
+
visibilityTransition: "",
|
|
629
|
+
updatedAt: q()
|
|
630
|
+
});
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
579
634
|
//#region src/composables/useMemiBoardSettings.ts
|
|
580
|
-
var
|
|
635
|
+
var Gt = [
|
|
581
636
|
{
|
|
582
637
|
id: "free",
|
|
583
638
|
label: "자유",
|
|
@@ -605,134 +660,111 @@ var Bt = [
|
|
|
605
660
|
visibility: "public",
|
|
606
661
|
order: 2
|
|
607
662
|
}
|
|
608
|
-
],
|
|
609
|
-
function
|
|
663
|
+
], Kt = Gt;
|
|
664
|
+
function qt(e) {
|
|
610
665
|
return e === "hidden" ? "hidden" : "public";
|
|
611
666
|
}
|
|
612
|
-
function
|
|
667
|
+
function Jt(e, t, n) {
|
|
613
668
|
return {
|
|
614
669
|
id: e,
|
|
615
670
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
616
671
|
order: typeof t.order == "number" ? t.order : n,
|
|
617
672
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
618
|
-
visibility:
|
|
673
|
+
visibility: qt(t.visibility),
|
|
674
|
+
visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
|
|
619
675
|
listView: t.listView ?? "default",
|
|
620
676
|
editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
|
|
621
677
|
writeRole: t.writeRole ?? "user",
|
|
622
678
|
commentWriteRole: t.commentWriteRole ?? "user",
|
|
623
679
|
allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : [],
|
|
680
|
+
allowedManagerUids: Array.isArray(t.allowedManagerUids) ? t.allowedManagerUids : [],
|
|
624
681
|
locale: E(t.locale) ?? ""
|
|
625
682
|
};
|
|
626
683
|
}
|
|
627
|
-
function
|
|
628
|
-
let e = () =>
|
|
629
|
-
function
|
|
630
|
-
if (e) return
|
|
631
|
-
}
|
|
632
|
-
let b = v;
|
|
633
|
-
function x(e) {
|
|
634
|
-
if (e) return m.value.find((t) => t.id === e)?.description?.trim() || void 0;
|
|
684
|
+
function Yt() {
|
|
685
|
+
let e = () => M(), t = Q(), n = Se(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = ht(), { t: l } = O(), u = P(() => K(y(t, e()), G("order", "asc"))), { data: d, pending: f } = ye(u, { ssrKey: r(e(), "boards") }), p = P(() => d.value.map((e, t) => Jt(e.id, e, t))), m = p, h = P(() => p.value.filter((e) => e.visibility !== "hidden")), g = h;
|
|
686
|
+
function _(e) {
|
|
687
|
+
if (e) return p.value.find((t) => t.id === e)?.label ?? e;
|
|
635
688
|
}
|
|
636
|
-
let
|
|
637
|
-
function
|
|
638
|
-
|
|
689
|
+
let v = _;
|
|
690
|
+
function b(e) {
|
|
691
|
+
if (e) return p.value.find((t) => t.id === e)?.description?.trim() || void 0;
|
|
639
692
|
}
|
|
640
|
-
let
|
|
641
|
-
function
|
|
642
|
-
return
|
|
693
|
+
let x = b;
|
|
694
|
+
function S(e) {
|
|
695
|
+
return e ? p.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
|
|
643
696
|
}
|
|
644
|
-
let
|
|
645
|
-
function
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
rt((e) => k(e));
|
|
649
|
-
function ee(e) {
|
|
650
|
-
return et(c.value?.uid, a.value, s.value, e ? k(e) : void 0);
|
|
697
|
+
let C = S;
|
|
698
|
+
function w(e) {
|
|
699
|
+
return S(e) === "hidden";
|
|
651
700
|
}
|
|
652
|
-
let
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
for (;;) {
|
|
656
|
-
let a = await z(W(e, Y("boardId", "==", n), U("__name__"), ...i ? [q(i)] : [], V(400)));
|
|
657
|
-
if (a.empty) break;
|
|
658
|
-
let o = X(t), s = 0;
|
|
659
|
-
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
660
|
-
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
661
|
-
}
|
|
701
|
+
let T = w;
|
|
702
|
+
function D(e) {
|
|
703
|
+
if (e) return p.value.find((t) => t.id === e);
|
|
662
704
|
}
|
|
663
|
-
|
|
664
|
-
|
|
705
|
+
it((e) => D(e));
|
|
706
|
+
function k(e) {
|
|
707
|
+
return tt(c.value?.uid, a.value, s.value, e ? D(e) : void 0);
|
|
665
708
|
}
|
|
666
|
-
|
|
709
|
+
let ee = P(() => p.value.filter((e) => k(e.id)));
|
|
710
|
+
function A(e, t) {
|
|
667
711
|
return {
|
|
668
712
|
label: e.label.trim(),
|
|
669
713
|
description: (e.description ?? "").trim(),
|
|
670
|
-
visibility:
|
|
714
|
+
visibility: qt(e.visibility),
|
|
671
715
|
listView: e.listView ?? "default",
|
|
672
716
|
editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
|
|
673
717
|
writeRole: e.writeRole ?? "user",
|
|
674
718
|
commentWriteRole: e.commentWriteRole ?? "user",
|
|
675
719
|
allowedStaffUids: e.allowedStaffUids ?? [],
|
|
720
|
+
allowedManagerUids: e.allowedManagerUids ?? [],
|
|
676
721
|
locale: E(e.locale) ?? "",
|
|
677
722
|
order: t,
|
|
678
|
-
updatedAt:
|
|
723
|
+
updatedAt: q()
|
|
679
724
|
};
|
|
680
725
|
}
|
|
681
|
-
async function
|
|
726
|
+
async function j(n, r = n.order ?? 0) {
|
|
682
727
|
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
683
|
-
let a = n.id.trim(),
|
|
684
|
-
if (!a || !
|
|
685
|
-
let
|
|
728
|
+
let a = n.id.trim(), o = n.label.trim();
|
|
729
|
+
if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
|
|
730
|
+
let s = qt(n.visibility), c = A({
|
|
686
731
|
...n,
|
|
687
|
-
label:
|
|
688
|
-
visibility:
|
|
732
|
+
label: o,
|
|
733
|
+
visibility: s
|
|
689
734
|
}, r);
|
|
690
|
-
await
|
|
735
|
+
await Wt(t, e(), a, c);
|
|
691
736
|
}
|
|
692
|
-
let
|
|
693
|
-
async function
|
|
737
|
+
let ne = j;
|
|
738
|
+
async function re(e) {
|
|
694
739
|
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
695
|
-
let
|
|
696
|
-
n.
|
|
697
|
-
let c = n.id.trim(), u = n.label.trim();
|
|
698
|
-
if (!c || !u) throw Error(l("boardSettings.idAndNameRequired"));
|
|
699
|
-
let d = Ht(n.visibility), f = ae({
|
|
700
|
-
...n,
|
|
701
|
-
label: u,
|
|
702
|
-
visibility: d
|
|
703
|
-
}, i);
|
|
704
|
-
a.set(o(t, e(), c), f, { merge: !0 }), ((r.get(c) ?? "public") !== d || d === "hidden") && s.push({
|
|
705
|
-
id: c,
|
|
706
|
-
listed: d !== "hidden"
|
|
707
|
-
});
|
|
708
|
-
}), await a.commit();
|
|
709
|
-
for (let e of s) await ie(e.id, e.listed);
|
|
740
|
+
for (let t of e) if (!t.id.trim() || !t.label.trim()) throw Error(l("boardSettings.idAndNameRequired"));
|
|
741
|
+
for (let [t, n] of e.entries()) await j(n, t);
|
|
710
742
|
}
|
|
711
|
-
let
|
|
712
|
-
async function
|
|
743
|
+
let ie = re;
|
|
744
|
+
async function ae(r) {
|
|
713
745
|
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
714
746
|
let a = r.trim();
|
|
715
747
|
if (!a) throw Error(l("boardSettings.deleteIdRequired"));
|
|
716
748
|
for (;;) {
|
|
717
|
-
let r = await
|
|
749
|
+
let r = await V(K(N(t, e()), Z("boardId", "==", a), U(25)));
|
|
718
750
|
if (r.empty) break;
|
|
719
|
-
for (let i of r.docs) await
|
|
751
|
+
for (let i of r.docs) await jt(t, Me(n), e(), i.id);
|
|
720
752
|
}
|
|
721
|
-
await
|
|
753
|
+
await de(o(t, e(), r));
|
|
722
754
|
}
|
|
723
|
-
let
|
|
724
|
-
async function
|
|
755
|
+
let oe = ae;
|
|
756
|
+
async function se() {
|
|
725
757
|
return Promise.resolve();
|
|
726
758
|
}
|
|
727
|
-
async function
|
|
759
|
+
async function ce(e) {
|
|
728
760
|
let t = e.trim();
|
|
729
761
|
if (!t) throw Error(l("boardSettings.nameRequired"));
|
|
730
762
|
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
731
|
-
let n =
|
|
763
|
+
let n = p.value.find((e) => e.label === t);
|
|
732
764
|
if (n) return n.id;
|
|
733
|
-
let r =
|
|
765
|
+
let r = te(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(p.value.map((e) => e.id));
|
|
734
766
|
for (; s.has(a);) a = `${r}-${o++}`;
|
|
735
|
-
return await
|
|
767
|
+
return await j({
|
|
736
768
|
id: a,
|
|
737
769
|
label: t,
|
|
738
770
|
visibility: "public",
|
|
@@ -740,76 +772,79 @@ function Wt() {
|
|
|
740
772
|
editorType: "default",
|
|
741
773
|
writeRole: "user",
|
|
742
774
|
commentWriteRole: "user",
|
|
743
|
-
order:
|
|
775
|
+
order: p.value.length
|
|
744
776
|
}), a;
|
|
745
777
|
}
|
|
746
778
|
return {
|
|
747
|
-
boards:
|
|
748
|
-
publicBoards:
|
|
749
|
-
boardLabel:
|
|
750
|
-
boardDescription:
|
|
751
|
-
boardVisibility:
|
|
752
|
-
isBoardHidden:
|
|
753
|
-
getBoard:
|
|
754
|
-
canManageBoard:
|
|
755
|
-
managedBoards:
|
|
756
|
-
categories:
|
|
757
|
-
publicCategories:
|
|
758
|
-
categoryLabel:
|
|
759
|
-
categoryDescription:
|
|
760
|
-
categoryVisibility:
|
|
761
|
-
isCategoryHidden:
|
|
762
|
-
settingsPending:
|
|
763
|
-
ensureSettings:
|
|
764
|
-
addBoard:
|
|
765
|
-
addCategory:
|
|
766
|
-
saveBoard:
|
|
767
|
-
saveCategory:
|
|
768
|
-
saveBoards:
|
|
769
|
-
saveCategories:
|
|
770
|
-
deleteBoard:
|
|
771
|
-
deleteCategory:
|
|
772
|
-
DEFAULT_BOARDS:
|
|
773
|
-
DEFAULT_CATEGORIES:
|
|
779
|
+
boards: p,
|
|
780
|
+
publicBoards: h,
|
|
781
|
+
boardLabel: _,
|
|
782
|
+
boardDescription: b,
|
|
783
|
+
boardVisibility: S,
|
|
784
|
+
isBoardHidden: w,
|
|
785
|
+
getBoard: D,
|
|
786
|
+
canManageBoard: k,
|
|
787
|
+
managedBoards: ee,
|
|
788
|
+
categories: m,
|
|
789
|
+
publicCategories: g,
|
|
790
|
+
categoryLabel: v,
|
|
791
|
+
categoryDescription: x,
|
|
792
|
+
categoryVisibility: C,
|
|
793
|
+
isCategoryHidden: T,
|
|
794
|
+
settingsPending: f,
|
|
795
|
+
ensureSettings: se,
|
|
796
|
+
addBoard: ce,
|
|
797
|
+
addCategory: ce,
|
|
798
|
+
saveBoard: j,
|
|
799
|
+
saveCategory: ne,
|
|
800
|
+
saveBoards: re,
|
|
801
|
+
saveCategories: ie,
|
|
802
|
+
deleteBoard: ae,
|
|
803
|
+
deleteCategory: oe,
|
|
804
|
+
DEFAULT_BOARDS: Gt,
|
|
805
|
+
DEFAULT_CATEGORIES: Kt
|
|
774
806
|
};
|
|
775
807
|
}
|
|
776
808
|
//#endregion
|
|
777
809
|
//#region src/composables/useMemiBoardPosts.ts
|
|
778
|
-
var
|
|
779
|
-
function
|
|
810
|
+
var Xt = /* @__PURE__ */ new Map();
|
|
811
|
+
function Zt(e) {
|
|
780
812
|
if (!e || typeof e != "object") return !1;
|
|
781
813
|
let t = "code" in e ? String(e.code) : "";
|
|
782
814
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
783
815
|
}
|
|
784
|
-
function
|
|
785
|
-
let n =
|
|
816
|
+
function Qt(e, t) {
|
|
817
|
+
let n = L(e)?.trim();
|
|
786
818
|
if (!n) throw Error(w(t, "posts.boardIdRequired"));
|
|
787
819
|
return n;
|
|
788
820
|
}
|
|
789
|
-
function
|
|
790
|
-
let t =
|
|
791
|
-
function
|
|
792
|
-
return !r(
|
|
821
|
+
function $t(e) {
|
|
822
|
+
let t = Q(), n = Se(), { isBoardHidden: r, getBoard: i } = Yt(), { user: a, canManageBoard: o } = ht(), { t: s, locale: u } = O(), d = () => M(), f = () => Qt(e, u.value), p = () => N(t, d()), m = (e) => l(t, d(), e), h = (e) => c(t, d(), e);
|
|
823
|
+
function g() {
|
|
824
|
+
return !r(f());
|
|
793
825
|
}
|
|
794
|
-
function
|
|
795
|
-
return i(
|
|
826
|
+
function _() {
|
|
827
|
+
return i(f())?.editorType === "image";
|
|
796
828
|
}
|
|
797
|
-
function
|
|
798
|
-
let t =
|
|
829
|
+
function v(e) {
|
|
830
|
+
let t = _(), n = Pt(e.content), r = Ft(e.content, e.attachments);
|
|
799
831
|
if (t) {
|
|
800
|
-
if (!r) throw Error(
|
|
832
|
+
if (!r) throw Error(s("posts.photoRequired"));
|
|
801
833
|
return;
|
|
802
834
|
}
|
|
803
|
-
if (!n && !r) throw Error(
|
|
804
|
-
if (!e.title?.trim()) throw Error(
|
|
835
|
+
if (!n && !r) throw Error(s("posts.bodyRequired"));
|
|
836
|
+
if (!e.title?.trim()) throw Error(s("posts.titleRequired"));
|
|
805
837
|
}
|
|
806
|
-
function
|
|
807
|
-
return
|
|
838
|
+
function y() {
|
|
839
|
+
return pe(p()).id;
|
|
840
|
+
}
|
|
841
|
+
function b(e) {
|
|
842
|
+
return bt(t, d(), f(), e, a.value?.uid, o(f()));
|
|
808
843
|
}
|
|
809
|
-
async function
|
|
810
|
-
let t = e.pageSize ?? 20, n = [
|
|
811
|
-
e.publicOnly !== !1 && n.push(
|
|
812
|
-
let r = await
|
|
844
|
+
async function x(e = {}) {
|
|
845
|
+
let t = e.pageSize ?? 20, n = [Z("boardId", "==", f()), Z("isPublished", "==", !0)];
|
|
846
|
+
e.publicOnly !== !1 && n.push(Z("listed", "==", !0)), n.push(G("createdAt", "desc")), e.cursor && n.push(Y(e.cursor)), n.push(U(t + 1));
|
|
847
|
+
let r = await V(K(p(), ...n)), i = r.docs.slice(0, t);
|
|
813
848
|
return {
|
|
814
849
|
posts: i.map((e) => ({
|
|
815
850
|
id: e.id,
|
|
@@ -819,73 +854,71 @@ function Jt(e) {
|
|
|
819
854
|
hasMore: r.docs.length > t
|
|
820
855
|
};
|
|
821
856
|
}
|
|
822
|
-
async function
|
|
823
|
-
let [t, n] = await Promise.all([
|
|
857
|
+
async function S(e) {
|
|
858
|
+
let [t, n] = await Promise.all([B(m(e)), B(h(e))]);
|
|
824
859
|
return t.exists() ? {
|
|
825
860
|
id: t.id,
|
|
826
861
|
...t.data(),
|
|
827
862
|
content: n.exists() ? n.data().content ?? "" : ""
|
|
828
863
|
} : null;
|
|
829
864
|
}
|
|
830
|
-
async function
|
|
865
|
+
async function C(e) {
|
|
831
866
|
try {
|
|
832
|
-
let t =
|
|
867
|
+
let t = await b(e.trim());
|
|
833
868
|
if (!t) return { status: "not-found" };
|
|
834
869
|
try {
|
|
835
|
-
let e = await
|
|
870
|
+
let e = await S(t.id);
|
|
836
871
|
return e ? {
|
|
837
872
|
status: "ok",
|
|
838
873
|
post: e
|
|
839
874
|
} : { status: "not-found" };
|
|
840
875
|
} catch (e) {
|
|
841
|
-
return
|
|
876
|
+
return Zt(e) ? { status: "permission-denied" } : {
|
|
842
877
|
status: "error",
|
|
843
878
|
message: e instanceof Error ? e.message : String(e)
|
|
844
879
|
};
|
|
845
880
|
}
|
|
846
881
|
} catch (e) {
|
|
847
|
-
return
|
|
882
|
+
return Zt(e) ? { status: "permission-denied" } : {
|
|
848
883
|
status: "error",
|
|
849
884
|
message: e instanceof Error ? e.message : String(e)
|
|
850
885
|
};
|
|
851
886
|
}
|
|
852
887
|
}
|
|
853
|
-
async function
|
|
854
|
-
let t = e.trim(), n =
|
|
888
|
+
async function w(e) {
|
|
889
|
+
let t = e.trim(), n = Xt.get(`${f()}/${t}`);
|
|
855
890
|
if (n) return {
|
|
856
891
|
status: "ok",
|
|
857
892
|
id: n
|
|
858
893
|
};
|
|
859
894
|
try {
|
|
860
|
-
let e =
|
|
895
|
+
let e = await b(t);
|
|
861
896
|
return e ? {
|
|
862
897
|
status: "ok",
|
|
863
898
|
id: e.id
|
|
864
|
-
} : { status: "not-found" };
|
|
899
|
+
} : { status: r(f()) ? "permission-denied" : "not-found" };
|
|
865
900
|
} catch (e) {
|
|
866
|
-
return
|
|
901
|
+
return Zt(e) ? { status: "permission-denied" } : {
|
|
867
902
|
status: "error",
|
|
868
903
|
message: e instanceof Error ? e.message : String(e)
|
|
869
904
|
};
|
|
870
905
|
}
|
|
871
906
|
}
|
|
872
|
-
async function
|
|
873
|
-
|
|
874
|
-
for (; !(await z(W(d(), Y("boardId", "==", u()), Y("slug", "==", n), V(1)))).empty;) r++, n = `${t}-${r}`;
|
|
875
|
-
return n;
|
|
907
|
+
async function T(e) {
|
|
908
|
+
return `${te(e.trim()) || "post"}-${y()}`;
|
|
876
909
|
}
|
|
877
|
-
async function
|
|
910
|
+
async function E(e) {
|
|
878
911
|
if (!e.createdAt) return {
|
|
879
912
|
previous: null,
|
|
880
913
|
next: null
|
|
881
914
|
};
|
|
882
915
|
try {
|
|
883
916
|
let t = [
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
...e.listed === !1 ? [] : [
|
|
887
|
-
|
|
888
|
-
], [n, r] = await Promise.all([
|
|
917
|
+
Z("boardId", "==", f()),
|
|
918
|
+
Z("isPublished", "==", !0),
|
|
919
|
+
...e.listed === !1 ? [] : [Z("listed", "==", !0)],
|
|
920
|
+
G("createdAt", "desc")
|
|
921
|
+
], [n, r] = await Promise.all([V(K(p(), ...t, Y(e.createdAt), U(1))), V(K(p(), ...t, he(e.createdAt), ge(1)))]), i = (e) => {
|
|
889
922
|
let t = e.docs[0];
|
|
890
923
|
return t ? {
|
|
891
924
|
id: t.id,
|
|
@@ -903,13 +936,13 @@ function Jt(e) {
|
|
|
903
936
|
};
|
|
904
937
|
}
|
|
905
938
|
}
|
|
906
|
-
async function
|
|
907
|
-
|
|
908
|
-
let n = e.postId ||
|
|
909
|
-
r ? (i = n, a =
|
|
910
|
-
let o =
|
|
911
|
-
return c.set(
|
|
912
|
-
boardId:
|
|
939
|
+
async function D(e) {
|
|
940
|
+
v(e);
|
|
941
|
+
let n = e.postId || y(), r = _(), i, a;
|
|
942
|
+
r ? (i = n, a = Nt(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await T(a));
|
|
943
|
+
let o = Bt(e.content, e.attachments), s = g(), c = ve(t);
|
|
944
|
+
return c.set(m(n), {
|
|
945
|
+
boardId: f(),
|
|
913
946
|
slug: i,
|
|
914
947
|
title: a,
|
|
915
948
|
...o,
|
|
@@ -925,56 +958,56 @@ function Jt(e) {
|
|
|
925
958
|
authorPhoto: e.authorPhoto,
|
|
926
959
|
moderationStatus: "approved",
|
|
927
960
|
moderationModel: e.moderationModel ?? null,
|
|
928
|
-
createdAt:
|
|
929
|
-
updatedAt:
|
|
930
|
-
}), c.set(
|
|
931
|
-
}
|
|
932
|
-
async function
|
|
933
|
-
|
|
934
|
-
let n =
|
|
935
|
-
await Promise.all([
|
|
961
|
+
createdAt: q(),
|
|
962
|
+
updatedAt: q()
|
|
963
|
+
}), c.set(h(n), { content: e.content }), await c.commit(), Xt.set(`${f()}/${i}`, n), i;
|
|
964
|
+
}
|
|
965
|
+
async function k(e, t) {
|
|
966
|
+
v(t);
|
|
967
|
+
let n = _() ? Nt(t.content) : t.title.trim(), r = Bt(t.content, t.attachments), i = g();
|
|
968
|
+
await Promise.all([X(m(e), {
|
|
936
969
|
title: n,
|
|
937
970
|
...r,
|
|
938
|
-
previewImage: r.previewImage ? r.previewImage :
|
|
939
|
-
videoUrl: r.videoUrl ? r.videoUrl :
|
|
971
|
+
previewImage: r.previewImage ? r.previewImage : fe(),
|
|
972
|
+
videoUrl: r.videoUrl ? r.videoUrl : fe(),
|
|
940
973
|
tags: t.tags ?? [],
|
|
941
974
|
listed: i,
|
|
942
975
|
attachments: t.attachments ?? [],
|
|
943
|
-
updatedAt:
|
|
944
|
-
}),
|
|
976
|
+
updatedAt: q()
|
|
977
|
+
}), J(h(e), { content: t.content }, { merge: !0 })]);
|
|
945
978
|
}
|
|
946
|
-
async function
|
|
947
|
-
await
|
|
979
|
+
async function ee(e) {
|
|
980
|
+
await X(m(e), {
|
|
948
981
|
isPublished: !0,
|
|
949
|
-
publishedAt:
|
|
950
|
-
updatedAt:
|
|
982
|
+
publishedAt: q(),
|
|
983
|
+
updatedAt: q()
|
|
951
984
|
});
|
|
952
985
|
}
|
|
953
|
-
async function
|
|
954
|
-
await
|
|
955
|
-
for (let [t, n] of
|
|
986
|
+
async function A(e) {
|
|
987
|
+
await jt(t, Me(n), d(), e);
|
|
988
|
+
for (let [t, n] of Xt) n === e && Xt.delete(t);
|
|
956
989
|
}
|
|
957
990
|
return {
|
|
958
|
-
createPostId:
|
|
959
|
-
getPosts:
|
|
960
|
-
getPost:
|
|
961
|
-
getPostBySlug:
|
|
962
|
-
resolvePostIdBySlug:
|
|
963
|
-
resolveUniqueSlug:
|
|
964
|
-
getAdjacentPosts:
|
|
965
|
-
createPost:
|
|
966
|
-
updatePost:
|
|
967
|
-
publishPost:
|
|
968
|
-
deletePost:
|
|
991
|
+
createPostId: y,
|
|
992
|
+
getPosts: x,
|
|
993
|
+
getPost: S,
|
|
994
|
+
getPostBySlug: C,
|
|
995
|
+
resolvePostIdBySlug: w,
|
|
996
|
+
resolveUniqueSlug: T,
|
|
997
|
+
getAdjacentPosts: E,
|
|
998
|
+
createPost: D,
|
|
999
|
+
updatePost: k,
|
|
1000
|
+
publishPost: ee,
|
|
1001
|
+
deletePost: A
|
|
969
1002
|
};
|
|
970
1003
|
}
|
|
971
|
-
var
|
|
972
|
-
function
|
|
973
|
-
let n =
|
|
1004
|
+
var en = 10;
|
|
1005
|
+
function tn(e, t = {}) {
|
|
1006
|
+
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = ht(), o = () => M(), s = t.pageSize ?? en, c = P(() => i.value?.uid ?? ""), l = P(() => Qt(e)), u = () => N(n, o()), d = P(() => t.publicOnly === void 0 ? !(r.value || a(l.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), f = I([]), p = I([]), m = I([]), h = I(!0), g = I(!0), _ = I(!1), v = I(""), y = null, b = null, x, S;
|
|
974
1007
|
function C(e) {
|
|
975
1008
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
976
1009
|
}
|
|
977
|
-
let w =
|
|
1010
|
+
let w = P(() => {
|
|
978
1011
|
let e = /* @__PURE__ */ new Map();
|
|
979
1012
|
for (let t of [
|
|
980
1013
|
...p.value,
|
|
@@ -984,11 +1017,11 @@ function Xt(e, t = {}) {
|
|
|
984
1017
|
return [...e.values()].sort((e, t) => C(t) - C(e) || String(t.id).localeCompare(String(e.id)));
|
|
985
1018
|
});
|
|
986
1019
|
function T() {
|
|
987
|
-
let e = [
|
|
988
|
-
return d.value && e.push(
|
|
1020
|
+
let e = [Z("boardId", "==", l.value), Z("isPublished", "==", !0)];
|
|
1021
|
+
return d.value && e.push(Z("listed", "==", !0)), e;
|
|
989
1022
|
}
|
|
990
1023
|
function E() {
|
|
991
|
-
x?.(), x =
|
|
1024
|
+
x?.(), x = W(K(u(), ...T(), G("createdAt", "desc"), G(z(), "desc"), U(s)), (e) => {
|
|
992
1025
|
f.value = e.docs.map((e) => ({
|
|
993
1026
|
id: e.id,
|
|
994
1027
|
...e.data()
|
|
@@ -1004,7 +1037,7 @@ function Xt(e, t = {}) {
|
|
|
1004
1037
|
m.value = [];
|
|
1005
1038
|
return;
|
|
1006
1039
|
}
|
|
1007
|
-
S =
|
|
1040
|
+
S = W(K(u(), Z("boardId", "==", l.value), Z("authorUid", "==", e), Z("isPublished", "==", !1), G("createdAt", "desc"), U(20)), (e) => {
|
|
1008
1041
|
m.value = e.docs.map((e) => ({
|
|
1009
1042
|
id: e.id,
|
|
1010
1043
|
...e.data()
|
|
@@ -1019,11 +1052,11 @@ function Xt(e, t = {}) {
|
|
|
1019
1052
|
try {
|
|
1020
1053
|
let e = b ?? y, t = [
|
|
1021
1054
|
...T(),
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
...e ? [
|
|
1025
|
-
|
|
1026
|
-
], n = await
|
|
1055
|
+
G("createdAt", "desc"),
|
|
1056
|
+
G(z(), "desc"),
|
|
1057
|
+
...e ? [Y(e)] : [],
|
|
1058
|
+
U(s + 1)
|
|
1059
|
+
], n = await V(K(u(), ...t)), r = n.docs.slice(0, s), i = r.map((e) => ({
|
|
1027
1060
|
id: e.id,
|
|
1028
1061
|
...e.data()
|
|
1029
1062
|
})), a = new Set([...p.value, ...f.value].map((e) => e.id));
|
|
@@ -1038,11 +1071,11 @@ function Xt(e, t = {}) {
|
|
|
1038
1071
|
function k() {
|
|
1039
1072
|
x?.(), f.value = [], p.value = [], y = null, b = null, g.value = !0, v.value = "", h.value = !0, E(), D();
|
|
1040
1073
|
}
|
|
1041
|
-
return
|
|
1074
|
+
return R([
|
|
1042
1075
|
l,
|
|
1043
1076
|
d,
|
|
1044
1077
|
c
|
|
1045
|
-
], k, { immediate: !0 }),
|
|
1078
|
+
], k, { immediate: !0 }), F(() => {
|
|
1046
1079
|
x?.(), S?.();
|
|
1047
1080
|
}), {
|
|
1048
1081
|
posts: w,
|
|
@@ -1053,22 +1086,22 @@ function Xt(e, t = {}) {
|
|
|
1053
1086
|
loadMore: O
|
|
1054
1087
|
};
|
|
1055
1088
|
}
|
|
1056
|
-
function
|
|
1057
|
-
let n =
|
|
1089
|
+
function nn(e, t) {
|
|
1090
|
+
let n = Q(), r = () => M(), i = P(() => Qt(e)), a = P(() => typeof t == "string" ? t : t.value), o = P(() => l(n, r(), a.value)), s = P(() => c(n, r(), a.value)), { data: u, pending: d } = xe(o), { data: f, pending: p } = xe(s);
|
|
1058
1091
|
return {
|
|
1059
|
-
post:
|
|
1092
|
+
post: P(() => !u.value || u.value.boardId !== i.value ? null : {
|
|
1060
1093
|
...u.value,
|
|
1061
1094
|
id: a.value,
|
|
1062
1095
|
content: f.value?.content ?? ""
|
|
1063
1096
|
}),
|
|
1064
|
-
pending:
|
|
1097
|
+
pending: P(() => d.value || p.value)
|
|
1065
1098
|
};
|
|
1066
1099
|
}
|
|
1067
1100
|
//#endregion
|
|
1068
1101
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
1069
|
-
var
|
|
1070
|
-
function
|
|
1071
|
-
let n =
|
|
1102
|
+
var rn = 10;
|
|
1103
|
+
function an(e, t = {}) {
|
|
1104
|
+
let n = Q(), r = () => M(), i = () => N(n, r()), a = t.pageSize ?? rn, o = P(() => L(e)?.trim() || ""), s = I([]), c = I([]), l = I(!0), u = I(!0), d = I(!1), f = I(""), p = null, m = null, h;
|
|
1072
1105
|
function g(e) {
|
|
1073
1106
|
return {
|
|
1074
1107
|
id: e.id,
|
|
@@ -1078,16 +1111,16 @@ function $t(e, t = {}) {
|
|
|
1078
1111
|
function _(e) {
|
|
1079
1112
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1080
1113
|
}
|
|
1081
|
-
let v =
|
|
1114
|
+
let v = P(() => {
|
|
1082
1115
|
let e = /* @__PURE__ */ new Map();
|
|
1083
1116
|
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1084
1117
|
return [...e.values()].sort((e, t) => _(t) - _(e) || String(t.id).localeCompare(String(e.id)));
|
|
1085
1118
|
});
|
|
1086
1119
|
function y() {
|
|
1087
1120
|
return [
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1121
|
+
Z("authorUid", "==", o.value),
|
|
1122
|
+
Z("isPublished", "==", !0),
|
|
1123
|
+
Z("listed", "==", !0)
|
|
1091
1124
|
];
|
|
1092
1125
|
}
|
|
1093
1126
|
function b() {
|
|
@@ -1095,7 +1128,7 @@ function $t(e, t = {}) {
|
|
|
1095
1128
|
l.value = !1;
|
|
1096
1129
|
return;
|
|
1097
1130
|
}
|
|
1098
|
-
h =
|
|
1131
|
+
h = W(K(i(), ...y(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
|
|
1099
1132
|
s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1100
1133
|
}, (e) => {
|
|
1101
1134
|
console.error("[memi-board:userPosts] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1107,11 +1140,11 @@ function $t(e, t = {}) {
|
|
|
1107
1140
|
try {
|
|
1108
1141
|
let e = m ?? p, t = [
|
|
1109
1142
|
...y(),
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
...e ? [
|
|
1113
|
-
|
|
1114
|
-
], n = await
|
|
1143
|
+
G("createdAt", "desc"),
|
|
1144
|
+
G(z(), "desc"),
|
|
1145
|
+
...e ? [Y(e)] : [],
|
|
1146
|
+
U(a + 1)
|
|
1147
|
+
], n = await V(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(g), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1115
1148
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1116
1149
|
} catch (e) {
|
|
1117
1150
|
f.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
|
|
@@ -1123,7 +1156,7 @@ function $t(e, t = {}) {
|
|
|
1123
1156
|
function S() {
|
|
1124
1157
|
h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, b();
|
|
1125
1158
|
}
|
|
1126
|
-
return
|
|
1159
|
+
return R(o, S, { immediate: !0 }), F(() => h?.()), {
|
|
1127
1160
|
posts: v,
|
|
1128
1161
|
postsPending: l,
|
|
1129
1162
|
hasMore: u,
|
|
@@ -1134,9 +1167,9 @@ function $t(e, t = {}) {
|
|
|
1134
1167
|
}
|
|
1135
1168
|
//#endregion
|
|
1136
1169
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1137
|
-
var
|
|
1138
|
-
function
|
|
1139
|
-
let n =
|
|
1170
|
+
var on = 10;
|
|
1171
|
+
function sn(e, t = {}) {
|
|
1172
|
+
let n = Q(), r = () => M(), i = () => u(n, r()), a = t.pageSize ?? on, o = P(() => L(e)?.trim() || ""), s = I([]), c = I([]), l = I(!0), d = I(!0), f = I(!1), p = I(""), m = null, h = null, g;
|
|
1140
1173
|
function _(e) {
|
|
1141
1174
|
return {
|
|
1142
1175
|
id: e.id,
|
|
@@ -1146,20 +1179,20 @@ function tn(e, t = {}) {
|
|
|
1146
1179
|
function v(e) {
|
|
1147
1180
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1148
1181
|
}
|
|
1149
|
-
let y =
|
|
1182
|
+
let y = P(() => {
|
|
1150
1183
|
let e = /* @__PURE__ */ new Map();
|
|
1151
1184
|
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1152
1185
|
return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
|
|
1153
1186
|
});
|
|
1154
1187
|
function b() {
|
|
1155
|
-
return [
|
|
1188
|
+
return [Z("authorUid", "==", o.value), Z("listed", "==", !0)];
|
|
1156
1189
|
}
|
|
1157
1190
|
function x() {
|
|
1158
1191
|
if (g?.(), !o.value) {
|
|
1159
1192
|
l.value = !1;
|
|
1160
1193
|
return;
|
|
1161
1194
|
}
|
|
1162
|
-
g =
|
|
1195
|
+
g = W(K(i(), ...b(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
|
|
1163
1196
|
s.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), l.value = !1;
|
|
1164
1197
|
}, (e) => {
|
|
1165
1198
|
console.error("[memi-board:userComments] onSnapshot failed", e), p.value = e.message || String(e), l.value = !1;
|
|
@@ -1171,11 +1204,11 @@ function tn(e, t = {}) {
|
|
|
1171
1204
|
try {
|
|
1172
1205
|
let e = h ?? m, t = [
|
|
1173
1206
|
...b(),
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
...e ? [
|
|
1177
|
-
|
|
1178
|
-
], n = await
|
|
1207
|
+
G("createdAt", "desc"),
|
|
1208
|
+
G(z(), "desc"),
|
|
1209
|
+
...e ? [Y(e)] : [],
|
|
1210
|
+
U(a + 1)
|
|
1211
|
+
], n = await V(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1179
1212
|
c.value.push(...o.filter((e) => !l.has(e.id))), h = r.at(-1) ?? h, d.value = n.docs.length > a;
|
|
1180
1213
|
} catch (e) {
|
|
1181
1214
|
p.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
@@ -1187,7 +1220,7 @@ function tn(e, t = {}) {
|
|
|
1187
1220
|
function C() {
|
|
1188
1221
|
g?.(), s.value = [], c.value = [], m = null, h = null, d.value = !0, p.value = "", l.value = !0, x();
|
|
1189
1222
|
}
|
|
1190
|
-
return
|
|
1223
|
+
return R(o, C, { immediate: !0 }), F(() => g?.()), {
|
|
1191
1224
|
comments: y,
|
|
1192
1225
|
commentsPending: l,
|
|
1193
1226
|
hasMore: d,
|
|
@@ -1198,12 +1231,12 @@ function tn(e, t = {}) {
|
|
|
1198
1231
|
}
|
|
1199
1232
|
//#endregion
|
|
1200
1233
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1201
|
-
var
|
|
1202
|
-
function
|
|
1203
|
-
let n =
|
|
1204
|
-
let e = Math.floor(Number(
|
|
1205
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1206
|
-
}), o =
|
|
1234
|
+
var cn = 10;
|
|
1235
|
+
function ln(e = "latest", t = {}) {
|
|
1236
|
+
let n = Q(), r = () => M(), i = () => u(n, r()), a = P(() => {
|
|
1237
|
+
let e = Math.floor(Number(L(t.pageSize)));
|
|
1238
|
+
return Number.isFinite(e) && e > 0 ? e : cn;
|
|
1239
|
+
}), o = P(() => L(e) === "likes" ? "likes" : "latest"), s = I([]), c = I([]), l = I(!0), d = I(!0), f = I(!1), p = I(""), m = null, h = null, g;
|
|
1207
1240
|
function _(e) {
|
|
1208
1241
|
return {
|
|
1209
1242
|
id: e.id,
|
|
@@ -1220,25 +1253,25 @@ function rn(e = "latest", t = {}) {
|
|
|
1220
1253
|
}
|
|
1221
1254
|
return v(t) - v(e) || String(t.id).localeCompare(String(e.id));
|
|
1222
1255
|
}
|
|
1223
|
-
let b =
|
|
1256
|
+
let b = P(() => {
|
|
1224
1257
|
let e = /* @__PURE__ */ new Map();
|
|
1225
1258
|
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1226
1259
|
return [...e.values()].sort(y);
|
|
1227
1260
|
});
|
|
1228
1261
|
function x() {
|
|
1229
1262
|
return o.value === "likes" ? [
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1263
|
+
Z("listed", "==", !0),
|
|
1264
|
+
G("likeCount", "desc"),
|
|
1265
|
+
G("createdAt", "desc"),
|
|
1266
|
+
G(z(), "desc")
|
|
1234
1267
|
] : [
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1268
|
+
Z("listed", "==", !0),
|
|
1269
|
+
G("createdAt", "desc"),
|
|
1270
|
+
G(z(), "desc")
|
|
1238
1271
|
];
|
|
1239
1272
|
}
|
|
1240
1273
|
function S() {
|
|
1241
|
-
g?.(), g =
|
|
1274
|
+
g?.(), g = W(K(i(), ...x(), U(a.value)), (e) => {
|
|
1242
1275
|
s.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a.value), l.value = !1;
|
|
1243
1276
|
}, (e) => {
|
|
1244
1277
|
console.error("[memi-board:commentFeed] onSnapshot failed", e), p.value = e.message || String(e), l.value = !1;
|
|
@@ -1248,7 +1281,7 @@ function rn(e = "latest", t = {}) {
|
|
|
1248
1281
|
if (!(f.value || !d.value || l.value)) {
|
|
1249
1282
|
f.value = !0, p.value = "";
|
|
1250
1283
|
try {
|
|
1251
|
-
let e = h ?? m, t = await
|
|
1284
|
+
let e = h ?? m, t = await V(K(i(), ...x(), ...e ? [Y(e)] : [], U(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1252
1285
|
c.value.push(...r.filter((e) => !o.has(e.id))), h = n.at(-1) ?? h, d.value = t.docs.length > a.value;
|
|
1253
1286
|
} catch (e) {
|
|
1254
1287
|
p.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
@@ -1260,7 +1293,7 @@ function rn(e = "latest", t = {}) {
|
|
|
1260
1293
|
function w() {
|
|
1261
1294
|
g?.(), s.value = [], c.value = [], m = null, h = null, d.value = !0, p.value = "", l.value = !0, S();
|
|
1262
1295
|
}
|
|
1263
|
-
return
|
|
1296
|
+
return R([o, a], w, { immediate: !0 }), F(() => g?.()), {
|
|
1264
1297
|
comments: b,
|
|
1265
1298
|
commentsPending: l,
|
|
1266
1299
|
hasMore: d,
|
|
@@ -1272,35 +1305,35 @@ function rn(e = "latest", t = {}) {
|
|
|
1272
1305
|
}
|
|
1273
1306
|
//#endregion
|
|
1274
1307
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1275
|
-
var
|
|
1308
|
+
var un = 100, dn = /* @__PURE__ */ new Map();
|
|
1276
1309
|
function $(e) {
|
|
1277
|
-
let t =
|
|
1278
|
-
return t || (t =
|
|
1310
|
+
let t = dn.get(e);
|
|
1311
|
+
return t || (t = ue({
|
|
1279
1312
|
text: "",
|
|
1280
1313
|
sentiment: null,
|
|
1281
1314
|
loaded: !1,
|
|
1282
1315
|
pending: !1,
|
|
1283
1316
|
saving: !1,
|
|
1284
1317
|
error: ""
|
|
1285
|
-
}),
|
|
1318
|
+
}), dn.set(e, t)), t;
|
|
1286
1319
|
}
|
|
1287
|
-
function
|
|
1288
|
-
let t =
|
|
1320
|
+
function fn(e) {
|
|
1321
|
+
let t = Q(), n = () => M(), { user: r } = ht(), { t: a, formatNumber: o } = O(), s = P(() => L(e)), c = P(() => `${r.value?.uid ?? ""}:${s.value}`), l = P(() => {
|
|
1289
1322
|
let e = r.value?.uid, a = s.value;
|
|
1290
1323
|
return !e || !a ? null : i(t, n(), e, a);
|
|
1291
|
-
}), u =
|
|
1324
|
+
}), u = P({
|
|
1292
1325
|
get: () => $(c.value).text,
|
|
1293
1326
|
set: (e) => {
|
|
1294
1327
|
$(c.value).text = e;
|
|
1295
1328
|
}
|
|
1296
|
-
}), d =
|
|
1329
|
+
}), d = P(() => $(c.value).sentiment), f = P(() => $(c.value).loaded), p = P(() => $(c.value).pending), m = P(() => $(c.value).saving), h = P(() => $(c.value).error);
|
|
1297
1330
|
async function g() {
|
|
1298
1331
|
let e = l.value;
|
|
1299
1332
|
if (!e) return;
|
|
1300
1333
|
let t = $(c.value);
|
|
1301
1334
|
t.pending = !0, t.error = "";
|
|
1302
1335
|
try {
|
|
1303
|
-
let n = (await
|
|
1336
|
+
let n = (await B(e)).data();
|
|
1304
1337
|
t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
|
|
1305
1338
|
} catch (e) {
|
|
1306
1339
|
t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
|
|
@@ -1316,10 +1349,10 @@ function sn(e) {
|
|
|
1316
1349
|
let i = $(c.value);
|
|
1317
1350
|
i.saving = !0, i.error = "";
|
|
1318
1351
|
try {
|
|
1319
|
-
await
|
|
1352
|
+
await J(n, {
|
|
1320
1353
|
text: r,
|
|
1321
1354
|
sentiment: t,
|
|
1322
|
-
updatedAt:
|
|
1355
|
+
updatedAt: q()
|
|
1323
1356
|
}), i.text = r, i.sentiment = t, i.loaded = !0;
|
|
1324
1357
|
} catch (e) {
|
|
1325
1358
|
throw i.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
|
|
@@ -1333,7 +1366,7 @@ function sn(e) {
|
|
|
1333
1366
|
let t = $(c.value);
|
|
1334
1367
|
t.saving = !0, t.error = "";
|
|
1335
1368
|
try {
|
|
1336
|
-
await
|
|
1369
|
+
await de(e), t.text = "", t.sentiment = null, t.loaded = !0;
|
|
1337
1370
|
} catch (e) {
|
|
1338
1371
|
throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
|
|
1339
1372
|
} finally {
|
|
@@ -1353,86 +1386,91 @@ function sn(e) {
|
|
|
1353
1386
|
};
|
|
1354
1387
|
}
|
|
1355
1388
|
//#endregion
|
|
1389
|
+
//#region src/utils/subscribeCommentRange.ts
|
|
1390
|
+
function pn(e, t, n, r, i, a) {
|
|
1391
|
+
return W(K(u(e, t), Z("postId", "==", n), ...r.rootId ? [Z("rootId", "==", r.rootId), Z("isReply", "==", !0)] : [Z("parentId", "==", null)], G("createdAt", "asc"), G(z(), "asc"), ...r.hasMore && r.cursor ? [me(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
|
|
1392
|
+
...e.data(),
|
|
1393
|
+
id: e.id
|
|
1394
|
+
}))), a);
|
|
1395
|
+
}
|
|
1396
|
+
//#endregion
|
|
1356
1397
|
//#region src/composables/useMemiBoardComments.ts
|
|
1357
|
-
var
|
|
1358
|
-
function
|
|
1398
|
+
var mn = 10, hn = 5, gn = 1e3;
|
|
1399
|
+
function _n(e, { t, formatNumber: n }) {
|
|
1359
1400
|
let r = e.trim();
|
|
1360
1401
|
if (!r) throw Error(t("comments.bodyRequired"));
|
|
1361
|
-
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(
|
|
1402
|
+
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(gn) }));
|
|
1362
1403
|
return r;
|
|
1363
1404
|
}
|
|
1364
|
-
function
|
|
1365
|
-
let r =
|
|
1405
|
+
function vn(e, t, n = {}) {
|
|
1406
|
+
let r = Q(), i = () => M(), { isBoardHidden: a } = Yt(), o = O(), { t: s } = o, c = () => u(r, i()), d = P(() => L(e)), f = P(() => L(t)), p = () => !a(d.value), m = I([]), h = I(n.subscribe !== !1), g = I([]), _ = I(!1), v = I(n.subscribe !== !1), y = I(n.subscribe !== !1), b = null, x = 0, S = 0, C;
|
|
1366
1407
|
function w(e) {
|
|
1367
1408
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1368
1409
|
}
|
|
1369
|
-
let T =
|
|
1410
|
+
let T = P(() => {
|
|
1370
1411
|
let e = /* @__PURE__ */ new Map();
|
|
1371
|
-
for (let t of [
|
|
1372
|
-
...m.value,
|
|
1373
|
-
...h.value,
|
|
1374
|
-
..._.value
|
|
1375
|
-
]) t.id && e.set(t.id, t);
|
|
1412
|
+
for (let t of [...m.value, ...g.value]) t.id && e.set(t.id, t);
|
|
1376
1413
|
return [...e.values()].sort((e, t) => w(e) - w(t) || String(e.id).localeCompare(String(t.id)));
|
|
1377
1414
|
});
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1415
|
+
R([f, d], async ([e]) => {
|
|
1416
|
+
let t = ++x;
|
|
1417
|
+
if (S++, _.value = !1, g.value = [], m.value = [], b = null, C?.(), C = void 0, v.value = n.subscribe !== !1, n.subscribe !== !1) {
|
|
1418
|
+
h.value = !0, y.value = !0;
|
|
1381
1419
|
try {
|
|
1382
|
-
let
|
|
1383
|
-
if (await Promise.all([
|
|
1384
|
-
if (
|
|
1385
|
-
let n =
|
|
1420
|
+
let n = V(K(c(), Z("postId", "==", e), G("createdAt", "desc"), G(z(), "desc")));
|
|
1421
|
+
if (await Promise.all([n.then((e) => {
|
|
1422
|
+
if (t !== x) return;
|
|
1423
|
+
let n = e.docs.map((e) => ({
|
|
1386
1424
|
id: e.id,
|
|
1387
1425
|
...e.data()
|
|
1388
1426
|
}));
|
|
1389
|
-
|
|
1390
|
-
}), D()]),
|
|
1427
|
+
m.value = n.filter((e) => e.parentId === void 0);
|
|
1428
|
+
}), D()]), t !== x) return;
|
|
1391
1429
|
} finally {
|
|
1392
|
-
|
|
1430
|
+
t === x && (h.value = !1, y.value = !1);
|
|
1393
1431
|
}
|
|
1394
1432
|
}
|
|
1395
1433
|
}, { immediate: !0 });
|
|
1396
1434
|
function E() {
|
|
1397
|
-
C?.()
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1435
|
+
C?.();
|
|
1436
|
+
let e = ++S;
|
|
1437
|
+
C = pn(r, i(), f.value, {
|
|
1438
|
+
cursor: b,
|
|
1439
|
+
hasMore: v.value
|
|
1440
|
+
}, (t) => {
|
|
1441
|
+
e === S && (g.value = t);
|
|
1404
1442
|
}, (e) => {
|
|
1405
1443
|
console.error("[memi-board:comments] onSnapshot failed", e);
|
|
1406
1444
|
});
|
|
1407
1445
|
}
|
|
1408
1446
|
async function D() {
|
|
1409
|
-
if (
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1447
|
+
if (_.value || !v.value) return;
|
|
1448
|
+
let e = x;
|
|
1449
|
+
_.value = !0;
|
|
1450
|
+
try {
|
|
1451
|
+
let t = [
|
|
1452
|
+
Z("postId", "==", f.value),
|
|
1453
|
+
Z("parentId", "==", null),
|
|
1454
|
+
G("createdAt", "asc"),
|
|
1455
|
+
G(z(), "asc"),
|
|
1456
|
+
...b ? [Y(b)] : [],
|
|
1457
|
+
U(11)
|
|
1458
|
+
], n = await V(K(c(), ...t));
|
|
1459
|
+
if (e !== x) return;
|
|
1460
|
+
let r = n.docs.slice(0, mn), i = r.map((e) => ({
|
|
1461
|
+
id: e.id,
|
|
1462
|
+
...e.data()
|
|
1463
|
+
})), a = new Set(g.value.map((e) => e.id));
|
|
1464
|
+
g.value.push(...i.filter((e) => !a.has(e.id))), b = r.at(-1) ?? b, v.value = n.size > mn, E();
|
|
1465
|
+
} finally {
|
|
1466
|
+
e === x && (_.value = !1);
|
|
1429
1467
|
}
|
|
1430
1468
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1469
|
+
F(() => {
|
|
1470
|
+
x++, S++, C?.();
|
|
1433
1471
|
});
|
|
1434
1472
|
async function ee(e) {
|
|
1435
|
-
let t =
|
|
1473
|
+
let t = _n(e.body, o), n = ve(r), a = pe(c());
|
|
1436
1474
|
n.set(a, {
|
|
1437
1475
|
postId: f.value,
|
|
1438
1476
|
boardId: d.value,
|
|
@@ -1450,14 +1488,14 @@ function fn(e, t, n = {}) {
|
|
|
1450
1488
|
replyCount: 0,
|
|
1451
1489
|
likeCount: 0,
|
|
1452
1490
|
isReply: !1,
|
|
1453
|
-
createdAt:
|
|
1454
|
-
updatedAt:
|
|
1455
|
-
}), n.update(l(r, i(), f.value), { commentCount:
|
|
1491
|
+
createdAt: q(),
|
|
1492
|
+
updatedAt: q()
|
|
1493
|
+
}), n.update(l(r, i(), f.value), { commentCount: H(1) }), await n.commit();
|
|
1456
1494
|
}
|
|
1457
|
-
async function
|
|
1458
|
-
let t =
|
|
1495
|
+
async function A(e) {
|
|
1496
|
+
let t = _n(e.body, o);
|
|
1459
1497
|
if (!e.parent.id) throw Error(s("comments.replyTargetNotFound"));
|
|
1460
|
-
let n =
|
|
1498
|
+
let n = ve(r), a = pe(c()), u = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1461
1499
|
if (!u) throw Error(s("comments.threadNotFound"));
|
|
1462
1500
|
let m = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1463
1501
|
n.set(a, {
|
|
@@ -1477,116 +1515,121 @@ function fn(e, t, n = {}) {
|
|
|
1477
1515
|
replyCount: 0,
|
|
1478
1516
|
likeCount: 0,
|
|
1479
1517
|
isReply: !0,
|
|
1480
|
-
createdAt:
|
|
1481
|
-
updatedAt:
|
|
1482
|
-
}), n.update(k(r, i(), u), { replyCount:
|
|
1518
|
+
createdAt: q(),
|
|
1519
|
+
updatedAt: q()
|
|
1520
|
+
}), n.update(k(r, i(), u), { replyCount: H(1) }), n.update(l(r, i(), f.value), { commentCount: H(1) }), await n.commit();
|
|
1483
1521
|
}
|
|
1484
|
-
async function
|
|
1522
|
+
async function j(e) {
|
|
1485
1523
|
if (!e.id) return;
|
|
1486
1524
|
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(s("comments.hasRepliesCannotDelete"));
|
|
1487
|
-
await
|
|
1488
|
-
let t =
|
|
1489
|
-
t.delete(k(r, i(), e.id)), e.parentId && e.rootId && t.update(k(r, i(), e.rootId), { replyCount:
|
|
1525
|
+
await At(r, i(), e.id);
|
|
1526
|
+
let t = ve(r);
|
|
1527
|
+
t.delete(k(r, i(), e.id)), e.parentId && e.rootId && t.update(k(r, i(), e.rootId), { replyCount: H(-1) }), t.update(l(r, i(), f.value), { commentCount: H(-1) }), await t.commit(), g.value = g.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
|
|
1490
1528
|
}
|
|
1491
|
-
async function
|
|
1492
|
-
let n =
|
|
1493
|
-
await
|
|
1529
|
+
async function te(e, t) {
|
|
1530
|
+
let n = _n(t, o);
|
|
1531
|
+
await X(k(r, i(), e), {
|
|
1494
1532
|
body: n,
|
|
1495
|
-
updatedAt:
|
|
1533
|
+
updatedAt: q()
|
|
1496
1534
|
});
|
|
1497
1535
|
}
|
|
1498
|
-
async function
|
|
1499
|
-
await
|
|
1536
|
+
async function ne(e, t, n) {
|
|
1537
|
+
await X(k(r, i(), e), {
|
|
1500
1538
|
isBlinded: t,
|
|
1501
|
-
moderatedAt:
|
|
1539
|
+
moderatedAt: q(),
|
|
1502
1540
|
moderatedBy: n
|
|
1503
1541
|
});
|
|
1504
1542
|
}
|
|
1505
1543
|
return {
|
|
1506
1544
|
comments: T,
|
|
1507
|
-
commentsPending:
|
|
1508
|
-
hasMore:
|
|
1509
|
-
loadingMore:
|
|
1545
|
+
commentsPending: P(() => y.value || h.value),
|
|
1546
|
+
hasMore: v,
|
|
1547
|
+
loadingMore: _,
|
|
1510
1548
|
loadMore: D,
|
|
1511
1549
|
addComment: ee,
|
|
1512
|
-
addReply:
|
|
1513
|
-
updateComment:
|
|
1514
|
-
setCommentBlinded:
|
|
1515
|
-
deleteComment:
|
|
1550
|
+
addReply: A,
|
|
1551
|
+
updateComment: te,
|
|
1552
|
+
setCommentBlinded: ne,
|
|
1553
|
+
deleteComment: j
|
|
1516
1554
|
};
|
|
1517
1555
|
}
|
|
1518
|
-
function
|
|
1519
|
-
let r =
|
|
1520
|
-
function
|
|
1556
|
+
function yn(e, t, n) {
|
|
1557
|
+
let r = Q(), i = () => M(), a = () => u(r, i()), o = t.trim(), s = I([]), c = I(!1), l = I(!1), d = I(!0), f = null, p, m = 0, h = 0;
|
|
1558
|
+
function g(e) {
|
|
1521
1559
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
1522
1560
|
}
|
|
1523
|
-
function
|
|
1524
|
-
p
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1561
|
+
function _() {
|
|
1562
|
+
p?.();
|
|
1563
|
+
let e = ++h;
|
|
1564
|
+
p = pn(r, i(), o, {
|
|
1565
|
+
rootId: n,
|
|
1566
|
+
cursor: f,
|
|
1567
|
+
hasMore: d.value
|
|
1568
|
+
}, (t) => {
|
|
1569
|
+
e === h && (s.value = t);
|
|
1531
1570
|
}, (e) => {
|
|
1532
1571
|
console.error("[memi-board:replies] onSnapshot failed", e);
|
|
1533
1572
|
});
|
|
1534
1573
|
}
|
|
1535
|
-
async function
|
|
1536
|
-
if (
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1574
|
+
async function v() {
|
|
1575
|
+
if (c.value || !d.value) return;
|
|
1576
|
+
let e = m;
|
|
1577
|
+
c.value = !0;
|
|
1578
|
+
try {
|
|
1579
|
+
let t = [
|
|
1580
|
+
Z("postId", "==", o),
|
|
1581
|
+
Z("rootId", "==", n),
|
|
1582
|
+
Z("isReply", "==", !0),
|
|
1583
|
+
G("createdAt", "asc"),
|
|
1584
|
+
G(z(), "asc"),
|
|
1585
|
+
...f ? [Y(f)] : [],
|
|
1586
|
+
U(6)
|
|
1587
|
+
], r = await V(K(a(), ...t));
|
|
1588
|
+
if (e !== m) return;
|
|
1589
|
+
let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, hn), u = c.map((e) => ({
|
|
1590
|
+
id: e.id,
|
|
1591
|
+
...e.data()
|
|
1592
|
+
})).filter((e) => !i.has(e.id));
|
|
1593
|
+
s.value = g([...s.value, ...u]), f = c.at(-1) ?? f, l.value = !0, d.value = r.size > hn, _();
|
|
1594
|
+
} finally {
|
|
1595
|
+
e === m && (c.value = !1);
|
|
1555
1596
|
}
|
|
1556
1597
|
}
|
|
1557
|
-
function
|
|
1558
|
-
p?.(), p = void 0, s.value = [], f = null, l.value = !1, d.value = !0;
|
|
1598
|
+
function y() {
|
|
1599
|
+
m++, h++, c.value = !1, p?.(), p = void 0, s.value = [], f = null, l.value = !1, d.value = !0;
|
|
1559
1600
|
}
|
|
1560
|
-
async function
|
|
1561
|
-
|
|
1601
|
+
async function b() {
|
|
1602
|
+
y(), await v();
|
|
1562
1603
|
}
|
|
1563
|
-
return
|
|
1604
|
+
return F(() => {
|
|
1605
|
+
m++, h++, p?.();
|
|
1606
|
+
}), {
|
|
1564
1607
|
replies: s,
|
|
1565
1608
|
loading: c,
|
|
1566
1609
|
loaded: l,
|
|
1567
1610
|
hasMore: d,
|
|
1568
|
-
loadMore:
|
|
1569
|
-
refresh:
|
|
1611
|
+
loadMore: v,
|
|
1612
|
+
refresh: b
|
|
1570
1613
|
};
|
|
1571
1614
|
}
|
|
1572
1615
|
//#endregion
|
|
1573
1616
|
//#region src/composables/useMemiBoardLikes.ts
|
|
1574
|
-
function
|
|
1575
|
-
let n = () =>
|
|
1617
|
+
function bn(e, t) {
|
|
1618
|
+
let n = () => M(), r = Q(), i = be(), { t: a } = O(), o = I(!1), s = I(!1), c;
|
|
1576
1619
|
function u(e) {
|
|
1577
1620
|
return d(r, n(), t, e);
|
|
1578
1621
|
}
|
|
1579
|
-
|
|
1622
|
+
R(i, (e) => {
|
|
1580
1623
|
if (c?.(), c = void 0, !e) {
|
|
1581
1624
|
o.value = !1;
|
|
1582
1625
|
return;
|
|
1583
1626
|
}
|
|
1584
|
-
c =
|
|
1627
|
+
c = W(u(e.uid), (e) => {
|
|
1585
1628
|
o.value = e.exists();
|
|
1586
1629
|
}, (e) => {
|
|
1587
1630
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
1588
1631
|
});
|
|
1589
|
-
}, { immediate: !0 }),
|
|
1632
|
+
}, { immediate: !0 }), F(() => c?.());
|
|
1590
1633
|
async function f() {
|
|
1591
1634
|
let c = i.value?.uid;
|
|
1592
1635
|
if (!c) throw Error(a("likes.signInRequired"));
|
|
@@ -1594,12 +1637,12 @@ function mn(e, t) {
|
|
|
1594
1637
|
s.value = !0;
|
|
1595
1638
|
try {
|
|
1596
1639
|
let i = u(c), a = l(r, n(), t);
|
|
1597
|
-
return await
|
|
1640
|
+
return await _e(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: H(-1) }), !1) : (n.set(i, {
|
|
1598
1641
|
uid: c,
|
|
1599
1642
|
postId: t,
|
|
1600
1643
|
boardId: e,
|
|
1601
|
-
createdAt:
|
|
1602
|
-
}), n.update(a, { likeCount:
|
|
1644
|
+
createdAt: q()
|
|
1645
|
+
}), n.update(a, { likeCount: H(1) }), !0));
|
|
1603
1646
|
} finally {
|
|
1604
1647
|
s.value = !1;
|
|
1605
1648
|
}
|
|
@@ -1610,11 +1653,11 @@ function mn(e, t) {
|
|
|
1610
1653
|
toggleLike: f
|
|
1611
1654
|
};
|
|
1612
1655
|
}
|
|
1613
|
-
var
|
|
1614
|
-
function
|
|
1615
|
-
let n = () =>
|
|
1616
|
-
|
|
1617
|
-
f?.(), f = void 0, l.value = /* @__PURE__ */ new Set(), e && t && (f =
|
|
1656
|
+
var xn = Symbol.for("memi-board:comment-likes");
|
|
1657
|
+
function Sn(e, t) {
|
|
1658
|
+
let n = () => M(), r = Q(), i = be(), { t: o } = O(), s = P(() => L(e)), c = P(() => L(t)), l = I(/* @__PURE__ */ new Set()), u = I(null), f;
|
|
1659
|
+
R([i, c], ([e, t]) => {
|
|
1660
|
+
f?.(), f = void 0, l.value = /* @__PURE__ */ new Set(), e && t && (f = W(K(a(r, n()), Z("uid", "==", e.uid), Z("postId", "==", t), Z("target", "==", "comment")), (e) => {
|
|
1618
1661
|
let t = /* @__PURE__ */ new Set();
|
|
1619
1662
|
for (let n of e.docs) {
|
|
1620
1663
|
let e = n.data().commentId;
|
|
@@ -1624,7 +1667,7 @@ function gn(e, t) {
|
|
|
1624
1667
|
}, (e) => {
|
|
1625
1668
|
console.error("[memi-board:comment-likes] onSnapshot failed", e);
|
|
1626
1669
|
}));
|
|
1627
|
-
}, { immediate: !0 }),
|
|
1670
|
+
}, { immediate: !0 }), F(() => f?.());
|
|
1628
1671
|
function p(e) {
|
|
1629
1672
|
return !!e && l.value.has(e);
|
|
1630
1673
|
}
|
|
@@ -1638,7 +1681,7 @@ function gn(e, t) {
|
|
|
1638
1681
|
if (u.value) return p(e);
|
|
1639
1682
|
u.value = e;
|
|
1640
1683
|
try {
|
|
1641
|
-
let i = d(r, n(), e, t), a = k(r, n(), e), u = await
|
|
1684
|
+
let i = d(r, n(), e, t), a = k(r, n(), e), u = await _e(r, async (n) => {
|
|
1642
1685
|
let r = await n.get(i), l = await n.get(a);
|
|
1643
1686
|
if (!l.exists()) throw Error(o("likes.commentNotFound"));
|
|
1644
1687
|
let u = Number(l.data()?.likeCount ?? 0);
|
|
@@ -1648,7 +1691,7 @@ function gn(e, t) {
|
|
|
1648
1691
|
boardId: s.value,
|
|
1649
1692
|
commentId: e,
|
|
1650
1693
|
target: "comment",
|
|
1651
|
-
createdAt:
|
|
1694
|
+
createdAt: q()
|
|
1652
1695
|
}), n.update(a, { likeCount: u + 1 }), !0);
|
|
1653
1696
|
}), f = new Set(l.value);
|
|
1654
1697
|
return u ? f.add(e) : f.delete(e), l.value = f, u;
|
|
@@ -1664,7 +1707,7 @@ function gn(e, t) {
|
|
|
1664
1707
|
}
|
|
1665
1708
|
//#endregion
|
|
1666
1709
|
//#region src/composables/useMemiBoardReports.ts
|
|
1667
|
-
var
|
|
1710
|
+
var Cn = 200, wn = [
|
|
1668
1711
|
{
|
|
1669
1712
|
value: "spam",
|
|
1670
1713
|
label: "스팸·광고"
|
|
@@ -1686,34 +1729,34 @@ var _n = 200, vn = [
|
|
|
1686
1729
|
label: "기타"
|
|
1687
1730
|
}
|
|
1688
1731
|
];
|
|
1689
|
-
function
|
|
1690
|
-
return
|
|
1732
|
+
function Tn(e, t) {
|
|
1733
|
+
return wn.some((t) => t.value === e) ? w(t, `reports.reason.${e}`) : e || w(t, "reports.fallbackLabel");
|
|
1691
1734
|
}
|
|
1692
|
-
function
|
|
1693
|
-
let r = () =>
|
|
1694
|
-
|
|
1735
|
+
function En(e, n) {
|
|
1736
|
+
let r = () => M(), i = Q(), a = be(), { t: o } = O(), s = I(!1), c = I(!1), l;
|
|
1737
|
+
R(a, (e) => {
|
|
1695
1738
|
if (l?.(), l = void 0, !e) {
|
|
1696
1739
|
s.value = !1;
|
|
1697
1740
|
return;
|
|
1698
1741
|
}
|
|
1699
|
-
l =
|
|
1742
|
+
l = W(t(i, r(), n, e.uid), (e) => {
|
|
1700
1743
|
s.value = e.exists();
|
|
1701
1744
|
}, (e) => {
|
|
1702
1745
|
console.error("[memi-board:reports] onSnapshot failed", e);
|
|
1703
1746
|
});
|
|
1704
|
-
}, { immediate: !0 }),
|
|
1747
|
+
}, { immediate: !0 }), F(() => l?.());
|
|
1705
1748
|
async function u(l) {
|
|
1706
1749
|
let u = a.value?.uid;
|
|
1707
1750
|
if (!u) throw Error(o("reports.signInRequired"));
|
|
1708
1751
|
if (c.value) return;
|
|
1709
1752
|
if (l.authorUid && l.authorUid === u) throw Error(o("reports.cannotReportOwn"));
|
|
1710
|
-
let d =
|
|
1753
|
+
let d = wn.some((e) => e.value === l.reason) ? l.reason : null;
|
|
1711
1754
|
if (!d) throw Error(o("reports.reasonRequired"));
|
|
1712
1755
|
let f = (l.detail || "").trim();
|
|
1713
1756
|
if (f.length > 200) throw Error(o("reports.detailTooLong", { max: 200 }));
|
|
1714
1757
|
c.value = !0;
|
|
1715
1758
|
try {
|
|
1716
|
-
await
|
|
1759
|
+
await J(t(i, r(), n, u), {
|
|
1717
1760
|
uid: u,
|
|
1718
1761
|
target: "post",
|
|
1719
1762
|
postId: n,
|
|
@@ -1724,7 +1767,7 @@ function bn(e, n) {
|
|
|
1724
1767
|
postTitle: (l.postTitle || "").trim().slice(0, 200),
|
|
1725
1768
|
authorUid: l.authorUid || "",
|
|
1726
1769
|
authorName: l.authorName ?? null,
|
|
1727
|
-
createdAt:
|
|
1770
|
+
createdAt: q()
|
|
1728
1771
|
}), s.value = !0;
|
|
1729
1772
|
} finally {
|
|
1730
1773
|
c.value = !1;
|
|
@@ -1736,64 +1779,64 @@ function bn(e, n) {
|
|
|
1736
1779
|
submitReport: u
|
|
1737
1780
|
};
|
|
1738
1781
|
}
|
|
1739
|
-
var
|
|
1740
|
-
function
|
|
1741
|
-
let n = () =>
|
|
1742
|
-
function
|
|
1782
|
+
var Dn = 20;
|
|
1783
|
+
function On(e = {}) {
|
|
1784
|
+
let n = () => M(), r = Q(), { isAdmin: i, canManageContent: a, canManageBoard: o } = ht(), c = e.pageSize ?? Dn, l = P(() => L(e.boardId)?.trim() || ""), u = I([]), d = I(!0), f = I(!1), p = I(!1), m = I(""), h = null, g;
|
|
1785
|
+
function _(e) {
|
|
1743
1786
|
return {
|
|
1744
1787
|
id: e.id,
|
|
1745
1788
|
...e.data()
|
|
1746
1789
|
};
|
|
1747
1790
|
}
|
|
1748
|
-
function
|
|
1749
|
-
if (
|
|
1750
|
-
|
|
1791
|
+
function v() {
|
|
1792
|
+
if (g?.(), !a.value || !i.value && !l.value) {
|
|
1793
|
+
u.value = [], d.value = !1;
|
|
1751
1794
|
return;
|
|
1752
1795
|
}
|
|
1753
|
-
|
|
1754
|
-
let e =
|
|
1755
|
-
|
|
1756
|
-
|
|
1796
|
+
d.value = !0;
|
|
1797
|
+
let e = l.value ? [Z("status", "==", "open"), Z("boardId", "==", l.value)] : [Z("status", "==", "open"), Z("target", "==", "post")];
|
|
1798
|
+
g = W(K(s(r, n()), ...e, G("createdAt", "desc"), G(z(), "desc"), U(c)), (e) => {
|
|
1799
|
+
u.value = e.docs.map(_).filter((e) => o(e.boardId)), h = e.docs.at(-1) ?? null, p.value = e.docs.length >= c, d.value = !1;
|
|
1757
1800
|
}, (e) => {
|
|
1758
|
-
console.error("[memi-board:report-queue] onSnapshot failed", e),
|
|
1801
|
+
console.error("[memi-board:report-queue] onSnapshot failed", e), m.value = e.message || String(e), d.value = !1;
|
|
1759
1802
|
});
|
|
1760
1803
|
}
|
|
1761
|
-
|
|
1762
|
-
async function
|
|
1763
|
-
if (!
|
|
1764
|
-
|
|
1804
|
+
R([a, l], v, { immediate: !0 }), F(() => g?.());
|
|
1805
|
+
async function y() {
|
|
1806
|
+
if (!f.value && p.value && h && a.value) {
|
|
1807
|
+
f.value = !0, m.value = "";
|
|
1765
1808
|
try {
|
|
1766
|
-
let e =
|
|
1767
|
-
|
|
1809
|
+
let e = l.value ? [Z("status", "==", "open"), Z("boardId", "==", l.value)] : [Z("status", "==", "open"), Z("target", "==", "post")], t = await V(K(s(r, n()), ...e, G("createdAt", "desc"), G(z(), "desc"), Y(h), U(c + 1))), i = t.docs.slice(0, c), a = new Set(u.value.map((e) => e.id));
|
|
1810
|
+
u.value.push(...i.map(_).filter((e) => !a.has(e.id) && o(e.boardId))), h = i.at(-1) ?? h, p.value = t.docs.length > c;
|
|
1768
1811
|
} catch (e) {
|
|
1769
|
-
|
|
1812
|
+
m.value = e.message || String(e);
|
|
1770
1813
|
} finally {
|
|
1771
|
-
|
|
1814
|
+
f.value = !1;
|
|
1772
1815
|
}
|
|
1773
1816
|
}
|
|
1774
1817
|
}
|
|
1775
|
-
async function
|
|
1776
|
-
e.id &&
|
|
1818
|
+
async function b(e, i) {
|
|
1819
|
+
e.id && o(e.boardId) && await X(t(r, n(), e.postId, e.uid), {
|
|
1777
1820
|
status: i,
|
|
1778
|
-
reviewedAt:
|
|
1821
|
+
reviewedAt: q(),
|
|
1779
1822
|
reviewedBy: be().value?.uid || ""
|
|
1780
1823
|
});
|
|
1781
1824
|
}
|
|
1782
1825
|
return {
|
|
1783
|
-
reports:
|
|
1784
|
-
pending:
|
|
1785
|
-
loadingMore:
|
|
1786
|
-
hasMore:
|
|
1787
|
-
loadError:
|
|
1788
|
-
loadMore:
|
|
1789
|
-
dismissReport: (e) =>
|
|
1790
|
-
actionReport: (e) =>
|
|
1826
|
+
reports: u,
|
|
1827
|
+
pending: d,
|
|
1828
|
+
loadingMore: f,
|
|
1829
|
+
hasMore: p,
|
|
1830
|
+
loadError: m,
|
|
1831
|
+
loadMore: y,
|
|
1832
|
+
dismissReport: (e) => b(e, "dismissed"),
|
|
1833
|
+
actionReport: (e) => b(e, "actioned")
|
|
1791
1834
|
};
|
|
1792
1835
|
}
|
|
1793
1836
|
//#endregion
|
|
1794
1837
|
//#region src/composables/useMemiBoardViews.ts
|
|
1795
|
-
function
|
|
1796
|
-
let e = () =>
|
|
1838
|
+
function kn() {
|
|
1839
|
+
let e = () => M(), t = Q();
|
|
1797
1840
|
function n(t, n) {
|
|
1798
1841
|
let { postsCollection: r } = e();
|
|
1799
1842
|
return `memi-board:viewed:${r}:${t}:${n}`;
|
|
@@ -1816,7 +1859,7 @@ function Cn() {
|
|
|
1816
1859
|
if (s && c && !r(s, c)) {
|
|
1817
1860
|
i(s, c);
|
|
1818
1861
|
try {
|
|
1819
|
-
await
|
|
1862
|
+
await X(l(t, e(), c), { viewCount: H(1) });
|
|
1820
1863
|
} catch (e) {
|
|
1821
1864
|
try {
|
|
1822
1865
|
sessionStorage.removeItem(n(s, c));
|
|
@@ -1832,7 +1875,7 @@ function Cn() {
|
|
|
1832
1875
|
}
|
|
1833
1876
|
//#endregion
|
|
1834
1877
|
//#region src/utils/moderation-prompt.ts
|
|
1835
|
-
var
|
|
1878
|
+
var An = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", jn = {
|
|
1836
1879
|
ko: "Korean",
|
|
1837
1880
|
en: "English",
|
|
1838
1881
|
ja: "Japanese",
|
|
@@ -1844,12 +1887,12 @@ var wn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
1844
1887
|
ar: "Arabic",
|
|
1845
1888
|
id: "Indonesian"
|
|
1846
1889
|
};
|
|
1847
|
-
function
|
|
1848
|
-
let t =
|
|
1890
|
+
function Mn(e) {
|
|
1891
|
+
let t = jn[e] ?? "English", n = D(e).name;
|
|
1849
1892
|
return t === n ? t : `${t} (${n})`;
|
|
1850
1893
|
}
|
|
1851
|
-
function
|
|
1852
|
-
let t =
|
|
1894
|
+
function Nn(e) {
|
|
1895
|
+
let t = Mn(e);
|
|
1853
1896
|
return `You are a strict moderator reviewing content for a message board.
|
|
1854
1897
|
If the text below contains profanity, vulgar slang, sexual harassment, hate speech, threats, spam, or repetitive/flooding advertisements, you MUST reject it (flagged=true).
|
|
1855
1898
|
|
|
@@ -1876,17 +1919,17 @@ Output JSON only:
|
|
|
1876
1919
|
Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
|
|
1877
1920
|
- If flagged=false, category is "none" and reason is ""`;
|
|
1878
1921
|
}
|
|
1879
|
-
function
|
|
1880
|
-
return e === "ko" ?
|
|
1922
|
+
function Pn(e = "ko") {
|
|
1923
|
+
return e === "ko" ? An : Nn(e);
|
|
1881
1924
|
}
|
|
1882
|
-
function
|
|
1925
|
+
function Fn(e, t = "ko") {
|
|
1883
1926
|
let n = `"""${e.slice(0, 4e3)}"""`;
|
|
1884
1927
|
return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
|
|
1885
1928
|
}
|
|
1886
|
-
function
|
|
1929
|
+
function In(e = "ko") {
|
|
1887
1930
|
return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
|
|
1888
1931
|
}
|
|
1889
|
-
var
|
|
1932
|
+
var Ln = {
|
|
1890
1933
|
abuse: "욕설·비속어·모욕",
|
|
1891
1934
|
spam: "스팸·광고",
|
|
1892
1935
|
adult: "선정적·성인 내용",
|
|
@@ -1894,10 +1937,10 @@ var jn = {
|
|
|
1894
1937
|
other: "부적절한 내용",
|
|
1895
1938
|
none: ""
|
|
1896
1939
|
};
|
|
1897
|
-
function
|
|
1898
|
-
return !e || e === "none" ? "" : e in
|
|
1940
|
+
function Rn(e, t = "ko") {
|
|
1941
|
+
return !e || e === "none" ? "" : e in Ln ? w(t, `moderationPrompt.categories.${e}`) : e;
|
|
1899
1942
|
}
|
|
1900
|
-
function
|
|
1943
|
+
function zn(e) {
|
|
1901
1944
|
let t = e.locale ?? "ko";
|
|
1902
1945
|
if (e.via === "local" && e.localHit) {
|
|
1903
1946
|
let n = e.localHit.trim();
|
|
@@ -1905,10 +1948,10 @@ function Nn(e) {
|
|
|
1905
1948
|
}
|
|
1906
1949
|
let n = (e.reason || "").trim();
|
|
1907
1950
|
if (n) return n;
|
|
1908
|
-
let r = e.category ?
|
|
1951
|
+
let r = e.category ? Rn(e.category, t) : "";
|
|
1909
1952
|
return r ? w(t, "moderationPrompt.categoryBlocked", { category: r }) : w(t, "moderationPrompt.genericBlocked");
|
|
1910
1953
|
}
|
|
1911
|
-
var
|
|
1954
|
+
var Bn = [
|
|
1912
1955
|
"씨발",
|
|
1913
1956
|
"시발",
|
|
1914
1957
|
"시이발",
|
|
@@ -1934,10 +1977,10 @@ var Pn = [
|
|
|
1934
1977
|
"bitch",
|
|
1935
1978
|
"asshole"
|
|
1936
1979
|
];
|
|
1937
|
-
function
|
|
1980
|
+
function Vn(e) {
|
|
1938
1981
|
return new RegExp(e.join("|"), "i");
|
|
1939
1982
|
}
|
|
1940
|
-
var
|
|
1983
|
+
var Hn = /* @__PURE__ */ new Set([
|
|
1941
1984
|
"none",
|
|
1942
1985
|
"abuse",
|
|
1943
1986
|
"spam",
|
|
@@ -1945,7 +1988,7 @@ var In = /* @__PURE__ */ new Set([
|
|
|
1945
1988
|
"violence",
|
|
1946
1989
|
"other"
|
|
1947
1990
|
]);
|
|
1948
|
-
function
|
|
1991
|
+
function Un(e) {
|
|
1949
1992
|
let t = e.trim();
|
|
1950
1993
|
if (!t) return null;
|
|
1951
1994
|
let n;
|
|
@@ -1963,7 +2006,7 @@ function Ln(e) {
|
|
|
1963
2006
|
if (typeof n != "object" || !n) return null;
|
|
1964
2007
|
let r = n;
|
|
1965
2008
|
if (typeof r.flagged != "boolean") return null;
|
|
1966
|
-
let i = typeof r.category == "string" &&
|
|
2009
|
+
let i = typeof r.category == "string" && Hn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
1967
2010
|
return {
|
|
1968
2011
|
flagged: r.flagged,
|
|
1969
2012
|
category: i,
|
|
@@ -1972,20 +2015,20 @@ function Ln(e) {
|
|
|
1972
2015
|
}
|
|
1973
2016
|
//#endregion
|
|
1974
2017
|
//#region src/composables/useMemiBoardModeration.ts
|
|
1975
|
-
var
|
|
2018
|
+
var Wn = {
|
|
1976
2019
|
flagged: !1,
|
|
1977
2020
|
category: "none",
|
|
1978
2021
|
reason: "",
|
|
1979
2022
|
via: "empty"
|
|
1980
2023
|
};
|
|
1981
|
-
function
|
|
1982
|
-
let t = O(), n =
|
|
2024
|
+
function Gn(e = {}) {
|
|
2025
|
+
let t = O(), n = P(() => E(L(e.locale)) ?? t.locale.value), r = le().moderation ?? {}, i = Se(), a = (e) => w(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = ht(), l = Vn([...Bn, ...r.localBlocklist ?? []]);
|
|
1983
2026
|
function u(e) {
|
|
1984
2027
|
let t = e.match(l);
|
|
1985
2028
|
return t ? {
|
|
1986
2029
|
flagged: !0,
|
|
1987
2030
|
category: "abuse",
|
|
1988
|
-
reason:
|
|
2031
|
+
reason: zn({
|
|
1989
2032
|
via: "local",
|
|
1990
2033
|
localHit: t[0] || "",
|
|
1991
2034
|
category: "abuse"
|
|
@@ -1994,21 +2037,21 @@ function zn(e = {}) {
|
|
|
1994
2037
|
} : null;
|
|
1995
2038
|
}
|
|
1996
2039
|
function d() {
|
|
1997
|
-
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a =
|
|
2040
|
+
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Le(i, {
|
|
1998
2041
|
backend: new Fe(),
|
|
1999
2042
|
useLimitedUseAppCheckTokens: t
|
|
2000
2043
|
});
|
|
2001
|
-
return
|
|
2044
|
+
return Re(a, {
|
|
2002
2045
|
model: e,
|
|
2003
|
-
systemInstruction:
|
|
2046
|
+
systemInstruction: Pn(n.value),
|
|
2004
2047
|
generationConfig: {
|
|
2005
2048
|
temperature: 0,
|
|
2006
2049
|
maxOutputTokens: 256,
|
|
2007
2050
|
responseMimeType: "application/json",
|
|
2008
|
-
responseSchema:
|
|
2009
|
-
flagged:
|
|
2010
|
-
category:
|
|
2011
|
-
reason:
|
|
2051
|
+
responseSchema: Ie.object({ properties: {
|
|
2052
|
+
flagged: Ie.boolean(),
|
|
2053
|
+
category: Ie.string(),
|
|
2054
|
+
reason: Ie.string()
|
|
2012
2055
|
} })
|
|
2013
2056
|
}
|
|
2014
2057
|
});
|
|
@@ -2029,7 +2072,7 @@ function zn(e = {}) {
|
|
|
2029
2072
|
};
|
|
2030
2073
|
}
|
|
2031
2074
|
function p(e, t = "") {
|
|
2032
|
-
let n =
|
|
2075
|
+
let n = zn({
|
|
2033
2076
|
reason: e.reason,
|
|
2034
2077
|
category: e.category,
|
|
2035
2078
|
via: e.via
|
|
@@ -2057,26 +2100,26 @@ function zn(e = {}) {
|
|
|
2057
2100
|
};
|
|
2058
2101
|
let i = e.trim();
|
|
2059
2102
|
if (!i) return {
|
|
2060
|
-
...
|
|
2103
|
+
...Wn,
|
|
2061
2104
|
via: "empty"
|
|
2062
2105
|
};
|
|
2063
2106
|
if (t?.skipFilter) return {
|
|
2064
|
-
...
|
|
2107
|
+
...Wn,
|
|
2065
2108
|
via: "admin-override"
|
|
2066
2109
|
};
|
|
2067
2110
|
let c = u(i);
|
|
2068
2111
|
if (c) return m(c);
|
|
2069
2112
|
if (r.enabled === !1) return {
|
|
2070
|
-
...
|
|
2113
|
+
...Wn,
|
|
2071
2114
|
via: "disabled"
|
|
2072
2115
|
};
|
|
2073
2116
|
try {
|
|
2074
|
-
let e =
|
|
2117
|
+
let e = Un((await d().generateContent(Fn(i, n.value))).response.text());
|
|
2075
2118
|
if (!e) return f();
|
|
2076
2119
|
let t = {
|
|
2077
2120
|
flagged: e.flagged,
|
|
2078
2121
|
category: e.category,
|
|
2079
|
-
reason:
|
|
2122
|
+
reason: zn({
|
|
2080
2123
|
reason: e.reason,
|
|
2081
2124
|
category: e.category,
|
|
2082
2125
|
via: "ai"
|
|
@@ -2096,22 +2139,22 @@ function zn(e = {}) {
|
|
|
2096
2139
|
via: "restricted"
|
|
2097
2140
|
};
|
|
2098
2141
|
if (r.enabled === !1 || !r.moderateImages) return {
|
|
2099
|
-
...
|
|
2142
|
+
...Wn,
|
|
2100
2143
|
via: "disabled"
|
|
2101
2144
|
};
|
|
2102
2145
|
try {
|
|
2103
2146
|
let t = await new Promise((t, n) => {
|
|
2104
2147
|
let r = new FileReader();
|
|
2105
2148
|
r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
|
|
2106
|
-
}), r =
|
|
2149
|
+
}), r = Un((await d().generateContent([{ inlineData: {
|
|
2107
2150
|
mimeType: e.type || "image/jpeg",
|
|
2108
2151
|
data: t
|
|
2109
|
-
} },
|
|
2152
|
+
} }, In(n.value)])).response.text());
|
|
2110
2153
|
if (!r) return f();
|
|
2111
2154
|
let i = {
|
|
2112
2155
|
flagged: r.flagged,
|
|
2113
2156
|
category: r.category,
|
|
2114
|
-
reason:
|
|
2157
|
+
reason: zn({
|
|
2115
2158
|
reason: r.reason,
|
|
2116
2159
|
category: r.category,
|
|
2117
2160
|
via: "ai"
|
|
@@ -2130,8 +2173,10 @@ function zn(e = {}) {
|
|
|
2130
2173
|
}
|
|
2131
2174
|
//#endregion
|
|
2132
2175
|
//#region src/composables/useMemiBoardUsers.ts
|
|
2133
|
-
var
|
|
2176
|
+
var Kn = [
|
|
2177
|
+
"owner",
|
|
2134
2178
|
"admin",
|
|
2179
|
+
"manager",
|
|
2135
2180
|
"staff",
|
|
2136
2181
|
"user"
|
|
2137
2182
|
].map((e) => ({
|
|
@@ -2139,13 +2184,13 @@ var Bn = [
|
|
|
2139
2184
|
label: w("ko", `common.role.${e}`),
|
|
2140
2185
|
description: w("ko", `boardUsers.roleDescription.${e}`)
|
|
2141
2186
|
}));
|
|
2142
|
-
function
|
|
2143
|
-
let n = () =>
|
|
2187
|
+
function qn(t = {}) {
|
|
2188
|
+
let n = () => M(), i = Q(), { t: a } = O(), o = P(() => L(t.enabled) ?? !0 ? e(i, n()) : null), s = ye(o, { ssrKey: r(n(), "users-management") });
|
|
2144
2189
|
async function c(e, t) {
|
|
2145
|
-
if (!
|
|
2146
|
-
await
|
|
2190
|
+
if (!Kn.some((e) => e.value === t)) throw Error(a("boardUsers.unsupportedRole"));
|
|
2191
|
+
await X(f(i, n(), e), {
|
|
2147
2192
|
role: t,
|
|
2148
|
-
updatedAt:
|
|
2193
|
+
updatedAt: q()
|
|
2149
2194
|
});
|
|
2150
2195
|
}
|
|
2151
2196
|
return {
|
|
@@ -2156,8 +2201,8 @@ function Vn(t = {}) {
|
|
|
2156
2201
|
}
|
|
2157
2202
|
//#endregion
|
|
2158
2203
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2159
|
-
function
|
|
2160
|
-
let t =
|
|
2204
|
+
function Jn(e) {
|
|
2205
|
+
let t = Q(), n = () => M(), r = P(() => L(e)), i = P(() => {
|
|
2161
2206
|
let e = r.value;
|
|
2162
2207
|
return e ? f(t, n(), e) : null;
|
|
2163
2208
|
}), { data: a, pending: o } = xe(i);
|
|
@@ -2168,7 +2213,7 @@ function Hn(e) {
|
|
|
2168
2213
|
}
|
|
2169
2214
|
//#endregion
|
|
2170
2215
|
//#region src/utils/section.ts
|
|
2171
|
-
var
|
|
2216
|
+
var Yn = [
|
|
2172
2217
|
3,
|
|
2173
2218
|
4,
|
|
2174
2219
|
5,
|
|
@@ -2178,7 +2223,7 @@ var Un = [
|
|
|
2178
2223
|
9,
|
|
2179
2224
|
12
|
|
2180
2225
|
];
|
|
2181
|
-
function
|
|
2226
|
+
function Xn(e) {
|
|
2182
2227
|
return [
|
|
2183
2228
|
{
|
|
2184
2229
|
value: "list",
|
|
@@ -2194,7 +2239,7 @@ function Wn(e) {
|
|
|
2194
2239
|
}
|
|
2195
2240
|
];
|
|
2196
2241
|
}
|
|
2197
|
-
function
|
|
2242
|
+
function Zn(e) {
|
|
2198
2243
|
return [{
|
|
2199
2244
|
value: "latest",
|
|
2200
2245
|
label: w(e, "sectionUtil.sort.latest")
|
|
@@ -2203,18 +2248,18 @@ function Gn(e) {
|
|
|
2203
2248
|
label: w(e, "sectionUtil.sort.likes")
|
|
2204
2249
|
}];
|
|
2205
2250
|
}
|
|
2206
|
-
var
|
|
2207
|
-
function
|
|
2251
|
+
var Qn = Xn("ko"), $n = Zn("ko");
|
|
2252
|
+
function er(e) {
|
|
2208
2253
|
return e === "list" ? 5 : 4;
|
|
2209
2254
|
}
|
|
2210
|
-
function
|
|
2255
|
+
function tr(e, t) {
|
|
2211
2256
|
let n = Math.floor(Number(t));
|
|
2212
|
-
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) :
|
|
2257
|
+
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : er(e);
|
|
2213
2258
|
}
|
|
2214
|
-
function
|
|
2259
|
+
function nr(e) {
|
|
2215
2260
|
return e === 12 ? null : 12 - e;
|
|
2216
2261
|
}
|
|
2217
|
-
function
|
|
2262
|
+
function rr(e) {
|
|
2218
2263
|
return {
|
|
2219
2264
|
3: "col-span-12 md:col-span-3",
|
|
2220
2265
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2226,36 +2271,36 @@ function Zn(e) {
|
|
|
2226
2271
|
12: "col-span-12"
|
|
2227
2272
|
}[e];
|
|
2228
2273
|
}
|
|
2229
|
-
function
|
|
2274
|
+
function ir(e) {
|
|
2230
2275
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2231
2276
|
}
|
|
2232
|
-
function
|
|
2277
|
+
function ar(e) {
|
|
2233
2278
|
let t = e.boardId || e.category || "free";
|
|
2234
2279
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2235
2280
|
}
|
|
2236
|
-
function
|
|
2281
|
+
function or(e) {
|
|
2237
2282
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2238
2283
|
}
|
|
2239
|
-
function
|
|
2284
|
+
function sr(e) {
|
|
2240
2285
|
if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2241
2286
|
}
|
|
2242
|
-
function
|
|
2287
|
+
function cr(e) {
|
|
2243
2288
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2244
2289
|
}
|
|
2245
|
-
function
|
|
2290
|
+
function lr(e, t) {
|
|
2246
2291
|
return e.title?.trim() || e.summary || w(t, "sectionUtil.untitledPost");
|
|
2247
2292
|
}
|
|
2248
|
-
function
|
|
2293
|
+
function ur(e) {
|
|
2249
2294
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2250
2295
|
}
|
|
2251
|
-
function
|
|
2252
|
-
return w(t,
|
|
2296
|
+
function dr(e, t) {
|
|
2297
|
+
return w(t, ur(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
|
|
2253
2298
|
}
|
|
2254
|
-
function
|
|
2299
|
+
function fr(e, t) {
|
|
2255
2300
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2256
2301
|
}
|
|
2257
|
-
function
|
|
2258
|
-
let n = e.map((e) =>
|
|
2302
|
+
function pr(e, t) {
|
|
2303
|
+
let n = e.map((e) => fr(e.boardId, t));
|
|
2259
2304
|
if (!n.length) return "default";
|
|
2260
2305
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2261
2306
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2263,27 +2308,27 @@ function sr(e, t) {
|
|
|
2263
2308
|
}
|
|
2264
2309
|
//#endregion
|
|
2265
2310
|
//#region src/composables/useMemiBoardSections.ts
|
|
2266
|
-
var
|
|
2267
|
-
function
|
|
2311
|
+
var mr = [];
|
|
2312
|
+
function hr(e) {
|
|
2268
2313
|
let t = Number(e.cols);
|
|
2269
|
-
if (
|
|
2314
|
+
if (Yn.includes(t)) return t;
|
|
2270
2315
|
let n = e.width;
|
|
2271
2316
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2272
2317
|
}
|
|
2273
|
-
function
|
|
2318
|
+
function gr(e) {
|
|
2274
2319
|
let t = e.height ?? e.size;
|
|
2275
2320
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2276
2321
|
}
|
|
2277
|
-
function
|
|
2322
|
+
function _r(e) {
|
|
2278
2323
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2279
2324
|
}
|
|
2280
|
-
function
|
|
2325
|
+
function vr(e) {
|
|
2281
2326
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2282
2327
|
}
|
|
2283
|
-
function
|
|
2328
|
+
function yr(e) {
|
|
2284
2329
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2285
2330
|
}
|
|
2286
|
-
function
|
|
2331
|
+
function br(e, t) {
|
|
2287
2332
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2288
2333
|
for (let e of n) {
|
|
2289
2334
|
let n = String(e || "").trim();
|
|
@@ -2291,47 +2336,47 @@ function mr(e, t) {
|
|
|
2291
2336
|
}
|
|
2292
2337
|
return r;
|
|
2293
2338
|
}
|
|
2294
|
-
function
|
|
2339
|
+
function xr(e, t) {
|
|
2295
2340
|
return w(t, "sections.defaultTitle", { n: e + 1 });
|
|
2296
2341
|
}
|
|
2297
|
-
function
|
|
2298
|
-
let r =
|
|
2342
|
+
function Sr(e, t, n) {
|
|
2343
|
+
let r = _r(e), i = tr(r, e.count);
|
|
2299
2344
|
return {
|
|
2300
2345
|
id: String(e.id || `box-${t + 1}`),
|
|
2301
|
-
title: String(e.title ||
|
|
2346
|
+
title: String(e.title || xr(t, n)),
|
|
2302
2347
|
showTitle: e.showTitle !== !1,
|
|
2303
2348
|
kind: r,
|
|
2304
|
-
boardId:
|
|
2349
|
+
boardId: yr(e),
|
|
2305
2350
|
count: i,
|
|
2306
|
-
postIds:
|
|
2307
|
-
sort:
|
|
2308
|
-
cols:
|
|
2309
|
-
height:
|
|
2351
|
+
postIds: br(e, i),
|
|
2352
|
+
sort: vr(e),
|
|
2353
|
+
cols: hr(e),
|
|
2354
|
+
height: gr(e),
|
|
2310
2355
|
order: t
|
|
2311
2356
|
};
|
|
2312
2357
|
}
|
|
2313
|
-
function
|
|
2314
|
-
let { locale: e } = O(), t =
|
|
2358
|
+
function Cr() {
|
|
2359
|
+
let { locale: e } = O(), t = Q(), n = pe(t, "settings", "homeLayout"), { data: r, pending: i } = xe(n), a = P(() => (Array.isArray(r.value?.blocks) ? r.value.blocks : mr).map((t, n) => Sr(t, n, e.value)));
|
|
2315
2360
|
async function o(t) {
|
|
2316
|
-
await
|
|
2361
|
+
await J(n, {
|
|
2317
2362
|
columns: 12,
|
|
2318
2363
|
blocks: t.map((t, n) => {
|
|
2319
|
-
let r = t, i =
|
|
2364
|
+
let r = t, i = _r(r), a = tr(i, t.count);
|
|
2320
2365
|
return {
|
|
2321
2366
|
id: t.id,
|
|
2322
|
-
title: t.title.trim() ||
|
|
2367
|
+
title: t.title.trim() || xr(n, e.value),
|
|
2323
2368
|
showTitle: t.showTitle !== !1,
|
|
2324
2369
|
kind: i,
|
|
2325
|
-
boardId:
|
|
2370
|
+
boardId: yr(r),
|
|
2326
2371
|
count: a,
|
|
2327
|
-
postIds:
|
|
2328
|
-
sort:
|
|
2329
|
-
cols:
|
|
2330
|
-
height:
|
|
2372
|
+
postIds: br(r, a),
|
|
2373
|
+
sort: vr(r),
|
|
2374
|
+
cols: hr(r),
|
|
2375
|
+
height: gr(r),
|
|
2331
2376
|
order: n
|
|
2332
2377
|
};
|
|
2333
2378
|
}),
|
|
2334
|
-
updatedAt:
|
|
2379
|
+
updatedAt: q()
|
|
2335
2380
|
});
|
|
2336
2381
|
}
|
|
2337
2382
|
return {
|
|
@@ -2339,28 +2384,28 @@ function _r() {
|
|
|
2339
2384
|
pending: i,
|
|
2340
2385
|
sections: a,
|
|
2341
2386
|
blocks: a,
|
|
2342
|
-
hasSavedLayout:
|
|
2387
|
+
hasSavedLayout: P(() => Array.isArray(r.value?.blocks)),
|
|
2343
2388
|
saveSections: o,
|
|
2344
2389
|
saveBlocks: o
|
|
2345
2390
|
};
|
|
2346
2391
|
}
|
|
2347
2392
|
//#endregion
|
|
2348
2393
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2349
|
-
function
|
|
2350
|
-
let n =
|
|
2351
|
-
let r = Math.max(1, Math.floor(Number(
|
|
2352
|
-
return a && o.unshift(
|
|
2394
|
+
function wr(e, t) {
|
|
2395
|
+
let n = Q(), i = () => M(), a = L(e)?.trim() || "all", o = P(() => {
|
|
2396
|
+
let r = Math.max(1, Math.floor(Number(L(t)) || 5)), a = L(e)?.trim() || "", o = [Z("isPublished", "==", !0), Z("listed", "==", !0)];
|
|
2397
|
+
return a && o.unshift(Z("boardId", "==", a)), K(N(n, i()), ...o, G("createdAt", "desc"), U(r));
|
|
2353
2398
|
}), { data: s, pending: c, error: l } = ye(o, { ssrKey: r(i(), `mini-list/${a}`) });
|
|
2354
2399
|
return {
|
|
2355
|
-
posts:
|
|
2400
|
+
posts: P(() => s.value ?? []),
|
|
2356
2401
|
pending: c,
|
|
2357
2402
|
error: l
|
|
2358
2403
|
};
|
|
2359
2404
|
}
|
|
2360
|
-
function
|
|
2361
|
-
let t =
|
|
2405
|
+
function Tr(e) {
|
|
2406
|
+
let t = Q(), n = () => M(), i = P(() => [...new Set(L(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), a = i.value.join(",") || "none", o = P(() => i.value.length ? K(N(t, n()), Z(z(), "in", i.value)) : null), { data: s, pending: c, error: l } = ye(o, { ssrKey: r(n(), `mini-picked/${a}`) });
|
|
2362
2407
|
return {
|
|
2363
|
-
posts:
|
|
2408
|
+
posts: P(() => {
|
|
2364
2409
|
let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
|
|
2365
2410
|
for (let n of i.value) {
|
|
2366
2411
|
let r = e.get(n);
|
|
@@ -2374,7 +2419,25 @@ function yr(e) {
|
|
|
2374
2419
|
}
|
|
2375
2420
|
//#endregion
|
|
2376
2421
|
//#region src/data/versionHistory.ts
|
|
2377
|
-
var
|
|
2422
|
+
var Er = [
|
|
2423
|
+
{
|
|
2424
|
+
version: "0.46.0",
|
|
2425
|
+
date: "2026-10-21",
|
|
2426
|
+
highlights: [
|
|
2427
|
+
"일반 사용자·스태프·매니저·관리자·최고 관리자, 다섯 가지 역할을 지원해요",
|
|
2428
|
+
"게시판별 매니저가 설정과 스태프 배정을 관리할 수 있어요",
|
|
2429
|
+
"서비스에 맞게 역할 이름을 바꿀 수 있어요"
|
|
2430
|
+
]
|
|
2431
|
+
},
|
|
2432
|
+
{
|
|
2433
|
+
version: "0.45.1",
|
|
2434
|
+
date: "2026-10-15",
|
|
2435
|
+
highlights: [
|
|
2436
|
+
"일반 계정 글쓰기·초안 조회와 태그 입력을 고쳤어요",
|
|
2437
|
+
"댓글·답글 수정과 좋아요가 다른 사용자에게 바로 반영돼요",
|
|
2438
|
+
"글 삭제·첨부파일 보호·게시판 공개 범위 변경을 보강했어요"
|
|
2439
|
+
]
|
|
2440
|
+
},
|
|
2378
2441
|
{
|
|
2379
2442
|
version: "0.45.0",
|
|
2380
2443
|
date: "2026-10-01",
|
|
@@ -2659,7 +2722,7 @@ var br = [
|
|
|
2659
2722
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
2660
2723
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
2661
2724
|
}
|
|
2662
|
-
],
|
|
2725
|
+
], Dr = 10, Or = 864e5, kr = 200, Ar = {
|
|
2663
2726
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
2664
2727
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
2665
2728
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -2668,7 +2731,7 @@ var br = [
|
|
|
2668
2731
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
2669
2732
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
2670
2733
|
};
|
|
2671
|
-
function
|
|
2734
|
+
function jr(e) {
|
|
2672
2735
|
return {
|
|
2673
2736
|
proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
|
|
2674
2737
|
polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
|
|
@@ -2679,34 +2742,34 @@ function Tr(e) {
|
|
|
2679
2742
|
title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
|
|
2680
2743
|
};
|
|
2681
2744
|
}
|
|
2682
|
-
function
|
|
2683
|
-
let r = t === "ko", i =
|
|
2745
|
+
function Mr(e, t, n) {
|
|
2746
|
+
let r = t === "ko", i = Mn(t);
|
|
2684
2747
|
if (e.action !== "custom") {
|
|
2685
|
-
if (r) return
|
|
2686
|
-
let t =
|
|
2748
|
+
if (r) return Ar[e.action];
|
|
2749
|
+
let t = jr(i)[e.action];
|
|
2687
2750
|
return e.action === "translate-en" || e.action === "translate-ko" || e.action === "title" ? t : `${t} Write the result in the same language as the original text; if unclear, write in ${i}.`;
|
|
2688
2751
|
}
|
|
2689
|
-
let a = e.customInstruction?.trim().slice(0,
|
|
2752
|
+
let a = e.customInstruction?.trim().slice(0, kr);
|
|
2690
2753
|
if (!a) throw Error(n("writingAssistant.customRequired"));
|
|
2691
2754
|
return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
|
|
2692
2755
|
}
|
|
2693
|
-
function
|
|
2756
|
+
function Nr(e) {
|
|
2694
2757
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
2695
2758
|
}
|
|
2696
|
-
function
|
|
2759
|
+
function Pr(e) {
|
|
2697
2760
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
2698
2761
|
}
|
|
2699
|
-
function
|
|
2700
|
-
let t = O(), n =
|
|
2762
|
+
function Fr(e = {}) {
|
|
2763
|
+
let t = O(), n = P(() => E(L(e.locale)) ?? t.locale.value), r = (e, t) => w(n.value, e, t), i = Se(), a = Q(), o = be(), s = le();
|
|
2701
2764
|
async function c() {
|
|
2702
2765
|
let e = o.value?.uid;
|
|
2703
2766
|
if (!e) throw Error(r("writingAssistant.loginRequired"));
|
|
2704
|
-
await
|
|
2705
|
-
let n = f(a,
|
|
2767
|
+
await _e(a, async (t) => {
|
|
2768
|
+
let n = f(a, M(), e), i = await t.get(n);
|
|
2706
2769
|
if (!i.exists()) throw Error(r("writingAssistant.userMissing"));
|
|
2707
|
-
let o = i.data(), s =
|
|
2770
|
+
let o = i.data(), s = Pr(o.aiWritingWindowAt), c = Math.max(0, Math.floor(Number(o.aiWritingCount) || 0)), l = Date.now(), u = s != null && l - s < Or;
|
|
2708
2771
|
if (u && c >= 10) {
|
|
2709
|
-
let e = s +
|
|
2772
|
+
let e = s + Or, t = Math.max(1, Math.ceil((e - l) / 36e5));
|
|
2710
2773
|
throw Error(r("writingAssistant.dailyLimit", {
|
|
2711
2774
|
limit: 10,
|
|
2712
2775
|
hours: t
|
|
@@ -2714,19 +2777,19 @@ function kr(e = {}) {
|
|
|
2714
2777
|
}
|
|
2715
2778
|
t.update(n, {
|
|
2716
2779
|
aiWritingCount: u ? c + 1 : 1,
|
|
2717
|
-
aiWritingWindowAt: u ? o.aiWritingWindowAt :
|
|
2718
|
-
updatedAt:
|
|
2780
|
+
aiWritingWindowAt: u ? o.aiWritingWindowAt : q(),
|
|
2781
|
+
updatedAt: q()
|
|
2719
2782
|
});
|
|
2720
2783
|
});
|
|
2721
2784
|
}
|
|
2722
2785
|
async function l(e) {
|
|
2723
|
-
let t = n.value, a = t === "ko", o =
|
|
2786
|
+
let t = n.value, a = t === "ko", o = Mr(e, t, r);
|
|
2724
2787
|
if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
|
|
2725
2788
|
await c();
|
|
2726
|
-
let l = s.moderation ?? {}, u =
|
|
2789
|
+
let l = s.moderation ?? {}, u = Le(i, {
|
|
2727
2790
|
backend: new Fe(),
|
|
2728
2791
|
useLimitedUseAppCheckTokens: l.useLimitedUseAppCheckTokens !== !1
|
|
2729
|
-
}), d =
|
|
2792
|
+
}), d = Re(u, {
|
|
2730
2793
|
model: l.model ?? "gemini-3.5-flash-lite",
|
|
2731
2794
|
systemInstruction: (a ? [
|
|
2732
2795
|
"너는 게시판 글쓰기 편집 도우미다.",
|
|
@@ -2749,7 +2812,7 @@ function kr(e = {}) {
|
|
|
2749
2812
|
`Task: ${o}`,
|
|
2750
2813
|
e.title ? `Current title: ${e.title}` : "",
|
|
2751
2814
|
`Body:\n${f.slice(0, 12e3)}`
|
|
2752
|
-
]).filter(Boolean).join("\n\n"), m =
|
|
2815
|
+
]).filter(Boolean).join("\n\n"), m = Nr((await d.generateContent(p)).response.text());
|
|
2753
2816
|
if (!m) throw Error(r("writingAssistant.emptyResult"));
|
|
2754
2817
|
return m;
|
|
2755
2818
|
}
|
|
@@ -2757,8 +2820,8 @@ function kr(e = {}) {
|
|
|
2757
2820
|
}
|
|
2758
2821
|
//#endregion
|
|
2759
2822
|
//#region src/composables/useMemiBoardAutoTranslate.ts
|
|
2760
|
-
var
|
|
2761
|
-
function
|
|
2823
|
+
var Ir = 3e4;
|
|
2824
|
+
function Lr(e, t) {
|
|
2762
2825
|
return new Promise((n, r) => {
|
|
2763
2826
|
let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
|
|
2764
2827
|
e.then((e) => {
|
|
@@ -2768,14 +2831,14 @@ function jr(e, t) {
|
|
|
2768
2831
|
});
|
|
2769
2832
|
});
|
|
2770
2833
|
}
|
|
2771
|
-
function
|
|
2772
|
-
let e = Se(), { moderation: t } =
|
|
2834
|
+
function Rr() {
|
|
2835
|
+
let e = Se(), { moderation: t } = le();
|
|
2773
2836
|
function n(n, r) {
|
|
2774
|
-
let i =
|
|
2837
|
+
let i = Le(e, {
|
|
2775
2838
|
backend: new Fe(),
|
|
2776
2839
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
2777
|
-
}), a =
|
|
2778
|
-
return
|
|
2840
|
+
}), a = Ie.object({ properties: Object.fromEntries(r.map((e) => [e, Ie.string()])) });
|
|
2841
|
+
return Re(i, {
|
|
2779
2842
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
2780
2843
|
systemInstruction: [
|
|
2781
2844
|
"You translate short user-interface strings for a website.",
|
|
@@ -2787,19 +2850,19 @@ function Mr() {
|
|
|
2787
2850
|
temperature: .2,
|
|
2788
2851
|
maxOutputTokens: 2048,
|
|
2789
2852
|
responseMimeType: "application/json",
|
|
2790
|
-
responseSchema:
|
|
2853
|
+
responseSchema: Ie.object({ properties: Object.fromEntries(n.map((e) => [e, a])) })
|
|
2791
2854
|
}
|
|
2792
2855
|
});
|
|
2793
2856
|
}
|
|
2794
2857
|
async function r(e, t, r, i) {
|
|
2795
2858
|
let a = Object.keys(e.fields), o = [
|
|
2796
2859
|
`Purpose: ${i}`,
|
|
2797
|
-
`Source language: ${
|
|
2798
|
-
`Translate into: ${r.map((e) => `${e} = ${
|
|
2860
|
+
`Source language: ${Mn(t)} (${t})`,
|
|
2861
|
+
`Translate into: ${r.map((e) => `${e} = ${Mn(e)}`).join(", ")}`,
|
|
2799
2862
|
e.context ? `Context (do not translate): ${e.context}` : "",
|
|
2800
2863
|
"Text (JSON):",
|
|
2801
2864
|
JSON.stringify(e.fields)
|
|
2802
|
-
].filter(Boolean).join("\n"), s = await
|
|
2865
|
+
].filter(Boolean).join("\n"), s = await Lr(n(r, a).generateContent(o), Ir), c = JSON.parse(s.response.text()), l = {};
|
|
2803
2866
|
for (let e of r) {
|
|
2804
2867
|
let t = c?.[e];
|
|
2805
2868
|
if (!t) continue;
|
|
@@ -2813,7 +2876,7 @@ function Mr() {
|
|
|
2813
2876
|
return l;
|
|
2814
2877
|
}
|
|
2815
2878
|
async function i(e, t = {}) {
|
|
2816
|
-
let n = t.source ?? "ko", i = (t.targets ??
|
|
2879
|
+
let n = t.source ?? "ko", i = (t.targets ?? j.map((e) => e.code)).filter((e) => e !== n), a = t.purpose ?? "website UI labels", o = e.map((e) => ({
|
|
2817
2880
|
...e,
|
|
2818
2881
|
fields: Object.fromEntries(Object.entries(e.fields).map(([e, t]) => [e, t.trim()]).filter(([, e]) => e))
|
|
2819
2882
|
})).filter((e) => Object.keys(e.fields).length);
|
|
@@ -2823,20 +2886,20 @@ function Mr() {
|
|
|
2823
2886
|
}
|
|
2824
2887
|
return { translateEntries: i };
|
|
2825
2888
|
}
|
|
2826
|
-
function
|
|
2889
|
+
function zr(e, t, n, r) {
|
|
2827
2890
|
let i = E(t);
|
|
2828
2891
|
return !i || i === "ko" ? r : e?.[i]?.[n] || e?.en?.[n] || r;
|
|
2829
2892
|
}
|
|
2830
2893
|
//#endregion
|
|
2831
2894
|
//#region src/utils/renderMarkdown.ts
|
|
2832
|
-
function
|
|
2895
|
+
function Br(e) {
|
|
2833
2896
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2834
2897
|
}
|
|
2835
|
-
function
|
|
2898
|
+
function Vr(e) {
|
|
2836
2899
|
let t = e;
|
|
2837
2900
|
return t = t.replace(/`([^`]+)`/g, "<code>$1</code>"), t = t.replace(/!\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g, "<img src=\"$2\" alt=\"$1\" class=\"max-w-full h-auto rounded-lg my-3\" loading=\"lazy\" />"), t = t.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary underline\">$1</a>"), t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), t = t.replace(/\*([^*]+)\*/g, "<em>$1</em>"), t = t.replace(/~~([^~]+)~~/g, "<del>$1</del>"), t;
|
|
2838
2901
|
}
|
|
2839
|
-
function
|
|
2902
|
+
function Hr(e) {
|
|
2840
2903
|
if (!e?.trim()) return "";
|
|
2841
2904
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
2842
2905
|
function c() {
|
|
@@ -2845,7 +2908,7 @@ function Ir(e) {
|
|
|
2845
2908
|
return;
|
|
2846
2909
|
}
|
|
2847
2910
|
let e = o;
|
|
2848
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
2911
|
+
n.push(`<${e}>${s.map((e) => `<li>${Vr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
2849
2912
|
}
|
|
2850
2913
|
for (; r < t.length;) {
|
|
2851
2914
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -2854,7 +2917,7 @@ function Ir(e) {
|
|
|
2854
2917
|
continue;
|
|
2855
2918
|
}
|
|
2856
2919
|
if (i) {
|
|
2857
|
-
a.push(
|
|
2920
|
+
a.push(Br(e)), r++;
|
|
2858
2921
|
continue;
|
|
2859
2922
|
}
|
|
2860
2923
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -2865,24 +2928,24 @@ function Ir(e) {
|
|
|
2865
2928
|
if (u) {
|
|
2866
2929
|
c();
|
|
2867
2930
|
let e = u[1].length;
|
|
2868
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${
|
|
2931
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${Vr(Br(u[2]))}</h${e}>`), r++;
|
|
2869
2932
|
continue;
|
|
2870
2933
|
}
|
|
2871
2934
|
if (e.startsWith("> ")) {
|
|
2872
2935
|
c();
|
|
2873
2936
|
let e = [];
|
|
2874
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
2875
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
2937
|
+
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(Br((t[r] ?? "").slice(2))), r++;
|
|
2938
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${Vr(e.join("<br />"))}</blockquote>`);
|
|
2876
2939
|
continue;
|
|
2877
2940
|
}
|
|
2878
2941
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
2879
2942
|
if (d) {
|
|
2880
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
2943
|
+
o && o !== "ul" && c(), o = "ul", s.push(Br(d[1])), r++;
|
|
2881
2944
|
continue;
|
|
2882
2945
|
}
|
|
2883
2946
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
2884
2947
|
if (f) {
|
|
2885
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
2948
|
+
o && o !== "ol" && c(), o = "ol", s.push(Br(f[1])), r++;
|
|
2886
2949
|
continue;
|
|
2887
2950
|
}
|
|
2888
2951
|
if (!e.trim()) {
|
|
@@ -2894,13 +2957,13 @@ function Ir(e) {
|
|
|
2894
2957
|
for (; r < t.length;) {
|
|
2895
2958
|
let e = t[r] ?? "";
|
|
2896
2959
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
2897
|
-
p.push(
|
|
2960
|
+
p.push(Br(e)), r++;
|
|
2898
2961
|
}
|
|
2899
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
2962
|
+
n.push(`<p class="my-2 leading-relaxed">${Vr(p.join("<br />"))}</p>`);
|
|
2900
2963
|
}
|
|
2901
2964
|
return c(), i && a.length && n.push(`<pre class="overflow-x-auto rounded-lg bg-muted p-3 text-sm"><code>${a.join("\n")}</code></pre>`), n.join("\n");
|
|
2902
2965
|
}
|
|
2903
2966
|
//#endregion
|
|
2904
|
-
export {
|
|
2967
|
+
export { un as AUTHOR_MEMO_MAX_LENGTH, Yn as BOARD_SECTION_COLS, Qn as BOARD_SECTION_KINDS, $n as BOARD_SECTION_SORTS, Kn as BOARD_USER_ROLES, gn as COMMENT_BODY_MAX_LENGTH, st as DEFAULT_BLOCK_BAN_DECAY_MS, ot as DEFAULT_BLOCK_BAN_THRESHOLD, Gt as DEFAULT_BOARDS, Kt as DEFAULT_CATEGORIES, S as DEFAULT_COMMENTS_COLLECTION, ce as DEFAULT_LIKES_COLLECTION, T as DEFAULT_MEMI_BOARD_LOCALE, h as DEFAULT_POSTS_COLLECTION, n as DEFAULT_REPORTS_COLLECTION, se as DEFAULT_SETTINGS_COLLECTION, g as DEFAULT_USERS_COLLECTION, re as FALLBACK_MEMI_BOARD_LOCALE, j as MEMI_BOARD_LOCALES, Cn as REPORT_DETAIL_MAX_LENGTH, wn as REPORT_REASONS, Dr as WRITING_ASSISTANT_DAILY_LIMIT, Be as asHttpUrl, Ke as boardListOgDescription, Ge as boardListOgTitle, We as boardPostOgDescription, Ue as boardPostOgTitle, Xn as boardSectionKinds, Zn as boardSectionSorts, r as boardSsrKey, f as boardUserDoc, e as boardUsersCol, Bt as buildPostPreview, tt as canManageBoardByRole, nt as canWriteCommentByRole, tr as clampSectionCount, k as commentDoc, u as commentsCol, b as configureMemiBoard, er as defaultSectionCount, ct as effectiveModerationBlockCount, xt as extractEditorImageUrls, Qe as fetchPublicListForSeo, Ze as fetchPublicPostForSeo, x as formatDate, ne as formatFullDate, ae as formatRelativeDate, dt as formatRestrictedUntilLabel, C as formatTimestampDetails, Ft as hasBodyImage, Pt as hasBodyText, yt as imeSafeSubmitClick, vt as imeSafeSubmitPointerDown, et as isAssignedStaff, It as isContentEmpty, Zt as isFirestorePermissionDenied, A as isMemiBoardLocaleLoaded, ut as isModerationWriteRestricted, d as likeDoc, p as likeDocId, a as likesCol, oe as loadMemiBoardLocale, zr as localizedText, xn as memiBoardCommentLikesKey, ie as memiBoardHostLocaleKey, D as memiBoardLocaleInfo, fr as miniBoardView, sr as miniCommentLink, or as miniMoreLink, cr as miniPostImage, ar as miniPostLink, lr as miniPostTitle, pr as miniPostsView, ur as miniViewGroup, dr as miniViewLabel, lt as moderationWriteRestrictedUntilMs, qe as normalizeBasePath, Sr as normalizeBoardSection, E as normalizeMemiBoardLocale, nr as pairSectionCols, Mt as plainTextFromHtml, c as postBodyDoc, l as postDoc, Ct as postNamespaceFromStoragePath, m as postStorageFolder, N as postsCol, Hr as renderMarkdownToHtml, t as reportDoc, v as reportDocId, Tn as reportReasonLabel, s as reportsCol, _ as resolveBoardPathConfig, Je as resolvePublicSeoDb, rr as sectionColClass, ir as sectionMinHeight, y as settingsCol, o as settingsDoc, te as slugify, St as storagePathFromDownloadUrl, Nt as titleFromBody, He as toAbsoluteUrl, Ve as toBoardOgImageUrl, w as translate, M as useBoardPathConfig, ht as useMemiBoardAuth, fn as useMemiBoardAuthorMemo, Rr as useMemiBoardAutoTranslate, ln as useMemiBoardCommentFeed, Sn as useMemiBoardCommentLikes, vn as useMemiBoardComments, le as useMemiBoardConfig, O as useMemiBoardI18n, bn as useMemiBoardLikes, wr as useMemiBoardMiniLatest, Tr as useMemiBoardMiniPicked, Gn as useMemiBoardModeration, nn as useMemiBoardPost, tn as useMemiBoardPostList, $t as useMemiBoardPosts, yn as useMemiBoardReplies, On as useMemiBoardReportQueue, En as useMemiBoardReports, Cr as useMemiBoardSections, Yt as useMemiBoardSettings, sn as useMemiBoardUserComments, an as useMemiBoardUserPosts, Jn as useMemiBoardUserProfile, qn as useMemiBoardUsers, kn as useMemiBoardViews, Fr as useMemiBoardWritingAssistant, Er as versionHistory, zt as videoListCoverUrl, Rt as youtubeId };
|
|
2905
2968
|
|
|
2906
2969
|
//# sourceMappingURL=index.js.map
|