memi-board 0.48.1 → 0.50.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 +18 -4
- package/dist/{commentImages-D1PEZIU9.js → commentImages-DyqzlNcX.js} +24 -2
- package/dist/commentImages-DyqzlNcX.js.map +1 -0
- package/dist/composables/useMemiBoardCleanup.d.ts +22 -0
- package/dist/{en-BSBqcgqW.js → en-CgBc_Nxe.js} +23 -1
- package/dist/en-CgBc_Nxe.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +744 -546
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/CategorySettings.vue +9 -0
- package/dist/runtime/components/Editor.vue +14 -2
- package/dist/runtime/components/OrphanCleanup.vue +58 -0
- package/dist/runtime/components/Settings.vue +12 -0
- package/dist/storage.js +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/utils/orphanCleanup.d.ts +21 -0
- package/docs/firestore.rules.example +8 -8
- package/docs/internationalization.md +3 -0
- package/docs/storage.rules.example +14 -8
- package/package.json +2 -1
- package/dist/commentImages-D1PEZIU9.js.map +0 -1
- package/dist/en-BSBqcgqW.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, J as l, K as u, L as d, M as f, N as p, O as m, P as h, R as g, S as _, T as v, U as y, V as b, W as x, X as S, Y as C, Z as w, _ as T, a as E, b as D, c as ee, d as te, f as ne, g as O, h as re, j as ie, k as ae, l as oe, m as k, o as se, p as ce, q as le, r as ue, s as A, t as de, u as fe, v as pe, w as j, x as me, y as M, z as he } from "./commentImages-
|
|
2
|
-
import { computed as N, onScopeDispose as P, reactive as ge, ref as F, toValue as I, watch as L } from "vue";
|
|
3
|
-
import { deleteDoc as
|
|
4
|
-
import { useCollection as
|
|
5
|
-
import { GoogleAuthProvider as
|
|
6
|
-
import { deleteObject as
|
|
7
|
-
import { GoogleAIBackend 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, J as l, K as u, L as d, M as f, N as p, O as m, P as h, R as g, S as _, T as v, U as y, V as b, W as x, X as S, Y as C, Z as w, _ as T, a as E, b as D, c as ee, d as te, f as ne, g as O, h as re, j as ie, k as ae, l as oe, m as k, o as se, p as ce, q as le, r as ue, s as A, t as de, u as fe, v as pe, w as j, x as me, y as M, z as he } from "./commentImages-DyqzlNcX.js";
|
|
2
|
+
import { computed as N, onScopeDispose as P, reactive as ge, readonly as _e, ref as F, toValue as I, watch as L } from "vue";
|
|
3
|
+
import { collection as ve, deleteDoc as ye, deleteField as be, doc as R, documentId as z, endAt as xe, endBefore as Se, getDoc as B, getDocFromServer as Ce, getDocs as V, getDocsFromServer as we, increment as H, limit as U, limitToLast as Te, onSnapshot as W, orderBy as G, query as K, runTransaction as Ee, serverTimestamp as q, setDoc as De, startAfter as J, updateDoc as Y, where as X, writeBatch as Oe } from "firebase/firestore";
|
|
4
|
+
import { useCollection as ke, useCurrentUser as Ae, useDocument as je, useFirebaseApp as Me, useFirebaseAuth as Ne, useFirestore as Z } from "vuefire";
|
|
5
|
+
import { GoogleAuthProvider as Pe, OAuthProvider as Fe, createUserWithEmailAndPassword as Ie, getAuth as Le, signInWithEmailAndPassword as Re, signInWithPopup as ze, signOut as Be, updateProfile as Ve } from "firebase/auth";
|
|
6
|
+
import { deleteObject as He, getMetadata as Ue, getStorage as We, list as Ge, listAll as Ke, ref as qe } from "firebase/storage";
|
|
7
|
+
import { GoogleAIBackend as Je, Schema as Ye, getAI as Xe, getGenerativeModel as Ze } from "firebase/ai";
|
|
8
8
|
//#region src/utils/boardSeo.ts
|
|
9
|
-
function
|
|
9
|
+
function Qe(e, t) {
|
|
10
10
|
return !e || e === "전체" || e === k(t, "seo.allCategory");
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function $e(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 et(e) {
|
|
18
|
+
let t = $e(e);
|
|
19
19
|
if (!t) return null;
|
|
20
20
|
if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
|
|
21
21
|
try {
|
|
@@ -32,49 +32,49 @@ function qe(e) {
|
|
|
32
32
|
return t;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function tt(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 ? tt(n, t) : r;
|
|
38
38
|
}
|
|
39
|
-
function
|
|
39
|
+
function nt(e, t, n, r, i) {
|
|
40
40
|
let a = r?.trim().slice(0, 40), o = e.trim() || a || k(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 rt(e, t) {
|
|
44
44
|
let n = [];
|
|
45
45
|
return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(k(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 it(e, t, n) {
|
|
48
48
|
let r = t.trim() || "Board", i = e?.trim();
|
|
49
|
-
return
|
|
49
|
+
return Qe(i, n) ? k(n, "seo.listTitleAll", { site: r }) : k(n, "seo.listTitle", {
|
|
50
50
|
name: i,
|
|
51
51
|
site: r
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
|
-
function
|
|
54
|
+
function at(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 = Qe(n, t) ? k(t, "seo.listDescriptionAll") : k(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 ot(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/utils/localizedFields.ts
|
|
65
|
-
var
|
|
65
|
+
var st = [
|
|
66
66
|
"label",
|
|
67
67
|
"description",
|
|
68
68
|
"title",
|
|
69
69
|
"summary",
|
|
70
70
|
"contentHash"
|
|
71
71
|
];
|
|
72
|
-
function
|
|
72
|
+
function ct(e) {
|
|
73
73
|
let t = 5381;
|
|
74
74
|
for (let n = 0; n < e.length; n++) t = (t << 5) + t ^ e.charCodeAt(n);
|
|
75
75
|
return (t >>> 0).toString(36);
|
|
76
76
|
}
|
|
77
|
-
function
|
|
77
|
+
function lt(e, t) {
|
|
78
78
|
let n = e?.[t];
|
|
79
79
|
return typeof n == "string" ? n.trim() : "";
|
|
80
80
|
}
|
|
@@ -90,19 +90,19 @@ function Q(e) {
|
|
|
90
90
|
}
|
|
91
91
|
return Object.keys(t).length ? t : void 0;
|
|
92
92
|
}
|
|
93
|
-
function
|
|
93
|
+
function ut(e) {
|
|
94
94
|
if (!e || typeof e != "object") return;
|
|
95
95
|
let t = e, n = {}, r = M(t.locale);
|
|
96
96
|
r && (n.locale = r);
|
|
97
|
-
for (let e of
|
|
97
|
+
for (let e of st) typeof t[e] == "string" && (n[e] = t[e]);
|
|
98
98
|
return Object.keys(n).length ? n : void 0;
|
|
99
99
|
}
|
|
100
|
-
function
|
|
100
|
+
function dt(e) {
|
|
101
101
|
if (!e || typeof e != "object") return;
|
|
102
102
|
let t = e, n = {}, r = M(t.locale);
|
|
103
103
|
return r && (n.locale = r), typeof t.hash == "string" && t.hash && (n.hash = t.hash), n.locale || n.hash ? n : void 0;
|
|
104
104
|
}
|
|
105
|
-
function
|
|
105
|
+
function ft(e, t, n) {
|
|
106
106
|
let r = {};
|
|
107
107
|
if (n) for (let e of T) {
|
|
108
108
|
let t = n[e.code];
|
|
@@ -121,58 +121,58 @@ function at(e, t, n) {
|
|
|
121
121
|
...i
|
|
122
122
|
}), r;
|
|
123
123
|
}
|
|
124
|
-
function
|
|
125
|
-
return !e || !t.length ? !1 : T.every((n) => t.every((t) => !!
|
|
124
|
+
function pt(e, t) {
|
|
125
|
+
return !e || !t.length ? !1 : T.every((n) => t.every((t) => !!lt(e[n.code], t)));
|
|
126
126
|
}
|
|
127
|
-
function
|
|
127
|
+
function mt(e, t, r, i, a) {
|
|
128
128
|
if (!n()) return i;
|
|
129
129
|
let o = M(t), s = M(a);
|
|
130
130
|
if (!o || s && o === s) return i;
|
|
131
|
-
let c =
|
|
131
|
+
let c = lt(e?.[o], r);
|
|
132
132
|
if (c) return c;
|
|
133
133
|
if (o !== "en" && s !== "en") {
|
|
134
|
-
let t =
|
|
134
|
+
let t = lt(e?.en, r);
|
|
135
135
|
if (t) return t;
|
|
136
136
|
}
|
|
137
137
|
return i;
|
|
138
138
|
}
|
|
139
|
-
function
|
|
140
|
-
return r ? !t || t[n] !== r ? r :
|
|
139
|
+
function ht(e, t, n, r, i) {
|
|
140
|
+
return r ? !t || t[n] !== r ? r : mt(e, i, n, r, t.locale) : "";
|
|
141
141
|
}
|
|
142
|
-
function
|
|
143
|
-
return
|
|
142
|
+
function gt(e, t) {
|
|
143
|
+
return ht(e.i18n, e.i18nSource, "title", e.title ?? "", t);
|
|
144
144
|
}
|
|
145
|
-
function
|
|
146
|
-
return
|
|
145
|
+
function _t(e, t) {
|
|
146
|
+
return ht(e.i18n, e.i18nSource, "summary", e.summary ?? "", t);
|
|
147
147
|
}
|
|
148
|
-
function
|
|
148
|
+
function vt(e, t) {
|
|
149
149
|
let n = e.content || "", r = e.contentI18nSource?.hash;
|
|
150
|
-
return !n || !r || r !==
|
|
150
|
+
return !n || !r || r !== ct(n) ? n : mt(e.contentI18n, t, "content", n, e.contentI18nSource?.locale) || n;
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function yt(e) {
|
|
153
153
|
let t = e.label.trim(), n = (e.description ?? "").trim();
|
|
154
|
-
return !t || !e.i18nSource || e.i18nSource.label !== t || (e.i18nSource.description ?? "") !== n ? !1 :
|
|
154
|
+
return !t || !e.i18nSource || e.i18nSource.label !== t || (e.i18nSource.description ?? "") !== n ? !1 : pt(e.i18n, n ? ["label", "description"] : ["label"]);
|
|
155
155
|
}
|
|
156
156
|
//#endregion
|
|
157
157
|
//#region src/composables/fetchPublicSeo.ts
|
|
158
|
-
function
|
|
158
|
+
function bt() {
|
|
159
159
|
return {
|
|
160
160
|
db: Z(),
|
|
161
161
|
paths: j()
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
-
async function
|
|
164
|
+
async function xt(e, t, n) {
|
|
165
165
|
try {
|
|
166
|
-
let r = await
|
|
166
|
+
let r = await B(w(e.db, e.paths, t));
|
|
167
167
|
if (!r.exists()) return {
|
|
168
168
|
label: t,
|
|
169
169
|
description: "",
|
|
170
170
|
hidden: !1
|
|
171
171
|
};
|
|
172
|
-
let i = r.data() ?? {}, a = typeof i.label == "string" && i.label.trim() ? i.label.trim() : t, o = typeof i.description == "string" ? i.description.trim() : "", s = Q(i.i18n), c =
|
|
172
|
+
let i = r.data() ?? {}, a = typeof i.label == "string" && i.label.trim() ? i.label.trim() : t, o = typeof i.description == "string" ? i.description.trim() : "", s = Q(i.i18n), c = ut(i.i18nSource);
|
|
173
173
|
return {
|
|
174
|
-
label:
|
|
175
|
-
description:
|
|
174
|
+
label: ht(s, c, "label", a, n) || a,
|
|
175
|
+
description: ht(s, c, "description", o, n),
|
|
176
176
|
hidden: i.visibility === "hidden"
|
|
177
177
|
};
|
|
178
178
|
} catch {
|
|
@@ -183,7 +183,7 @@ async function mt(e, t, n) {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
-
function
|
|
186
|
+
function St(e) {
|
|
187
187
|
if (e && typeof e == "object" && "toDate" in e) try {
|
|
188
188
|
let t = e.toDate();
|
|
189
189
|
return t instanceof Date ? t.toISOString() : null;
|
|
@@ -192,17 +192,17 @@ function ht(e) {
|
|
|
192
192
|
}
|
|
193
193
|
return null;
|
|
194
194
|
}
|
|
195
|
-
async function
|
|
195
|
+
async function Ct(e, t, n, r) {
|
|
196
196
|
let i = e.trim(), a = t.trim();
|
|
197
197
|
if (!i || !a) return null;
|
|
198
|
-
let s = n ??
|
|
198
|
+
let s = n ?? bt();
|
|
199
199
|
s.paths = C(s.paths), r && await ce(r);
|
|
200
|
-
let c = await
|
|
200
|
+
let c = await xt(s, i, r);
|
|
201
201
|
if (c.hidden) return null;
|
|
202
202
|
try {
|
|
203
|
-
let e = (await
|
|
203
|
+
let e = (await V(K(o(s.db, s.paths), X("boardId", "==", i), X("slug", "==", a), X("listed", "==", !0), X("isPublished", "==", !0), U(1)))).docs[0];
|
|
204
204
|
if (!e) return null;
|
|
205
|
-
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title : "", l = typeof t.summary == "string" ? t.summary : "", u = Q(t.i18n), d =
|
|
205
|
+
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title : "", l = typeof t.summary == "string" ? t.summary : "", u = Q(t.i18n), d = ut(t.i18nSource), f = gt({
|
|
206
206
|
title: n,
|
|
207
207
|
i18n: u,
|
|
208
208
|
i18nSource: d
|
|
@@ -213,24 +213,24 @@ async function gt(e, t, n, r) {
|
|
|
213
213
|
category: i,
|
|
214
214
|
categoryLabel: c.label,
|
|
215
215
|
title: f,
|
|
216
|
-
summary:
|
|
216
|
+
summary: _t({
|
|
217
217
|
summary: l,
|
|
218
218
|
i18n: u,
|
|
219
219
|
i18nSource: d
|
|
220
220
|
}, r).trim().slice(0, 200),
|
|
221
|
-
previewImage:
|
|
221
|
+
previewImage: et(t.previewImage),
|
|
222
222
|
authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
|
|
223
|
-
createdAt:
|
|
223
|
+
createdAt: St(t.createdAt)
|
|
224
224
|
} : null;
|
|
225
225
|
} catch (e) {
|
|
226
226
|
return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
-
async function
|
|
229
|
+
async function wt(e, t, n) {
|
|
230
230
|
n && await ce(n);
|
|
231
|
-
let r = e?.trim() || null, i = t ??
|
|
231
|
+
let r = e?.trim() || null, i = t ?? bt();
|
|
232
232
|
if (i.paths = C(i.paths), r) {
|
|
233
|
-
let e = await
|
|
233
|
+
let e = await xt(i, r, n);
|
|
234
234
|
if (e.hidden) return {
|
|
235
235
|
kind: "category",
|
|
236
236
|
category: r,
|
|
@@ -239,7 +239,7 @@ async function _t(e, t, n) {
|
|
|
239
239
|
ogImage: null,
|
|
240
240
|
recentTitles: []
|
|
241
241
|
};
|
|
242
|
-
let { recentTitles: t, ogImage: a } = await
|
|
242
|
+
let { recentTitles: t, ogImage: a } = await Tt(i, r, n);
|
|
243
243
|
return {
|
|
244
244
|
kind: "category",
|
|
245
245
|
category: r,
|
|
@@ -258,17 +258,17 @@ async function _t(e, t, n) {
|
|
|
258
258
|
recentTitles: []
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
-
async function
|
|
261
|
+
async function Tt(e, t, n) {
|
|
262
262
|
let r = [], i = null;
|
|
263
263
|
try {
|
|
264
|
-
let a = await
|
|
264
|
+
let a = await V(K(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), G("createdAt", "desc"), U(12)));
|
|
265
265
|
for (let e of a.docs) {
|
|
266
|
-
let t = e.data() ?? {}, a =
|
|
266
|
+
let t = e.data() ?? {}, a = gt({
|
|
267
267
|
title: typeof t.title == "string" ? t.title : "",
|
|
268
268
|
i18n: Q(t.i18n),
|
|
269
|
-
i18nSource:
|
|
269
|
+
i18nSource: ut(t.i18nSource)
|
|
270
270
|
}, n).trim();
|
|
271
|
-
a && r.length < 5 && r.push(a), i ||=
|
|
271
|
+
a && r.length < 5 && r.push(a), i ||= et(t.previewImage);
|
|
272
272
|
}
|
|
273
273
|
} catch (e) {
|
|
274
274
|
import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
|
|
@@ -280,31 +280,31 @@ async function vt(e, t, n) {
|
|
|
280
280
|
}
|
|
281
281
|
//#endregion
|
|
282
282
|
//#region src/utils/boardAccess.ts
|
|
283
|
-
function
|
|
283
|
+
function Et(e, t) {
|
|
284
284
|
return !e || !t ? !1 : [...t.allowedStaffUids ?? [], ...t.allowedManagerUids ?? []].includes(e);
|
|
285
285
|
}
|
|
286
|
-
function
|
|
287
|
-
return e ? t ? !0 : n ?
|
|
286
|
+
function Dt(e, t, n, r) {
|
|
287
|
+
return e ? t ? !0 : n ? Et(e, r) : !1 : !1;
|
|
288
288
|
}
|
|
289
|
-
function
|
|
289
|
+
function Ot(e, t, n, r) {
|
|
290
290
|
if (!e) return !1;
|
|
291
|
-
if (r?.visibility === "hidden") return
|
|
291
|
+
if (r?.visibility === "hidden") return Dt(e, t, n, r);
|
|
292
292
|
let i = r?.commentWriteRole ?? "user";
|
|
293
|
-
return i === "admin" ? t : i !== "staff" ||
|
|
293
|
+
return i === "admin" ? t : i !== "staff" || Dt(e, t, n, r);
|
|
294
294
|
}
|
|
295
295
|
//#endregion
|
|
296
296
|
//#region src/composables/boardLookup.ts
|
|
297
|
-
var
|
|
298
|
-
function
|
|
299
|
-
|
|
297
|
+
var kt;
|
|
298
|
+
function At(e) {
|
|
299
|
+
kt = e;
|
|
300
300
|
}
|
|
301
|
-
function
|
|
302
|
-
return
|
|
301
|
+
function jt(e) {
|
|
302
|
+
return kt?.(e);
|
|
303
303
|
}
|
|
304
304
|
//#endregion
|
|
305
305
|
//#region src/utils/moderation-strike.ts
|
|
306
|
-
var
|
|
307
|
-
function
|
|
306
|
+
var Mt = 10, Nt = 864e5;
|
|
307
|
+
function Pt(e, t, n = Date.now(), r = Nt) {
|
|
308
308
|
let i = Math.max(0, Math.floor(Number(e) || 0));
|
|
309
309
|
if (i <= 0) return 0;
|
|
310
310
|
let a = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -312,7 +312,7 @@ function Dt(e, t, n = Date.now(), r = Et) {
|
|
|
312
312
|
let o = Math.floor((n - a) / r);
|
|
313
313
|
return Math.max(0, i - o);
|
|
314
314
|
}
|
|
315
|
-
function
|
|
315
|
+
function Ft(e, t, n = Date.now(), r = 10, i = Nt) {
|
|
316
316
|
let a = Math.max(0, Math.floor(Number(e) || 0));
|
|
317
317
|
if (a < r) return null;
|
|
318
318
|
let o = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -320,10 +320,10 @@ function Ot(e, t, n = Date.now(), r = 10, i = Et) {
|
|
|
320
320
|
let s = o + (a - (r - 1)) * i;
|
|
321
321
|
return s > n ? s : null;
|
|
322
322
|
}
|
|
323
|
-
function
|
|
324
|
-
return
|
|
323
|
+
function It(e, t, n = Date.now(), r = 10, i = Nt) {
|
|
324
|
+
return Ft(e, t, n, r, i) != null;
|
|
325
325
|
}
|
|
326
|
-
function
|
|
326
|
+
function Lt(e, t = Date.now(), n) {
|
|
327
327
|
let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
|
|
328
328
|
if (i <= 0) return k(n, "moderationStrike.liftSoon");
|
|
329
329
|
if (i < 24) return k(n, "moderationStrike.liftInHours", { hours: i });
|
|
@@ -333,7 +333,7 @@ function At(e, t = Date.now(), n) {
|
|
|
333
333
|
hours: o
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
|
-
function
|
|
336
|
+
function Rt(e) {
|
|
337
337
|
if (e == null) return null;
|
|
338
338
|
if (typeof e == "number" && Number.isFinite(e)) return e;
|
|
339
339
|
if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
|
|
@@ -345,26 +345,26 @@ function jt(e) {
|
|
|
345
345
|
}
|
|
346
346
|
//#endregion
|
|
347
347
|
//#region src/composables/useMemiBoardAuth.ts
|
|
348
|
-
var
|
|
349
|
-
function
|
|
348
|
+
var zt = "__MEMI_BOARD_AUTH_STATE__";
|
|
349
|
+
function Bt() {
|
|
350
350
|
let e = globalThis;
|
|
351
|
-
return e[
|
|
351
|
+
return e[zt] || (e[zt] = {
|
|
352
352
|
role: F("user"),
|
|
353
353
|
rolePending: F(!0),
|
|
354
354
|
rawBlockCount: F(0),
|
|
355
355
|
blockAtMs: F(null),
|
|
356
356
|
watchStarted: !1
|
|
357
|
-
}), e[
|
|
357
|
+
}), e[zt];
|
|
358
358
|
}
|
|
359
|
-
function
|
|
360
|
-
let e = v(), { t, locale: n } = A(), r =
|
|
359
|
+
function Vt() {
|
|
360
|
+
let e = v(), { t, locale: n } = A(), r = Bt(), i = Z(), o = Ne(), s = Ae(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
|
|
361
361
|
function f() {
|
|
362
362
|
let t = e.moderation?.blockBanThreshold;
|
|
363
363
|
return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
|
|
364
364
|
}
|
|
365
365
|
function p() {
|
|
366
366
|
let t = e.moderation?.blockBanDecayMs;
|
|
367
|
-
return typeof t == "number" && t > 0 ? t :
|
|
367
|
+
return typeof t == "number" && t > 0 ? t : Nt;
|
|
368
368
|
}
|
|
369
369
|
function m(e) {
|
|
370
370
|
return h(i, j(), e);
|
|
@@ -380,25 +380,25 @@ function Pt() {
|
|
|
380
380
|
u.value = 0, d.value = null;
|
|
381
381
|
return;
|
|
382
382
|
}
|
|
383
|
-
u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value =
|
|
383
|
+
u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = Rt(e.moderationBlockAt);
|
|
384
384
|
}
|
|
385
385
|
async function _(e) {
|
|
386
|
-
let t = m(e.uid), n = a(i, j(), e.uid), r = await
|
|
387
|
-
if (!r.exists()) return await Promise.all([
|
|
386
|
+
let t = m(e.uid), n = a(i, j(), e.uid), r = await B(t);
|
|
387
|
+
if (!r.exists()) return await Promise.all([De(t, {
|
|
388
388
|
role: "user",
|
|
389
389
|
displayName: e.displayName,
|
|
390
390
|
photoURL: e.photoURL,
|
|
391
|
-
joinedAt:
|
|
392
|
-
lastVisitAt:
|
|
393
|
-
updatedAt:
|
|
394
|
-
}),
|
|
391
|
+
joinedAt: q(),
|
|
392
|
+
lastVisitAt: q(),
|
|
393
|
+
updatedAt: q()
|
|
394
|
+
}), De(n, { email: e.email })]), g({ moderationBlockCount: 0 }), "user";
|
|
395
395
|
let o = r.data();
|
|
396
396
|
g(o);
|
|
397
397
|
let s = typeof o.displayName == "string" && o.displayName.trim(), c = typeof o.photoURL == "string" && o.photoURL.trim(), l = {
|
|
398
|
-
lastVisitAt:
|
|
399
|
-
updatedAt:
|
|
398
|
+
lastVisitAt: q(),
|
|
399
|
+
updatedAt: q()
|
|
400
400
|
};
|
|
401
|
-
return !s && e.displayName && (l.displayName = e.displayName), !c && e.photoURL && (l.photoURL = e.photoURL), await Promise.all([Y(t, l),
|
|
401
|
+
return !s && e.displayName && (l.displayName = e.displayName), !c && e.photoURL && (l.photoURL = e.photoURL), await Promise.all([Y(t, l), De(n, { email: e.email }, { merge: !0 })]), [
|
|
402
402
|
"owner",
|
|
403
403
|
"admin",
|
|
404
404
|
"manager",
|
|
@@ -412,7 +412,7 @@ function Pt() {
|
|
|
412
412
|
g(void 0);
|
|
413
413
|
return;
|
|
414
414
|
}
|
|
415
|
-
let t = await
|
|
415
|
+
let t = await B(m(e));
|
|
416
416
|
g(t.exists() ? t.data() : void 0);
|
|
417
417
|
}
|
|
418
418
|
r.watchStarted || (r.watchStarted = !0, L(s, async (e) => {
|
|
@@ -428,14 +428,14 @@ function Pt() {
|
|
|
428
428
|
l.value = !1;
|
|
429
429
|
}
|
|
430
430
|
}, { immediate: !0 }));
|
|
431
|
-
let b = N(() => !!s.value), x = N(() => c.value === "owner"), S = N(() => c.value === "manager"), C = N(() => x.value || c.value === "admin"), w = N(() => S.value || c.value === "staff"), T = N(() => C.value || w.value), E = N(() =>
|
|
431
|
+
let b = N(() => !!s.value), x = N(() => c.value === "owner"), S = N(() => c.value === "manager"), C = N(() => x.value || c.value === "admin"), w = N(() => S.value || c.value === "staff"), T = N(() => C.value || w.value), E = N(() => Pt(u.value, d.value, Date.now(), p())), D = N(() => {
|
|
432
432
|
let e = f();
|
|
433
|
-
return e <= 0 ? !1 :
|
|
433
|
+
return e <= 0 ? !1 : It(u.value, d.value, Date.now(), e, p());
|
|
434
434
|
}), ee = N(() => {
|
|
435
435
|
let e = f();
|
|
436
436
|
if (e <= 0) return null;
|
|
437
|
-
let t =
|
|
438
|
-
return t == null ? null :
|
|
437
|
+
let t = Ft(u.value, d.value, Date.now(), e, p());
|
|
438
|
+
return t == null ? null : Lt(t, Date.now(), n.value);
|
|
439
439
|
}), te = N(() => {
|
|
440
440
|
if (!D.value) return null;
|
|
441
441
|
let e = ee.value;
|
|
@@ -448,26 +448,26 @@ function Pt() {
|
|
|
448
448
|
restricted: !1,
|
|
449
449
|
messageSuffix: ""
|
|
450
450
|
};
|
|
451
|
-
let i = m(e), a = await
|
|
451
|
+
let i = m(e), a = await B(i);
|
|
452
452
|
a.exists() ? await Y(i, {
|
|
453
|
-
moderationBlockCount:
|
|
454
|
-
moderationBlockAt:
|
|
455
|
-
updatedAt:
|
|
456
|
-
}) : await
|
|
453
|
+
moderationBlockCount: H(1),
|
|
454
|
+
moderationBlockAt: q(),
|
|
455
|
+
updatedAt: q()
|
|
456
|
+
}) : await De(i, {
|
|
457
457
|
role: "user",
|
|
458
458
|
displayName: s.value?.displayName ?? null,
|
|
459
459
|
moderationBlockCount: 1,
|
|
460
|
-
moderationBlockAt:
|
|
461
|
-
updatedAt:
|
|
460
|
+
moderationBlockAt: q(),
|
|
461
|
+
updatedAt: q()
|
|
462
462
|
});
|
|
463
463
|
let o = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
|
|
464
464
|
u.value = o, d.value = c;
|
|
465
|
-
let l =
|
|
465
|
+
let l = Pt(o, c, c, p()), h = It(o, c, c, r, p()), g = t("auth.strikeSuffix", {
|
|
466
466
|
count: l,
|
|
467
467
|
threshold: r
|
|
468
468
|
});
|
|
469
469
|
if (h) {
|
|
470
|
-
let e =
|
|
470
|
+
let e = Ft(o, c, c, r, p()), i = e == null ? null : Lt(e, c, n.value);
|
|
471
471
|
g = i ? t("auth.strikeRestrictedUntil", {
|
|
472
472
|
count: l,
|
|
473
473
|
until: i
|
|
@@ -484,27 +484,27 @@ function Pt() {
|
|
|
484
484
|
return o;
|
|
485
485
|
}
|
|
486
486
|
async function re() {
|
|
487
|
-
return
|
|
487
|
+
return ze(O(), new Pe());
|
|
488
488
|
}
|
|
489
489
|
async function ie() {
|
|
490
|
-
let e = new
|
|
491
|
-
return e.addScope("email"), e.addScope("name"),
|
|
490
|
+
let e = new Fe("apple.com");
|
|
491
|
+
return e.addScope("email"), e.addScope("name"), ze(O(), e);
|
|
492
492
|
}
|
|
493
493
|
async function ae(e, t) {
|
|
494
|
-
return
|
|
494
|
+
return Re(O(), e, t);
|
|
495
495
|
}
|
|
496
496
|
async function oe(e, t, n) {
|
|
497
|
-
let r = await
|
|
498
|
-
return n && await
|
|
497
|
+
let r = await Ie(O(), e, t);
|
|
498
|
+
return n && await Ve(r.user, { displayName: n }), r;
|
|
499
499
|
}
|
|
500
500
|
async function k() {
|
|
501
|
-
await
|
|
501
|
+
await Be(O());
|
|
502
502
|
}
|
|
503
503
|
function se(e) {
|
|
504
|
-
return
|
|
504
|
+
return Dt(s.value?.uid, C.value, w.value, e ? jt(e) : void 0);
|
|
505
505
|
}
|
|
506
506
|
function ce(e) {
|
|
507
|
-
return s.value ? C.value ? !0 : S.value && !!e && (
|
|
507
|
+
return s.value ? C.value ? !0 : S.value && !!e && (jt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
|
|
508
508
|
}
|
|
509
509
|
function le(e) {
|
|
510
510
|
return b.value && (s.value?.uid === e.authorUid || se(e.boardId));
|
|
@@ -546,34 +546,34 @@ function Pt() {
|
|
|
546
546
|
}
|
|
547
547
|
//#endregion
|
|
548
548
|
//#region src/utils/imeSafeSubmit.ts
|
|
549
|
-
var
|
|
550
|
-
function
|
|
549
|
+
var Ht = /* @__PURE__ */ new WeakMap();
|
|
550
|
+
function Ut(e) {
|
|
551
551
|
let t = e.currentTarget;
|
|
552
552
|
return t instanceof HTMLButtonElement ? t : t instanceof Element ? t.closest("button") : null;
|
|
553
553
|
}
|
|
554
|
-
function
|
|
554
|
+
function Wt(e) {
|
|
555
555
|
if (e.pointerType !== "mouse" || e.button !== 0) return;
|
|
556
|
-
let t =
|
|
556
|
+
let t = Ut(e), n = t?.form;
|
|
557
557
|
if (!t || !n || t.disabled) return;
|
|
558
|
-
|
|
558
|
+
Ht.set(t, Date.now() + 1e3);
|
|
559
559
|
let r = document.activeElement;
|
|
560
560
|
r instanceof HTMLElement && r.blur(), setTimeout(() => n.requestSubmit(t), 0);
|
|
561
561
|
}
|
|
562
|
-
function
|
|
563
|
-
let t =
|
|
562
|
+
function Gt(e) {
|
|
563
|
+
let t = Ut(e);
|
|
564
564
|
if (!t) return;
|
|
565
|
-
let n =
|
|
566
|
-
n && Date.now() < n && (e.preventDefault(),
|
|
565
|
+
let n = Ht.get(t);
|
|
566
|
+
n && Date.now() < n && (e.preventDefault(), Ht.delete(t));
|
|
567
567
|
}
|
|
568
568
|
//#endregion
|
|
569
569
|
//#region src/utils/findReadablePost.ts
|
|
570
|
-
async function
|
|
570
|
+
async function Kt(e, t, n, r, i, a) {
|
|
571
571
|
let s = [X("boardId", "==", n), X("slug", "==", r)];
|
|
572
|
-
return a ? (await
|
|
572
|
+
return a ? (await V(K(o(e, t), ...s, U(1)))).docs[0] : (await Promise.all([V(K(o(e, t), ...s, X("listed", "==", !0), X("isPublished", "==", !0), U(1))), ...i ? [V(K(o(e, t), ...s, X("authorUid", "==", i), U(1)))] : []])).flatMap((e) => e.docs)[0];
|
|
573
573
|
}
|
|
574
574
|
//#endregion
|
|
575
575
|
//#region src/utils/extractEditorImageUrls.ts
|
|
576
|
-
function
|
|
576
|
+
function qt(e) {
|
|
577
577
|
if (!e) return [];
|
|
578
578
|
let t = /* @__PURE__ */ new Set();
|
|
579
579
|
for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&", "&"));
|
|
@@ -582,7 +582,7 @@ function Bt(e) {
|
|
|
582
582
|
}
|
|
583
583
|
//#endregion
|
|
584
584
|
//#region src/utils/storagePath.ts
|
|
585
|
-
function
|
|
585
|
+
function Jt(e) {
|
|
586
586
|
try {
|
|
587
587
|
let t = new URL(e), n = t.hostname.toLowerCase();
|
|
588
588
|
if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
|
|
@@ -592,107 +592,107 @@ function Vt(e) {
|
|
|
592
592
|
return null;
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
|
-
function
|
|
595
|
+
function Yt(e) {
|
|
596
596
|
return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
|
|
597
597
|
}
|
|
598
598
|
//#endregion
|
|
599
599
|
//#region src/utils/deletePostCascade.ts
|
|
600
|
-
async function
|
|
600
|
+
async function Xt(e) {
|
|
601
601
|
try {
|
|
602
|
-
let t = await
|
|
603
|
-
await Promise.all([...t.items.map((e) =>
|
|
602
|
+
let t = await Ke(e);
|
|
603
|
+
await Promise.all([...t.items.map((e) => He(e).catch(() => {})), ...t.prefixes.map((e) => Xt(e))]);
|
|
604
604
|
} catch {}
|
|
605
605
|
}
|
|
606
|
-
async function
|
|
607
|
-
await
|
|
606
|
+
async function Zt(e, t) {
|
|
607
|
+
await Xt(qe(e, `${t}/images`)), await Xt(qe(e, `${t}/attachments`)), await Xt(qe(e, `${t}/comment-images`));
|
|
608
608
|
}
|
|
609
|
-
var
|
|
610
|
-
async function
|
|
611
|
-
return (await
|
|
609
|
+
var Qt = 450;
|
|
610
|
+
async function $t(e, t, n) {
|
|
611
|
+
return (await V(K(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => de(e.data().images, `${x(t, n)}/comment-images/`));
|
|
612
612
|
}
|
|
613
|
-
async function
|
|
613
|
+
async function en(e, t) {
|
|
614
614
|
let n = [...new Set([...t].filter(Boolean))];
|
|
615
|
-
await Promise.all(n.map((t) =>
|
|
615
|
+
await Promise.all(n.map((t) => He(qe(e, t)).catch(() => {})));
|
|
616
616
|
}
|
|
617
|
-
async function
|
|
618
|
-
let n = await
|
|
619
|
-
for (let t = 0; t < n.docs.length; t +=
|
|
620
|
-
let r =
|
|
621
|
-
n.docs.slice(t, t +
|
|
617
|
+
async function tn(e, t) {
|
|
618
|
+
let n = await V(t);
|
|
619
|
+
for (let t = 0; t < n.docs.length; t += Qt) {
|
|
620
|
+
let r = Oe(e);
|
|
621
|
+
n.docs.slice(t, t + Qt).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
622
622
|
}
|
|
623
623
|
}
|
|
624
|
-
async function
|
|
625
|
-
await
|
|
624
|
+
async function nn(e, t, n) {
|
|
625
|
+
await tn(e, K(b(e, t), X("postId", "==", n)));
|
|
626
626
|
}
|
|
627
|
-
async function
|
|
628
|
-
await
|
|
627
|
+
async function rn(e, t, n) {
|
|
628
|
+
await tn(e, K(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
|
|
629
629
|
}
|
|
630
|
-
async function
|
|
631
|
-
let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([
|
|
630
|
+
async function an(e, t, n, r) {
|
|
631
|
+
let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([B(i), B(a)]);
|
|
632
632
|
if (!o.exists()) return;
|
|
633
|
-
let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await
|
|
633
|
+
let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await $t(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
634
634
|
for (let e of l.attachments ?? []) {
|
|
635
635
|
if (!e?.path) continue;
|
|
636
636
|
f.add(e.path);
|
|
637
|
-
let t =
|
|
637
|
+
let t = Yt(e.path);
|
|
638
638
|
t && t !== r && p.add(t);
|
|
639
639
|
}
|
|
640
|
-
for (let e of
|
|
641
|
-
let t =
|
|
640
|
+
for (let e of qt(u)) {
|
|
641
|
+
let t = Jt(e);
|
|
642
642
|
if (!t) continue;
|
|
643
643
|
f.add(t);
|
|
644
644
|
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
645
645
|
n !== t && n.includes("/images/thumbnails/") && f.add(n);
|
|
646
646
|
let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
647
647
|
i && !t.includes("/thumbnails/") && f.add(`${i[1]}thumbnails/${i[2]}.jpg`);
|
|
648
|
-
let a =
|
|
648
|
+
let a = Yt(t);
|
|
649
649
|
a && a !== r && p.add(a);
|
|
650
650
|
}
|
|
651
|
-
await
|
|
652
|
-
for (let e of p) await
|
|
653
|
-
await
|
|
651
|
+
await Zt(t, x(n, r));
|
|
652
|
+
for (let e of p) await Zt(t, x(n, e));
|
|
653
|
+
await en(t, d), await en(t, f), await tn(e, K(g(e, n), X("postId", "==", r))), await nn(e, n, r), await ye(a), await ye(i);
|
|
654
654
|
}
|
|
655
655
|
//#endregion
|
|
656
656
|
//#region src/utils/postBody.ts
|
|
657
|
-
function
|
|
657
|
+
function on(e) {
|
|
658
658
|
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();
|
|
659
659
|
}
|
|
660
|
-
function
|
|
661
|
-
let n =
|
|
660
|
+
function sn(e, t = 40) {
|
|
661
|
+
let n = on(e);
|
|
662
662
|
if (!n) return "";
|
|
663
663
|
if (n.length <= t) return n;
|
|
664
664
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
665
665
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
666
666
|
}
|
|
667
|
-
function
|
|
668
|
-
return
|
|
667
|
+
function cn(e) {
|
|
668
|
+
return on(e).length > 0;
|
|
669
669
|
}
|
|
670
|
-
function
|
|
670
|
+
function ln(e, t) {
|
|
671
671
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
672
672
|
}
|
|
673
|
-
function
|
|
674
|
-
return !
|
|
673
|
+
function un(e) {
|
|
674
|
+
return !cn(e);
|
|
675
675
|
}
|
|
676
676
|
//#endregion
|
|
677
677
|
//#region src/utils/postPreview.ts
|
|
678
|
-
function
|
|
678
|
+
function dn(e) {
|
|
679
679
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
680
680
|
}
|
|
681
|
-
function
|
|
681
|
+
function fn(e) {
|
|
682
682
|
if (e) try {
|
|
683
|
-
let t = new URL(
|
|
683
|
+
let t = new URL(dn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
684
684
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
685
685
|
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;
|
|
686
686
|
} catch {
|
|
687
687
|
return;
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
|
-
function
|
|
691
|
-
let t =
|
|
690
|
+
function pn(e) {
|
|
691
|
+
let t = fn(e.videoUrl);
|
|
692
692
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
693
693
|
}
|
|
694
|
-
function
|
|
695
|
-
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 ?
|
|
694
|
+
function mn(e, t = []) {
|
|
695
|
+
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 ? dn(r[1]) : a, s = o ? fn(o) : void 0, c = n ? dn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
696
696
|
return {
|
|
697
697
|
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),
|
|
698
698
|
...c ? { previewImage: c } : {},
|
|
@@ -701,44 +701,44 @@ function sn(e, t = []) {
|
|
|
701
701
|
}
|
|
702
702
|
//#endregion
|
|
703
703
|
//#region src/utils/saveBoardVisibility.ts
|
|
704
|
-
function
|
|
704
|
+
function hn(e) {
|
|
705
705
|
return e === "hidden" ? "hidden" : "public";
|
|
706
706
|
}
|
|
707
|
-
async function
|
|
707
|
+
async function gn(e, t, n, r) {
|
|
708
708
|
let i;
|
|
709
709
|
for (;;) {
|
|
710
|
-
let a = await
|
|
710
|
+
let a = await V(K(t, X("boardId", "==", n), G("__name__"), ...i ? [J(i)] : [], U(400)));
|
|
711
711
|
if (a.empty) break;
|
|
712
|
-
let o =
|
|
712
|
+
let o = Oe(e), s = 0;
|
|
713
713
|
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
714
714
|
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
715
715
|
}
|
|
716
716
|
}
|
|
717
|
-
async function
|
|
718
|
-
await
|
|
717
|
+
async function _n(e, t, n, r) {
|
|
718
|
+
await gn(e, o(e, t), n, r), await gn(e, g(e, t), n, r);
|
|
719
719
|
}
|
|
720
|
-
async function
|
|
721
|
-
let i = w(e, t, n), a =
|
|
722
|
-
await
|
|
720
|
+
async function vn(e, t, n, r) {
|
|
721
|
+
let i = w(e, t, n), a = hn(r.visibility);
|
|
722
|
+
await Ee(e, async (e) => {
|
|
723
723
|
let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
|
|
724
724
|
if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
|
|
725
|
-
return !t.exists() || !o &&
|
|
725
|
+
return !t.exists() || !o && hn(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
|
|
726
726
|
...r,
|
|
727
|
-
visibility: a === "public" ? "public" :
|
|
727
|
+
visibility: a === "public" ? "public" : hn(n?.visibility),
|
|
728
728
|
visibilityTransition: a
|
|
729
729
|
}, { merge: !0 }), !0);
|
|
730
|
-
}) && (await
|
|
730
|
+
}) && (await _n(e, t, n, a !== "hidden"), await Ee(e, async (e) => {
|
|
731
731
|
if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
|
|
732
732
|
e.update(i, {
|
|
733
733
|
visibility: a,
|
|
734
734
|
visibilityTransition: "",
|
|
735
|
-
updatedAt:
|
|
735
|
+
updatedAt: q()
|
|
736
736
|
});
|
|
737
737
|
}));
|
|
738
738
|
}
|
|
739
739
|
//#endregion
|
|
740
740
|
//#region src/utils/moderation-prompt.ts
|
|
741
|
-
var
|
|
741
|
+
var yn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", bn = {
|
|
742
742
|
ko: "Korean",
|
|
743
743
|
en: "English",
|
|
744
744
|
ja: "Japanese",
|
|
@@ -750,12 +750,12 @@ var fn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
750
750
|
ar: "Arabic",
|
|
751
751
|
id: "Indonesian"
|
|
752
752
|
};
|
|
753
|
-
function
|
|
754
|
-
let t =
|
|
753
|
+
function xn(e) {
|
|
754
|
+
let t = bn[e] ?? "English", n = pe(e).name;
|
|
755
755
|
return t === n ? t : `${t} (${n})`;
|
|
756
756
|
}
|
|
757
|
-
function
|
|
758
|
-
let t =
|
|
757
|
+
function Sn(e) {
|
|
758
|
+
let t = xn(e);
|
|
759
759
|
return `You are a strict moderator reviewing content for a message board.
|
|
760
760
|
If the text below contains profanity, vulgar slang, sexual harassment, hate speech, threats, spam, or repetitive/flooding advertisements, you MUST reject it (flagged=true).
|
|
761
761
|
|
|
@@ -782,17 +782,17 @@ Output JSON only:
|
|
|
782
782
|
Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
|
|
783
783
|
- If flagged=false, category is "none" and reason is ""`;
|
|
784
784
|
}
|
|
785
|
-
function
|
|
786
|
-
return e === "ko" ?
|
|
785
|
+
function Cn(e = "ko") {
|
|
786
|
+
return e === "ko" ? yn : Sn(e);
|
|
787
787
|
}
|
|
788
|
-
function
|
|
788
|
+
function wn(e, t = "ko") {
|
|
789
789
|
let n = `"""${e.slice(0, 4e3)}"""`;
|
|
790
790
|
return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
|
|
791
791
|
}
|
|
792
|
-
function
|
|
792
|
+
function Tn(e = "ko") {
|
|
793
793
|
return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
|
|
794
794
|
}
|
|
795
|
-
var
|
|
795
|
+
var En = {
|
|
796
796
|
abuse: "욕설·비속어·모욕",
|
|
797
797
|
spam: "스팸·광고",
|
|
798
798
|
adult: "선정적·성인 내용",
|
|
@@ -800,10 +800,10 @@ var yn = {
|
|
|
800
800
|
other: "부적절한 내용",
|
|
801
801
|
none: ""
|
|
802
802
|
};
|
|
803
|
-
function
|
|
804
|
-
return !e || e === "none" ? "" : e in
|
|
803
|
+
function Dn(e, t = "ko") {
|
|
804
|
+
return !e || e === "none" ? "" : e in En ? k(t, `moderationPrompt.categories.${e}`) : e;
|
|
805
805
|
}
|
|
806
|
-
function
|
|
806
|
+
function On(e) {
|
|
807
807
|
let t = e.locale ?? "ko";
|
|
808
808
|
if (e.via === "local" && e.localHit) {
|
|
809
809
|
let n = e.localHit.trim();
|
|
@@ -811,10 +811,10 @@ function xn(e) {
|
|
|
811
811
|
}
|
|
812
812
|
let n = (e.reason || "").trim();
|
|
813
813
|
if (n) return n;
|
|
814
|
-
let r = e.category ?
|
|
814
|
+
let r = e.category ? Dn(e.category, t) : "";
|
|
815
815
|
return r ? k(t, "moderationPrompt.categoryBlocked", { category: r }) : k(t, "moderationPrompt.genericBlocked");
|
|
816
816
|
}
|
|
817
|
-
var
|
|
817
|
+
var kn = [
|
|
818
818
|
"씨발",
|
|
819
819
|
"시발",
|
|
820
820
|
"시이발",
|
|
@@ -840,10 +840,10 @@ var Sn = [
|
|
|
840
840
|
"bitch",
|
|
841
841
|
"asshole"
|
|
842
842
|
];
|
|
843
|
-
function
|
|
843
|
+
function An(e) {
|
|
844
844
|
return new RegExp(e.join("|"), "i");
|
|
845
845
|
}
|
|
846
|
-
var
|
|
846
|
+
var jn = /* @__PURE__ */ new Set([
|
|
847
847
|
"none",
|
|
848
848
|
"abuse",
|
|
849
849
|
"spam",
|
|
@@ -851,7 +851,7 @@ var wn = /* @__PURE__ */ new Set([
|
|
|
851
851
|
"violence",
|
|
852
852
|
"other"
|
|
853
853
|
]);
|
|
854
|
-
function
|
|
854
|
+
function Mn(e) {
|
|
855
855
|
let t = e.trim();
|
|
856
856
|
if (!t) return null;
|
|
857
857
|
let n;
|
|
@@ -869,7 +869,7 @@ function Tn(e) {
|
|
|
869
869
|
if (typeof n != "object" || !n) return null;
|
|
870
870
|
let r = n;
|
|
871
871
|
if (typeof r.flagged != "boolean") return null;
|
|
872
|
-
let i = typeof r.category == "string" &&
|
|
872
|
+
let i = typeof r.category == "string" && jn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
873
873
|
return {
|
|
874
874
|
flagged: r.flagged,
|
|
875
875
|
category: i,
|
|
@@ -878,11 +878,11 @@ function Tn(e) {
|
|
|
878
878
|
}
|
|
879
879
|
//#endregion
|
|
880
880
|
//#region src/composables/useMemiBoardAutoTranslate.ts
|
|
881
|
-
var
|
|
882
|
-
function
|
|
881
|
+
var Nn = 3e4, Pn = 45e3, Fn = 12e3, In = 3;
|
|
882
|
+
function Ln(e) {
|
|
883
883
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
884
884
|
}
|
|
885
|
-
async function
|
|
885
|
+
async function Rn(e, t, n) {
|
|
886
886
|
let r = Array(e.length), i = 0;
|
|
887
887
|
async function a() {
|
|
888
888
|
for (; i < e.length;) {
|
|
@@ -892,7 +892,7 @@ async function jn(e, t, n) {
|
|
|
892
892
|
}
|
|
893
893
|
return await Promise.all(Array.from({ length: Math.min(t, e.length) }, () => a())), r;
|
|
894
894
|
}
|
|
895
|
-
function
|
|
895
|
+
function zn(e, t) {
|
|
896
896
|
return new Promise((n, r) => {
|
|
897
897
|
let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
|
|
898
898
|
e.then((e) => {
|
|
@@ -902,14 +902,14 @@ function Mn(e, t) {
|
|
|
902
902
|
});
|
|
903
903
|
});
|
|
904
904
|
}
|
|
905
|
-
function
|
|
906
|
-
let e =
|
|
905
|
+
function Bn() {
|
|
906
|
+
let e = Me(), { moderation: t } = v();
|
|
907
907
|
function n(n, r) {
|
|
908
|
-
let i =
|
|
909
|
-
backend: new
|
|
908
|
+
let i = Xe(e, {
|
|
909
|
+
backend: new Je(),
|
|
910
910
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
911
|
-
}), a =
|
|
912
|
-
return
|
|
911
|
+
}), a = Ye.object({ properties: Object.fromEntries(r.map((e) => [e, Ye.string()])) });
|
|
912
|
+
return Ze(i, {
|
|
913
913
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
914
914
|
systemInstruction: [
|
|
915
915
|
"You translate short user-interface strings for a website.",
|
|
@@ -921,19 +921,19 @@ function Nn() {
|
|
|
921
921
|
temperature: .2,
|
|
922
922
|
maxOutputTokens: 2048,
|
|
923
923
|
responseMimeType: "application/json",
|
|
924
|
-
responseSchema:
|
|
924
|
+
responseSchema: Ye.object({ properties: Object.fromEntries(n.map((e) => [e, a])) })
|
|
925
925
|
}
|
|
926
926
|
});
|
|
927
927
|
}
|
|
928
928
|
async function r(e, t, r, i) {
|
|
929
929
|
let a = Object.keys(e.fields), o = [
|
|
930
930
|
`Purpose: ${i}`,
|
|
931
|
-
`Source language: ${
|
|
932
|
-
`Translate into: ${r.map((e) => `${e} = ${
|
|
931
|
+
`Source language: ${xn(t)} (${t})`,
|
|
932
|
+
`Translate into: ${r.map((e) => `${e} = ${xn(e)}`).join(", ")}`,
|
|
933
933
|
e.context ? `Context (do not translate): ${e.context}` : "",
|
|
934
934
|
"Text (JSON):",
|
|
935
935
|
JSON.stringify(e.fields)
|
|
936
|
-
].filter(Boolean).join("\n"), s = await
|
|
936
|
+
].filter(Boolean).join("\n"), s = await zn(n(r, a).generateContent(o), Nn), c = JSON.parse(s.response.text()), l = {};
|
|
937
937
|
for (let e of r) {
|
|
938
938
|
let t = c?.[e];
|
|
939
939
|
if (!t) continue;
|
|
@@ -957,11 +957,11 @@ function Nn() {
|
|
|
957
957
|
return Object.fromEntries(o.map((e, t) => [e.id, s[t]]));
|
|
958
958
|
}
|
|
959
959
|
function a() {
|
|
960
|
-
let n =
|
|
961
|
-
backend: new
|
|
960
|
+
let n = Xe(e, {
|
|
961
|
+
backend: new Je(),
|
|
962
962
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
963
963
|
});
|
|
964
|
-
return
|
|
964
|
+
return Ze(n, {
|
|
965
965
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
966
966
|
systemInstruction: [
|
|
967
967
|
"You translate an HTML fragment of a message-board post.",
|
|
@@ -979,14 +979,14 @@ function Nn() {
|
|
|
979
979
|
D();
|
|
980
980
|
let n = t.source ?? "ko", r = (t.targets ?? T.map((e) => e.code)).filter((e) => e !== n), i = e.trim();
|
|
981
981
|
if (!i || !r.length) return {};
|
|
982
|
-
if (i.length > 12e3) throw Error(`translation is limited to ${
|
|
983
|
-
let o = await
|
|
982
|
+
if (i.length > 12e3) throw Error(`translation is limited to ${Fn} characters`);
|
|
983
|
+
let o = await Rn(r, In, async (e) => {
|
|
984
984
|
let t = [
|
|
985
|
-
`Source language: ${
|
|
986
|
-
`Translate into: ${e} = ${
|
|
985
|
+
`Source language: ${xn(n)} (${n})`,
|
|
986
|
+
`Translate into: ${e} = ${xn(e)}`,
|
|
987
987
|
"HTML:",
|
|
988
988
|
i
|
|
989
|
-
].join("\n"), r =
|
|
989
|
+
].join("\n"), r = Ln((await zn(a().generateContent(t), Pn)).response.text());
|
|
990
990
|
if (!r) throw Error(`empty translation for ${e}`);
|
|
991
991
|
return [e, r];
|
|
992
992
|
});
|
|
@@ -999,7 +999,7 @@ function Nn() {
|
|
|
999
999
|
}
|
|
1000
1000
|
//#endregion
|
|
1001
1001
|
//#region src/composables/useMemiBoardSettings.ts
|
|
1002
|
-
var
|
|
1002
|
+
var Vn = [
|
|
1003
1003
|
{
|
|
1004
1004
|
id: "free",
|
|
1005
1005
|
label: "자유",
|
|
@@ -1027,17 +1027,17 @@ var Pn = [
|
|
|
1027
1027
|
visibility: "public",
|
|
1028
1028
|
order: 2
|
|
1029
1029
|
}
|
|
1030
|
-
],
|
|
1031
|
-
function
|
|
1030
|
+
], Hn = Vn;
|
|
1031
|
+
function Un(e) {
|
|
1032
1032
|
return e === "hidden" ? "hidden" : "public";
|
|
1033
1033
|
}
|
|
1034
|
-
function
|
|
1034
|
+
function Wn(e, t, n) {
|
|
1035
1035
|
return {
|
|
1036
1036
|
id: e,
|
|
1037
1037
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
1038
1038
|
order: typeof t.order == "number" ? t.order : n,
|
|
1039
1039
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
1040
|
-
visibility:
|
|
1040
|
+
visibility: Un(t.visibility),
|
|
1041
1041
|
visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
|
|
1042
1042
|
listView: t.listView ?? "default",
|
|
1043
1043
|
editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
|
|
@@ -1047,22 +1047,23 @@ function Ln(e, t, n) {
|
|
|
1047
1047
|
allowedManagerUids: Array.isArray(t.allowedManagerUids) ? t.allowedManagerUids : [],
|
|
1048
1048
|
locale: M(t.locale) ?? "",
|
|
1049
1049
|
autoTranslate: t.autoTranslate === !0,
|
|
1050
|
+
autoTranslatePosts: t.autoTranslatePosts === !0,
|
|
1050
1051
|
i18n: Q(t.i18n),
|
|
1051
|
-
i18nSource:
|
|
1052
|
+
i18nSource: ut(t.i18nSource)
|
|
1052
1053
|
};
|
|
1053
1054
|
}
|
|
1054
|
-
function
|
|
1055
|
-
let e = () => j(), t = Z(), r =
|
|
1055
|
+
function Gn() {
|
|
1056
|
+
let e = () => j(), t = Z(), r = Me(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Vt(), { t: l, locale: u } = A(), { translateEntries: d } = Bn(), f = N(() => K(S(t, e()), G("order", "asc"))), { data: m, pending: h } = ke(f, { ssrKey: p(e(), "boards") }), g = N(() => m.value.map((e, t) => Wn(e.id, e, t))), _ = g, v = N(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
|
|
1056
1057
|
function b(e, t) {
|
|
1057
1058
|
if (!e) return;
|
|
1058
1059
|
let n = g.value.find((t) => t.id === e);
|
|
1059
|
-
return n ?
|
|
1060
|
+
return n ? ht(n.i18n, n.i18nSource, "label", n.label, t ?? u.value) || n.label : e;
|
|
1060
1061
|
}
|
|
1061
1062
|
let x = b;
|
|
1062
1063
|
function C(e, t) {
|
|
1063
1064
|
if (!e) return;
|
|
1064
1065
|
let n = g.value.find((t) => t.id === e), r = n?.description?.trim();
|
|
1065
|
-
if (n && r) return
|
|
1066
|
+
if (n && r) return ht(n.i18n, n.i18nSource, "description", r, t ?? u.value) || void 0;
|
|
1066
1067
|
}
|
|
1067
1068
|
let T = C;
|
|
1068
1069
|
function D(e) {
|
|
@@ -1076,16 +1077,16 @@ function Rn() {
|
|
|
1076
1077
|
function O(e) {
|
|
1077
1078
|
if (e) return g.value.find((t) => t.id === e);
|
|
1078
1079
|
}
|
|
1079
|
-
|
|
1080
|
+
At((e) => O(e));
|
|
1080
1081
|
function re(e) {
|
|
1081
|
-
return
|
|
1082
|
+
return Dt(c.value?.uid, a.value, s.value, e ? O(e) : void 0);
|
|
1082
1083
|
}
|
|
1083
1084
|
let ie = N(() => g.value.filter((e) => re(e.id)));
|
|
1084
1085
|
function ae(e, t) {
|
|
1085
1086
|
return {
|
|
1086
1087
|
label: e.label.trim(),
|
|
1087
1088
|
description: (e.description ?? "").trim(),
|
|
1088
|
-
visibility:
|
|
1089
|
+
visibility: Un(e.visibility),
|
|
1089
1090
|
listView: e.listView ?? "default",
|
|
1090
1091
|
editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
|
|
1091
1092
|
writeRole: e.writeRole ?? "user",
|
|
@@ -1094,17 +1095,18 @@ function Rn() {
|
|
|
1094
1095
|
allowedManagerUids: e.allowedManagerUids ?? [],
|
|
1095
1096
|
locale: M(e.locale) ?? "",
|
|
1096
1097
|
autoTranslate: e.autoTranslate === !0,
|
|
1098
|
+
autoTranslatePosts: e.autoTranslatePosts === !0,
|
|
1097
1099
|
...e.i18n ? { i18n: e.i18n } : {},
|
|
1098
1100
|
...e.i18nSource ? { i18nSource: e.i18nSource } : {},
|
|
1099
1101
|
order: t,
|
|
1100
|
-
updatedAt:
|
|
1102
|
+
updatedAt: q()
|
|
1101
1103
|
};
|
|
1102
1104
|
}
|
|
1103
1105
|
async function oe(e) {
|
|
1104
1106
|
if (!n()) return e;
|
|
1105
1107
|
let t = e.label.trim(), r = (e.description ?? "").trim();
|
|
1106
1108
|
if (!t) return e;
|
|
1107
|
-
if (
|
|
1109
|
+
if (yt({
|
|
1108
1110
|
...e,
|
|
1109
1111
|
label: t,
|
|
1110
1112
|
description: r
|
|
@@ -1115,7 +1117,7 @@ function Rn() {
|
|
|
1115
1117
|
};
|
|
1116
1118
|
let i = u.value, a = { label: t };
|
|
1117
1119
|
r && (a.description = r);
|
|
1118
|
-
let o =
|
|
1120
|
+
let o = ft(i, a, (await d([{
|
|
1119
1121
|
id: e.id || "board",
|
|
1120
1122
|
fields: a,
|
|
1121
1123
|
context: e.id
|
|
@@ -1123,7 +1125,7 @@ function Rn() {
|
|
|
1123
1125
|
source: i,
|
|
1124
1126
|
purpose: "board name and short description shown in navigation and page headers"
|
|
1125
1127
|
}))[e.id || "board"]);
|
|
1126
|
-
if (!
|
|
1128
|
+
if (!pt(o, Object.keys(a))) throw Error(l("settings.autoTranslate.failed"));
|
|
1127
1129
|
return {
|
|
1128
1130
|
...e,
|
|
1129
1131
|
label: t,
|
|
@@ -1140,12 +1142,12 @@ function Rn() {
|
|
|
1140
1142
|
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
1141
1143
|
let a = n.id.trim(), o = n.label.trim();
|
|
1142
1144
|
if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
|
|
1143
|
-
let s =
|
|
1145
|
+
let s = Un(n.visibility), c = ae({
|
|
1144
1146
|
...n,
|
|
1145
1147
|
label: o,
|
|
1146
1148
|
visibility: s
|
|
1147
1149
|
}, r);
|
|
1148
|
-
await
|
|
1150
|
+
await vn(t, e(), a, c);
|
|
1149
1151
|
}
|
|
1150
1152
|
let se = k;
|
|
1151
1153
|
async function ce(e) {
|
|
@@ -1159,11 +1161,11 @@ function Rn() {
|
|
|
1159
1161
|
let a = n.trim();
|
|
1160
1162
|
if (!a) throw Error(l("boardSettings.deleteIdRequired"));
|
|
1161
1163
|
for (;;) {
|
|
1162
|
-
let n = await
|
|
1164
|
+
let n = await V(K(o(t, e()), X("boardId", "==", a), U(25)));
|
|
1163
1165
|
if (n.empty) break;
|
|
1164
|
-
for (let i of n.docs) await
|
|
1166
|
+
for (let i of n.docs) await an(t, We(r), e(), i.id);
|
|
1165
1167
|
}
|
|
1166
|
-
await
|
|
1168
|
+
await ye(w(t, e(), n));
|
|
1167
1169
|
}
|
|
1168
1170
|
let de = ue;
|
|
1169
1171
|
async function fe() {
|
|
@@ -1215,34 +1217,34 @@ function Rn() {
|
|
|
1215
1217
|
saveCategories: le,
|
|
1216
1218
|
deleteBoard: ue,
|
|
1217
1219
|
deleteCategory: de,
|
|
1218
|
-
DEFAULT_BOARDS:
|
|
1219
|
-
DEFAULT_CATEGORIES:
|
|
1220
|
+
DEFAULT_BOARDS: Vn,
|
|
1221
|
+
DEFAULT_CATEGORIES: Hn
|
|
1220
1222
|
};
|
|
1221
1223
|
}
|
|
1222
1224
|
//#endregion
|
|
1223
1225
|
//#region src/composables/useMemiBoardPosts.ts
|
|
1224
|
-
var
|
|
1225
|
-
function
|
|
1226
|
+
var Kn = /* @__PURE__ */ new Map();
|
|
1227
|
+
function qn(e) {
|
|
1226
1228
|
if (!e || typeof e != "object") return !1;
|
|
1227
1229
|
let t = "code" in e ? String(e.code) : "";
|
|
1228
1230
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
1229
1231
|
}
|
|
1230
|
-
function
|
|
1232
|
+
function Jn(e, t) {
|
|
1231
1233
|
let n = I(e)?.trim();
|
|
1232
1234
|
if (!n) throw Error(k(t, "posts.boardIdRequired"));
|
|
1233
1235
|
return n;
|
|
1234
1236
|
}
|
|
1235
|
-
function
|
|
1236
|
-
let t = Z(), n =
|
|
1237
|
+
function Yn(e) {
|
|
1238
|
+
let t = Z(), n = Me(), { isBoardHidden: r, getBoard: i } = Gn(), { user: a, canManageBoard: c } = Vt(), { t: l, locale: u } = A(), d = () => j(), f = () => Jn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
|
|
1237
1239
|
function g(e, t, n) {
|
|
1238
1240
|
return {
|
|
1239
1241
|
id: e,
|
|
1240
1242
|
...t,
|
|
1241
1243
|
i18n: Q(t.i18n),
|
|
1242
|
-
i18nSource:
|
|
1244
|
+
i18nSource: ut(t.i18nSource),
|
|
1243
1245
|
content: typeof n?.content == "string" ? n.content : "",
|
|
1244
1246
|
contentI18n: Q(n?.i18n),
|
|
1245
|
-
contentI18nSource:
|
|
1247
|
+
contentI18nSource: dt(n?.i18nSource)
|
|
1246
1248
|
};
|
|
1247
1249
|
}
|
|
1248
1250
|
function _() {
|
|
@@ -1252,7 +1254,7 @@ function Hn(e) {
|
|
|
1252
1254
|
return i(f())?.editorType === "image";
|
|
1253
1255
|
}
|
|
1254
1256
|
function b(e) {
|
|
1255
|
-
let t = v(), n =
|
|
1257
|
+
let t = v(), n = cn(e.content), r = ln(e.content, e.attachments);
|
|
1256
1258
|
if (t) {
|
|
1257
1259
|
if (!r) throw Error(l("posts.photoRequired"));
|
|
1258
1260
|
return;
|
|
@@ -1261,15 +1263,15 @@ function Hn(e) {
|
|
|
1261
1263
|
if (!e.title?.trim()) throw Error(l("posts.titleRequired"));
|
|
1262
1264
|
}
|
|
1263
1265
|
function x() {
|
|
1264
|
-
return
|
|
1266
|
+
return R(p()).id;
|
|
1265
1267
|
}
|
|
1266
1268
|
function S(e) {
|
|
1267
|
-
return
|
|
1269
|
+
return Kt(t, d(), f(), e, a.value?.uid, c(f()));
|
|
1268
1270
|
}
|
|
1269
1271
|
async function C(e = {}) {
|
|
1270
1272
|
let t = e.pageSize ?? 20, n = [X("boardId", "==", f()), X("isPublished", "==", !0)];
|
|
1271
|
-
e.publicOnly !== !1 && n.push(X("listed", "==", !0)), n.push(
|
|
1272
|
-
let r = await
|
|
1273
|
+
e.publicOnly !== !1 && n.push(X("listed", "==", !0)), n.push(G("createdAt", "desc")), e.cursor && n.push(J(e.cursor)), n.push(U(t + 1));
|
|
1274
|
+
let r = await V(K(p(), ...n)), i = r.docs.slice(0, t);
|
|
1273
1275
|
return {
|
|
1274
1276
|
posts: i.map((e) => ({
|
|
1275
1277
|
id: e.id,
|
|
@@ -1280,7 +1282,7 @@ function Hn(e) {
|
|
|
1280
1282
|
};
|
|
1281
1283
|
}
|
|
1282
1284
|
async function w(e) {
|
|
1283
|
-
let [t, n] = await Promise.all([
|
|
1285
|
+
let [t, n] = await Promise.all([B(m(e)), B(h(e))]);
|
|
1284
1286
|
return t.exists() ? g(t.id, t.data(), n.exists() ? n.data() : void 0) : null;
|
|
1285
1287
|
}
|
|
1286
1288
|
async function T(e) {
|
|
@@ -1294,20 +1296,20 @@ function Hn(e) {
|
|
|
1294
1296
|
post: e
|
|
1295
1297
|
} : { status: "not-found" };
|
|
1296
1298
|
} catch (e) {
|
|
1297
|
-
return
|
|
1299
|
+
return qn(e) ? { status: "permission-denied" } : {
|
|
1298
1300
|
status: "error",
|
|
1299
1301
|
message: e instanceof Error ? e.message : String(e)
|
|
1300
1302
|
};
|
|
1301
1303
|
}
|
|
1302
1304
|
} catch (e) {
|
|
1303
|
-
return
|
|
1305
|
+
return qn(e) ? { status: "permission-denied" } : {
|
|
1304
1306
|
status: "error",
|
|
1305
1307
|
message: e instanceof Error ? e.message : String(e)
|
|
1306
1308
|
};
|
|
1307
1309
|
}
|
|
1308
1310
|
}
|
|
1309
1311
|
async function ee(e) {
|
|
1310
|
-
let t = e.trim(), n =
|
|
1312
|
+
let t = e.trim(), n = Kn.get(`${f()}/${t}`);
|
|
1311
1313
|
if (n) return {
|
|
1312
1314
|
status: "ok",
|
|
1313
1315
|
id: n
|
|
@@ -1319,7 +1321,7 @@ function Hn(e) {
|
|
|
1319
1321
|
id: e.id
|
|
1320
1322
|
} : { status: r(f()) ? "permission-denied" : "not-found" };
|
|
1321
1323
|
} catch (e) {
|
|
1322
|
-
return
|
|
1324
|
+
return qn(e) ? { status: "permission-denied" } : {
|
|
1323
1325
|
status: "error",
|
|
1324
1326
|
message: e instanceof Error ? e.message : String(e)
|
|
1325
1327
|
};
|
|
@@ -1338,8 +1340,8 @@ function Hn(e) {
|
|
|
1338
1340
|
X("boardId", "==", f()),
|
|
1339
1341
|
X("isPublished", "==", !0),
|
|
1340
1342
|
...e.listed === !1 ? [] : [X("listed", "==", !0)],
|
|
1341
|
-
|
|
1342
|
-
], [n, r] = await Promise.all([
|
|
1343
|
+
G("createdAt", "desc")
|
|
1344
|
+
], [n, r] = await Promise.all([V(K(p(), ...t, J(e.createdAt), U(1))), V(K(p(), ...t, Se(e.createdAt), Te(1)))]), i = (e) => {
|
|
1343
1345
|
let t = e.docs[0];
|
|
1344
1346
|
return t ? {
|
|
1345
1347
|
id: t.id,
|
|
@@ -1360,8 +1362,8 @@ function Hn(e) {
|
|
|
1360
1362
|
async function O(e) {
|
|
1361
1363
|
b(e);
|
|
1362
1364
|
let n = e.postId || x(), r = v(), i, a;
|
|
1363
|
-
r ? (i = n, a =
|
|
1364
|
-
let o =
|
|
1365
|
+
r ? (i = n, a = sn(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await te(a));
|
|
1366
|
+
let o = mn(e.content, e.attachments), s = _(), c = Oe(t);
|
|
1365
1367
|
return c.set(m(n), {
|
|
1366
1368
|
boardId: f(),
|
|
1367
1369
|
slug: i,
|
|
@@ -1379,8 +1381,8 @@ function Hn(e) {
|
|
|
1379
1381
|
authorPhoto: e.authorPhoto,
|
|
1380
1382
|
moderationStatus: "approved",
|
|
1381
1383
|
moderationModel: e.moderationModel ?? null,
|
|
1382
|
-
createdAt:
|
|
1383
|
-
updatedAt:
|
|
1384
|
+
createdAt: q(),
|
|
1385
|
+
updatedAt: q(),
|
|
1384
1386
|
...e.translations ? {
|
|
1385
1387
|
i18n: e.translations.i18n,
|
|
1386
1388
|
i18nSource: e.translations.i18nSource
|
|
@@ -1391,32 +1393,32 @@ function Hn(e) {
|
|
|
1391
1393
|
i18n: e.translations.contentI18n,
|
|
1392
1394
|
i18nSource: e.translations.contentI18nSource
|
|
1393
1395
|
} : {}
|
|
1394
|
-
}), await c.commit(),
|
|
1396
|
+
}), await c.commit(), Kn.set(`${f()}/${i}`, n), i;
|
|
1395
1397
|
}
|
|
1396
1398
|
async function re(e, t) {
|
|
1397
1399
|
b(t);
|
|
1398
|
-
let n = v() ?
|
|
1400
|
+
let n = v() ? sn(t.content) : t.title.trim(), r = mn(t.content, t.attachments), i = _();
|
|
1399
1401
|
await Promise.all([Y(m(e), {
|
|
1400
1402
|
title: n,
|
|
1401
1403
|
...r,
|
|
1402
|
-
previewImage: r.previewImage ? r.previewImage :
|
|
1403
|
-
videoUrl: r.videoUrl ? r.videoUrl :
|
|
1404
|
+
previewImage: r.previewImage ? r.previewImage : be(),
|
|
1405
|
+
videoUrl: r.videoUrl ? r.videoUrl : be(),
|
|
1404
1406
|
tags: t.tags ?? [],
|
|
1405
1407
|
listed: i,
|
|
1406
1408
|
attachments: t.attachments ?? [],
|
|
1407
|
-
updatedAt:
|
|
1409
|
+
updatedAt: q(),
|
|
1408
1410
|
...t.translations ? {
|
|
1409
1411
|
i18n: t.translations.i18n,
|
|
1410
1412
|
i18nSource: t.translations.i18nSource
|
|
1411
1413
|
} : {}
|
|
1412
|
-
}),
|
|
1414
|
+
}), De(h(e), {
|
|
1413
1415
|
content: t.content,
|
|
1414
1416
|
...t.translations ? t.translations.contentI18n ? {
|
|
1415
1417
|
i18n: t.translations.contentI18n,
|
|
1416
1418
|
i18nSource: t.translations.contentI18nSource
|
|
1417
1419
|
} : {
|
|
1418
|
-
i18n:
|
|
1419
|
-
i18nSource:
|
|
1420
|
+
i18n: be(),
|
|
1421
|
+
i18nSource: be()
|
|
1420
1422
|
} : {}
|
|
1421
1423
|
}, { merge: !0 })]);
|
|
1422
1424
|
}
|
|
@@ -1424,25 +1426,25 @@ function Hn(e) {
|
|
|
1424
1426
|
D(), await Promise.all([Y(m(e), {
|
|
1425
1427
|
i18n: t.i18n,
|
|
1426
1428
|
i18nSource: t.i18nSource,
|
|
1427
|
-
updatedAt:
|
|
1428
|
-
}),
|
|
1429
|
+
updatedAt: q()
|
|
1430
|
+
}), De(h(e), t.contentI18n ? {
|
|
1429
1431
|
i18n: t.contentI18n,
|
|
1430
1432
|
i18nSource: t.contentI18nSource
|
|
1431
1433
|
} : {
|
|
1432
|
-
i18n:
|
|
1433
|
-
i18nSource:
|
|
1434
|
+
i18n: be(),
|
|
1435
|
+
i18nSource: be()
|
|
1434
1436
|
}, { merge: !0 })]);
|
|
1435
1437
|
}
|
|
1436
1438
|
async function ae(e) {
|
|
1437
1439
|
await Y(m(e), {
|
|
1438
1440
|
isPublished: !0,
|
|
1439
|
-
publishedAt:
|
|
1440
|
-
updatedAt:
|
|
1441
|
+
publishedAt: q(),
|
|
1442
|
+
updatedAt: q()
|
|
1441
1443
|
});
|
|
1442
1444
|
}
|
|
1443
1445
|
async function oe(e) {
|
|
1444
|
-
await
|
|
1445
|
-
for (let [t, n] of
|
|
1446
|
+
await an(t, We(n), d(), e);
|
|
1447
|
+
for (let [t, n] of Kn) n === e && Kn.delete(t);
|
|
1446
1448
|
}
|
|
1447
1449
|
return {
|
|
1448
1450
|
createPostId: x,
|
|
@@ -1459,9 +1461,9 @@ function Hn(e) {
|
|
|
1459
1461
|
deletePost: oe
|
|
1460
1462
|
};
|
|
1461
1463
|
}
|
|
1462
|
-
var
|
|
1463
|
-
function
|
|
1464
|
-
let n = Z(), { isAdmin: r, user: i, canManageBoard: a } =
|
|
1464
|
+
var Xn = 10;
|
|
1465
|
+
function Zn(e, t = {}) {
|
|
1466
|
+
let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Vt(), s = () => j(), c = t.pageSize ?? Xn, l = N(() => i.value?.uid ?? ""), u = N(() => Jn(e)), d = () => o(n, s()), f = N(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = F([]), m = F([]), h = F([]), g = F(!0), _ = F(!0), v = F(!1), y = F(""), b = null, x = null, S, C;
|
|
1465
1467
|
function w(e) {
|
|
1466
1468
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1467
1469
|
}
|
|
@@ -1479,7 +1481,7 @@ function Wn(e, t = {}) {
|
|
|
1479
1481
|
return f.value && e.push(X("listed", "==", !0)), e;
|
|
1480
1482
|
}
|
|
1481
1483
|
function D() {
|
|
1482
|
-
S?.(), S =
|
|
1484
|
+
S?.(), S = W(K(d(), ...E(), G("createdAt", "desc"), G(z(), "desc"), U(c)), (e) => {
|
|
1483
1485
|
p.value = e.docs.map((e) => ({
|
|
1484
1486
|
id: e.id,
|
|
1485
1487
|
...e.data()
|
|
@@ -1495,7 +1497,7 @@ function Wn(e, t = {}) {
|
|
|
1495
1497
|
h.value = [];
|
|
1496
1498
|
return;
|
|
1497
1499
|
}
|
|
1498
|
-
C =
|
|
1500
|
+
C = W(K(d(), X("boardId", "==", u.value), X("authorUid", "==", e), X("isPublished", "==", !1), G("createdAt", "desc"), U(20)), (e) => {
|
|
1499
1501
|
h.value = e.docs.map((e) => ({
|
|
1500
1502
|
id: e.id,
|
|
1501
1503
|
...e.data()
|
|
@@ -1510,11 +1512,11 @@ function Wn(e, t = {}) {
|
|
|
1510
1512
|
try {
|
|
1511
1513
|
let e = x ?? b, t = [
|
|
1512
1514
|
...E(),
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
+
G("createdAt", "desc"),
|
|
1516
|
+
G(z(), "desc"),
|
|
1515
1517
|
...e ? [J(e)] : [],
|
|
1516
|
-
|
|
1517
|
-
], n = await
|
|
1518
|
+
U(c + 1)
|
|
1519
|
+
], n = await V(K(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
|
|
1518
1520
|
id: e.id,
|
|
1519
1521
|
...e.data()
|
|
1520
1522
|
})), a = new Set([...m.value, ...p.value].map((e) => e.id));
|
|
@@ -1544,8 +1546,8 @@ function Wn(e, t = {}) {
|
|
|
1544
1546
|
loadMore: te
|
|
1545
1547
|
};
|
|
1546
1548
|
}
|
|
1547
|
-
function
|
|
1548
|
-
let n = Z(), r = () => j(), i = N(() =>
|
|
1549
|
+
function Qn(e, t) {
|
|
1550
|
+
let n = Z(), r = () => j(), i = N(() => Jn(e)), a = N(() => typeof t == "string" ? t : t.value), o = N(() => y(n, r(), a.value)), c = N(() => s(n, r(), a.value)), { data: l, pending: u } = je(o), { data: d, pending: f } = je(c);
|
|
1549
1551
|
return {
|
|
1550
1552
|
post: N(() => {
|
|
1551
1553
|
if (!l.value || l.value.boardId !== i.value) return null;
|
|
@@ -1554,10 +1556,10 @@ function Gn(e, t) {
|
|
|
1554
1556
|
...l.value,
|
|
1555
1557
|
id: a.value,
|
|
1556
1558
|
i18n: Q(l.value.i18n),
|
|
1557
|
-
i18nSource:
|
|
1559
|
+
i18nSource: ut(l.value.i18nSource),
|
|
1558
1560
|
content: d.value?.content ?? "",
|
|
1559
1561
|
contentI18n: Q(e?.i18n),
|
|
1560
|
-
contentI18nSource:
|
|
1562
|
+
contentI18nSource: dt(e?.i18nSource)
|
|
1561
1563
|
};
|
|
1562
1564
|
}),
|
|
1563
1565
|
pending: N(() => u.value || f.value)
|
|
@@ -1565,9 +1567,9 @@ function Gn(e, t) {
|
|
|
1565
1567
|
}
|
|
1566
1568
|
//#endregion
|
|
1567
1569
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
1568
|
-
var
|
|
1569
|
-
function
|
|
1570
|
-
let n = Z(), r = () => j(), i = () => o(n, r()), a = t.pageSize ??
|
|
1570
|
+
var $n = 10;
|
|
1571
|
+
function er(e, t = {}) {
|
|
1572
|
+
let n = Z(), r = () => j(), i = () => o(n, r()), a = t.pageSize ?? $n, s = N(() => I(e)?.trim() || ""), c = F([]), l = F([]), u = F(!0), d = F(!0), f = F(!1), p = F(""), m = null, h = null, g;
|
|
1571
1573
|
function _(e) {
|
|
1572
1574
|
return {
|
|
1573
1575
|
id: e.id,
|
|
@@ -1594,7 +1596,7 @@ function qn(e, t = {}) {
|
|
|
1594
1596
|
u.value = !1;
|
|
1595
1597
|
return;
|
|
1596
1598
|
}
|
|
1597
|
-
g =
|
|
1599
|
+
g = W(K(i(), ...b(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
|
|
1598
1600
|
c.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), u.value = !1;
|
|
1599
1601
|
}, (e) => {
|
|
1600
1602
|
console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
|
|
@@ -1606,11 +1608,11 @@ function qn(e, t = {}) {
|
|
|
1606
1608
|
try {
|
|
1607
1609
|
let e = h ?? m, t = [
|
|
1608
1610
|
...b(),
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
+
G("createdAt", "desc"),
|
|
1612
|
+
G(z(), "desc"),
|
|
1611
1613
|
...e ? [J(e)] : [],
|
|
1612
|
-
|
|
1613
|
-
], n = await
|
|
1614
|
+
U(a + 1)
|
|
1615
|
+
], n = await V(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), s = new Set([...l.value, ...c.value].map((e) => e.id));
|
|
1614
1616
|
l.value.push(...o.filter((e) => !s.has(e.id))), h = r.at(-1) ?? h, d.value = n.docs.length > a;
|
|
1615
1617
|
} catch (e) {
|
|
1616
1618
|
p.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
|
|
@@ -1633,9 +1635,9 @@ function qn(e, t = {}) {
|
|
|
1633
1635
|
}
|
|
1634
1636
|
//#endregion
|
|
1635
1637
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1636
|
-
var
|
|
1637
|
-
function
|
|
1638
|
-
let n = Z(), r = () => j(), i = () => g(n, r()), a = t.pageSize ??
|
|
1638
|
+
var tr = 10;
|
|
1639
|
+
function nr(e, t = {}) {
|
|
1640
|
+
let n = Z(), r = () => j(), i = () => g(n, r()), a = t.pageSize ?? tr, o = N(() => I(e)?.trim() || ""), s = F([]), c = F([]), l = F(!0), u = F(!0), d = F(!1), f = F(""), p = null, m = null, h;
|
|
1639
1641
|
function _(e) {
|
|
1640
1642
|
return {
|
|
1641
1643
|
id: e.id,
|
|
@@ -1658,7 +1660,7 @@ function Yn(e, t = {}) {
|
|
|
1658
1660
|
l.value = !1;
|
|
1659
1661
|
return;
|
|
1660
1662
|
}
|
|
1661
|
-
h =
|
|
1663
|
+
h = W(K(i(), ...b(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
|
|
1662
1664
|
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1663
1665
|
}, (e) => {
|
|
1664
1666
|
console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1670,11 +1672,11 @@ function Yn(e, t = {}) {
|
|
|
1670
1672
|
try {
|
|
1671
1673
|
let e = m ?? p, t = [
|
|
1672
1674
|
...b(),
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
+
G("createdAt", "desc"),
|
|
1676
|
+
G(z(), "desc"),
|
|
1675
1677
|
...e ? [J(e)] : [],
|
|
1676
|
-
|
|
1677
|
-
], n = await
|
|
1678
|
+
U(a + 1)
|
|
1679
|
+
], 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));
|
|
1678
1680
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1679
1681
|
} catch (e) {
|
|
1680
1682
|
f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
@@ -1697,11 +1699,11 @@ function Yn(e, t = {}) {
|
|
|
1697
1699
|
}
|
|
1698
1700
|
//#endregion
|
|
1699
1701
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1700
|
-
var
|
|
1701
|
-
function
|
|
1702
|
+
var rr = 10;
|
|
1703
|
+
function ir(e = "latest", t = {}) {
|
|
1702
1704
|
let n = Z(), r = () => j(), i = () => g(n, r()), a = N(() => {
|
|
1703
1705
|
let e = Math.floor(Number(I(t.pageSize)));
|
|
1704
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1706
|
+
return Number.isFinite(e) && e > 0 ? e : rr;
|
|
1705
1707
|
}), o = N(() => I(e) === "likes" ? "likes" : "latest"), s = F([]), c = F([]), l = F(!0), u = F(!0), d = F(!1), f = F(""), p = null, m = null, h;
|
|
1706
1708
|
function _(e) {
|
|
1707
1709
|
return {
|
|
@@ -1727,17 +1729,17 @@ function Zn(e = "latest", t = {}) {
|
|
|
1727
1729
|
function x() {
|
|
1728
1730
|
return o.value === "likes" ? [
|
|
1729
1731
|
X("listed", "==", !0),
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1732
|
+
G("likeCount", "desc"),
|
|
1733
|
+
G("createdAt", "desc"),
|
|
1734
|
+
G(z(), "desc")
|
|
1733
1735
|
] : [
|
|
1734
1736
|
X("listed", "==", !0),
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
+
G("createdAt", "desc"),
|
|
1738
|
+
G(z(), "desc")
|
|
1737
1739
|
];
|
|
1738
1740
|
}
|
|
1739
1741
|
function S() {
|
|
1740
|
-
h?.(), h =
|
|
1742
|
+
h?.(), h = W(K(i(), ...x(), U(a.value)), (e) => {
|
|
1741
1743
|
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
|
|
1742
1744
|
}, (e) => {
|
|
1743
1745
|
console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1747,7 +1749,7 @@ function Zn(e = "latest", t = {}) {
|
|
|
1747
1749
|
if (!(d.value || !u.value || l.value)) {
|
|
1748
1750
|
d.value = !0, f.value = "";
|
|
1749
1751
|
try {
|
|
1750
|
-
let e = m ?? p, t = await
|
|
1752
|
+
let e = m ?? p, t = await V(K(i(), ...x(), ...e ? [J(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));
|
|
1751
1753
|
c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
|
|
1752
1754
|
} catch (e) {
|
|
1753
1755
|
f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
@@ -1771,9 +1773,9 @@ function Zn(e = "latest", t = {}) {
|
|
|
1771
1773
|
}
|
|
1772
1774
|
//#endregion
|
|
1773
1775
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1774
|
-
var
|
|
1776
|
+
var ar = 100, or = /* @__PURE__ */ new Map();
|
|
1775
1777
|
function $(e) {
|
|
1776
|
-
let t =
|
|
1778
|
+
let t = or.get(e);
|
|
1777
1779
|
return t || (t = ge({
|
|
1778
1780
|
text: "",
|
|
1779
1781
|
sentiment: null,
|
|
@@ -1781,10 +1783,10 @@ function $(e) {
|
|
|
1781
1783
|
pending: !1,
|
|
1782
1784
|
saving: !1,
|
|
1783
1785
|
error: ""
|
|
1784
|
-
}),
|
|
1786
|
+
}), or.set(e, t)), t;
|
|
1785
1787
|
}
|
|
1786
|
-
function
|
|
1787
|
-
let t = Z(), n = () => j(), { user: r } =
|
|
1788
|
+
function sr(e) {
|
|
1789
|
+
let t = Z(), n = () => j(), { user: r } = Vt(), { t: i, formatNumber: a } = A(), o = N(() => I(e)), s = N(() => `${r.value?.uid ?? ""}:${o.value}`), c = N(() => {
|
|
1788
1790
|
let e = r.value?.uid, i = o.value;
|
|
1789
1791
|
return !e || !i ? null : f(t, n(), e, i);
|
|
1790
1792
|
}), l = N({
|
|
@@ -1799,7 +1801,7 @@ function er(e) {
|
|
|
1799
1801
|
let t = $(s.value);
|
|
1800
1802
|
t.pending = !0, t.error = "";
|
|
1801
1803
|
try {
|
|
1802
|
-
let n = (await
|
|
1804
|
+
let n = (await B(e)).data();
|
|
1803
1805
|
t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
|
|
1804
1806
|
} catch (e) {
|
|
1805
1807
|
t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
|
|
@@ -1815,10 +1817,10 @@ function er(e) {
|
|
|
1815
1817
|
let o = $(s.value);
|
|
1816
1818
|
o.saving = !0, o.error = "";
|
|
1817
1819
|
try {
|
|
1818
|
-
await
|
|
1820
|
+
await De(n, {
|
|
1819
1821
|
text: r,
|
|
1820
1822
|
sentiment: t,
|
|
1821
|
-
updatedAt:
|
|
1823
|
+
updatedAt: q()
|
|
1822
1824
|
}), o.text = r, o.sentiment = t, o.loaded = !0;
|
|
1823
1825
|
} catch (e) {
|
|
1824
1826
|
throw o.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
|
|
@@ -1832,7 +1834,7 @@ function er(e) {
|
|
|
1832
1834
|
let t = $(s.value);
|
|
1833
1835
|
t.saving = !0, t.error = "";
|
|
1834
1836
|
try {
|
|
1835
|
-
await
|
|
1837
|
+
await ye(e), t.text = "", t.sentiment = null, t.loaded = !0;
|
|
1836
1838
|
} catch (e) {
|
|
1837
1839
|
throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
|
|
1838
1840
|
} finally {
|
|
@@ -1853,23 +1855,23 @@ function er(e) {
|
|
|
1853
1855
|
}
|
|
1854
1856
|
//#endregion
|
|
1855
1857
|
//#region src/utils/subscribeCommentRange.ts
|
|
1856
|
-
function
|
|
1857
|
-
return
|
|
1858
|
+
function cr(e, t, n, r, i, a) {
|
|
1859
|
+
return W(K(g(e, t), X("postId", "==", n), ...r.rootId ? [X("rootId", "==", r.rootId), X("isReply", "==", !0)] : [X("parentId", "==", null)], G("createdAt", "asc"), G(z(), "asc"), ...r.hasMore && r.cursor ? [xe(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
|
|
1858
1860
|
...e.data(),
|
|
1859
1861
|
id: e.id
|
|
1860
1862
|
}))), a);
|
|
1861
1863
|
}
|
|
1862
1864
|
//#endregion
|
|
1863
1865
|
//#region src/composables/useMemiBoardComments.ts
|
|
1864
|
-
var
|
|
1865
|
-
function
|
|
1866
|
+
var lr = 10, ur = 5, dr = 1e3;
|
|
1867
|
+
function fr(e, { t, formatNumber: n }) {
|
|
1866
1868
|
let r = e.trim();
|
|
1867
1869
|
if (!r) throw Error(t("comments.bodyRequired"));
|
|
1868
|
-
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(
|
|
1870
|
+
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(dr) }));
|
|
1869
1871
|
return r;
|
|
1870
1872
|
}
|
|
1871
|
-
function
|
|
1872
|
-
let r = Z(), i =
|
|
1873
|
+
function pr(e, t, n = {}) {
|
|
1874
|
+
let r = Z(), i = Me(), a = () => j(), { isBoardHidden: o } = Gn(), s = A(), { t: c } = s, l = () => g(r, a()), u = N(() => I(e)), f = N(() => I(t)), p = () => !o(u.value), m = F([]), h = F(n.subscribe !== !1), _ = F([]), v = F(!1), b = F(n.subscribe !== !1), S = F(n.subscribe !== !1), C = null, w = 0, T = 0, E;
|
|
1873
1875
|
function D(e) {
|
|
1874
1876
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1875
1877
|
}
|
|
@@ -1883,7 +1885,7 @@ function or(e, t, n = {}) {
|
|
|
1883
1885
|
if (T++, v.value = !1, _.value = [], m.value = [], C = null, E?.(), E = void 0, b.value = n.subscribe !== !1, n.subscribe !== !1) {
|
|
1884
1886
|
h.value = !0, S.value = !0;
|
|
1885
1887
|
try {
|
|
1886
|
-
let n =
|
|
1888
|
+
let n = V(K(l(), X("postId", "==", e), G("createdAt", "desc"), G(z(), "desc")));
|
|
1887
1889
|
if (await Promise.all([n.then((e) => {
|
|
1888
1890
|
if (t !== w) return;
|
|
1889
1891
|
let n = e.docs.map((e) => ({
|
|
@@ -1900,7 +1902,7 @@ function or(e, t, n = {}) {
|
|
|
1900
1902
|
function te() {
|
|
1901
1903
|
E?.();
|
|
1902
1904
|
let e = ++T;
|
|
1903
|
-
E =
|
|
1905
|
+
E = cr(r, a(), f.value, {
|
|
1904
1906
|
cursor: C,
|
|
1905
1907
|
hasMore: b.value
|
|
1906
1908
|
}, (t) => {
|
|
@@ -1917,17 +1919,17 @@ function or(e, t, n = {}) {
|
|
|
1917
1919
|
let t = [
|
|
1918
1920
|
X("postId", "==", f.value),
|
|
1919
1921
|
X("parentId", "==", null),
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
+
G("createdAt", "asc"),
|
|
1923
|
+
G(z(), "asc"),
|
|
1922
1924
|
...C ? [J(C)] : [],
|
|
1923
|
-
|
|
1924
|
-
], n = await
|
|
1925
|
+
U(11)
|
|
1926
|
+
], n = await V(K(l(), ...t));
|
|
1925
1927
|
if (e !== w) return;
|
|
1926
|
-
let r = n.docs.slice(0,
|
|
1928
|
+
let r = n.docs.slice(0, lr), i = r.map((e) => ({
|
|
1927
1929
|
id: e.id,
|
|
1928
1930
|
...e.data()
|
|
1929
1931
|
})), a = new Set(_.value.map((e) => e.id));
|
|
1930
|
-
_.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size >
|
|
1932
|
+
_.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size > lr, te();
|
|
1931
1933
|
} finally {
|
|
1932
1934
|
e === w && (v.value = !1);
|
|
1933
1935
|
}
|
|
@@ -1936,10 +1938,10 @@ function or(e, t, n = {}) {
|
|
|
1936
1938
|
w++, T++, E?.();
|
|
1937
1939
|
});
|
|
1938
1940
|
function O() {
|
|
1939
|
-
return
|
|
1941
|
+
return R(l()).id;
|
|
1940
1942
|
}
|
|
1941
1943
|
function re(e) {
|
|
1942
|
-
if (!e) return
|
|
1944
|
+
if (!e) return R(l());
|
|
1943
1945
|
if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(c("commentForm.uploadFailed"));
|
|
1944
1946
|
return d(r, a(), e);
|
|
1945
1947
|
}
|
|
@@ -1953,25 +1955,25 @@ function or(e, t, n = {}) {
|
|
|
1953
1955
|
postId: f.value,
|
|
1954
1956
|
boardId: u.value,
|
|
1955
1957
|
listed: p(),
|
|
1956
|
-
body:
|
|
1958
|
+
body: fr(e.body, s),
|
|
1957
1959
|
authorUid: e.authorUid,
|
|
1958
1960
|
authorName: e.authorName,
|
|
1959
1961
|
authorPhoto: e.authorPhoto,
|
|
1960
1962
|
moderationStatus: "approved",
|
|
1961
1963
|
replyCount: 0,
|
|
1962
1964
|
likeCount: 0,
|
|
1963
|
-
createdAt:
|
|
1964
|
-
updatedAt:
|
|
1965
|
+
createdAt: q(),
|
|
1966
|
+
updatedAt: q(),
|
|
1965
1967
|
...n.length ? { images: n } : {}
|
|
1966
1968
|
};
|
|
1967
1969
|
}
|
|
1968
1970
|
async function oe(e) {
|
|
1969
1971
|
if (!e.length) return;
|
|
1970
|
-
let t =
|
|
1971
|
-
await Promise.all(e.map((e) =>
|
|
1972
|
+
let t = We(i);
|
|
1973
|
+
await Promise.all(e.map((e) => He(qe(t, e)).catch(() => {})));
|
|
1972
1974
|
}
|
|
1973
1975
|
async function k(e) {
|
|
1974
|
-
let t =
|
|
1976
|
+
let t = Oe(r), n = re(e.id);
|
|
1975
1977
|
t.set(n, {
|
|
1976
1978
|
...ae(e, n.id),
|
|
1977
1979
|
parentId: null,
|
|
@@ -1980,11 +1982,11 @@ function or(e, t, n = {}) {
|
|
|
1980
1982
|
replyToUid: null,
|
|
1981
1983
|
replyToName: null,
|
|
1982
1984
|
isReply: !1
|
|
1983
|
-
}), t.update(y(r, a(), f.value), { commentCount:
|
|
1985
|
+
}), t.update(y(r, a(), f.value), { commentCount: H(1) }), await t.commit();
|
|
1984
1986
|
}
|
|
1985
1987
|
async function se(e) {
|
|
1986
1988
|
if (!e.parent.id) throw Error(c("comments.replyTargetNotFound"));
|
|
1987
|
-
let t =
|
|
1989
|
+
let t = Oe(r), n = re(e.id), i = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1988
1990
|
if (!i) throw Error(c("comments.threadNotFound"));
|
|
1989
1991
|
let o = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1990
1992
|
t.set(n, {
|
|
@@ -1995,31 +1997,31 @@ function or(e, t, n = {}) {
|
|
|
1995
1997
|
replyToUid: e.parent.authorUid,
|
|
1996
1998
|
replyToName: e.parent.authorName,
|
|
1997
1999
|
isReply: !0
|
|
1998
|
-
}), t.update(d(r, a(), i), { replyCount:
|
|
2000
|
+
}), t.update(d(r, a(), i), { replyCount: H(1) }), t.update(y(r, a(), f.value), { commentCount: H(1) }), await t.commit();
|
|
1999
2001
|
}
|
|
2000
2002
|
async function ce(e) {
|
|
2001
2003
|
if (!e.id) return;
|
|
2002
2004
|
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(c("comments.hasRepliesCannotDelete"));
|
|
2003
2005
|
let t = de(e.images, `${x(a(), f.value)}/comment-images/${e.id}/`);
|
|
2004
|
-
await
|
|
2005
|
-
let n =
|
|
2006
|
-
n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount:
|
|
2006
|
+
await rn(r, a(), e.id);
|
|
2007
|
+
let n = Oe(r);
|
|
2008
|
+
n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount: H(-1) }), n.update(y(r, a(), f.value), { commentCount: H(-1) }), await n.commit(), await oe(t), _.value = _.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
|
|
2007
2009
|
}
|
|
2008
2010
|
async function le(e, t, n) {
|
|
2009
2011
|
let i = {
|
|
2010
|
-
body:
|
|
2011
|
-
updatedAt:
|
|
2012
|
+
body: fr(t, s),
|
|
2013
|
+
updatedAt: q()
|
|
2012
2014
|
};
|
|
2013
2015
|
if (n !== void 0) {
|
|
2014
2016
|
let t = ie(e, n);
|
|
2015
|
-
i.images = t.length ? t :
|
|
2017
|
+
i.images = t.length ? t : be();
|
|
2016
2018
|
}
|
|
2017
2019
|
await Y(d(r, a(), e), i);
|
|
2018
2020
|
}
|
|
2019
2021
|
async function fe(e, t, n) {
|
|
2020
2022
|
await Y(d(r, a(), e), {
|
|
2021
2023
|
isBlinded: t,
|
|
2022
|
-
moderatedAt:
|
|
2024
|
+
moderatedAt: q(),
|
|
2023
2025
|
moderatedBy: n
|
|
2024
2026
|
});
|
|
2025
2027
|
}
|
|
@@ -2037,7 +2039,7 @@ function or(e, t, n = {}) {
|
|
|
2037
2039
|
deleteComment: ce
|
|
2038
2040
|
};
|
|
2039
2041
|
}
|
|
2040
|
-
function
|
|
2042
|
+
function mr(e, t, n) {
|
|
2041
2043
|
let r = Z(), i = () => j(), a = () => g(r, i()), o = t.trim(), s = F([]), c = F(!1), l = F(!1), u = F(!0), d = null, f, p = 0, m = 0;
|
|
2042
2044
|
function h(e) {
|
|
2043
2045
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
@@ -2045,7 +2047,7 @@ function sr(e, t, n) {
|
|
|
2045
2047
|
function _() {
|
|
2046
2048
|
f?.();
|
|
2047
2049
|
let e = ++m;
|
|
2048
|
-
f =
|
|
2050
|
+
f = cr(r, i(), o, {
|
|
2049
2051
|
rootId: n,
|
|
2050
2052
|
cursor: d,
|
|
2051
2053
|
hasMore: u.value
|
|
@@ -2064,17 +2066,17 @@ function sr(e, t, n) {
|
|
|
2064
2066
|
X("postId", "==", o),
|
|
2065
2067
|
X("rootId", "==", n),
|
|
2066
2068
|
X("isReply", "==", !0),
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
+
G("createdAt", "asc"),
|
|
2070
|
+
G(z(), "asc"),
|
|
2069
2071
|
...d ? [J(d)] : [],
|
|
2070
|
-
|
|
2071
|
-
], r = await
|
|
2072
|
+
U(6)
|
|
2073
|
+
], r = await V(K(a(), ...t));
|
|
2072
2074
|
if (e !== p) return;
|
|
2073
|
-
let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0,
|
|
2075
|
+
let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, ur), f = c.map((e) => ({
|
|
2074
2076
|
id: e.id,
|
|
2075
2077
|
...e.data()
|
|
2076
2078
|
})).filter((e) => !i.has(e.id));
|
|
2077
|
-
s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size >
|
|
2079
|
+
s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > ur, _();
|
|
2078
2080
|
} finally {
|
|
2079
2081
|
e === p && (c.value = !1);
|
|
2080
2082
|
}
|
|
@@ -2098,8 +2100,8 @@ function sr(e, t, n) {
|
|
|
2098
2100
|
}
|
|
2099
2101
|
//#endregion
|
|
2100
2102
|
//#region src/composables/useMemiBoardLikes.ts
|
|
2101
|
-
function
|
|
2102
|
-
let n = () => j(), r = Z(), i =
|
|
2103
|
+
function hr(e, t) {
|
|
2104
|
+
let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = F(!1), s = F(!1), c;
|
|
2103
2105
|
function l(e) {
|
|
2104
2106
|
return he(r, n(), t, e);
|
|
2105
2107
|
}
|
|
@@ -2108,7 +2110,7 @@ function cr(e, t) {
|
|
|
2108
2110
|
o.value = !1;
|
|
2109
2111
|
return;
|
|
2110
2112
|
}
|
|
2111
|
-
c =
|
|
2113
|
+
c = W(l(e.uid), (e) => {
|
|
2112
2114
|
o.value = e.exists();
|
|
2113
2115
|
}, (e) => {
|
|
2114
2116
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
@@ -2121,12 +2123,12 @@ function cr(e, t) {
|
|
|
2121
2123
|
s.value = !0;
|
|
2122
2124
|
try {
|
|
2123
2125
|
let i = l(c), a = y(r, n(), t);
|
|
2124
|
-
return await
|
|
2126
|
+
return await Ee(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: H(-1) }), !1) : (n.set(i, {
|
|
2125
2127
|
uid: c,
|
|
2126
2128
|
postId: t,
|
|
2127
2129
|
boardId: e,
|
|
2128
|
-
createdAt:
|
|
2129
|
-
}), n.update(a, { likeCount:
|
|
2130
|
+
createdAt: q()
|
|
2131
|
+
}), n.update(a, { likeCount: H(1) }), !0));
|
|
2130
2132
|
} finally {
|
|
2131
2133
|
s.value = !1;
|
|
2132
2134
|
}
|
|
@@ -2137,11 +2139,11 @@ function cr(e, t) {
|
|
|
2137
2139
|
toggleLike: u
|
|
2138
2140
|
};
|
|
2139
2141
|
}
|
|
2140
|
-
var
|
|
2141
|
-
function
|
|
2142
|
-
let n = () => j(), r = Z(), i =
|
|
2142
|
+
var gr = Symbol.for("memi-board:comment-likes");
|
|
2143
|
+
function _r(e, t) {
|
|
2144
|
+
let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = N(() => I(e)), s = N(() => I(t)), c = F(/* @__PURE__ */ new Set()), l = F(null), u;
|
|
2143
2145
|
L([i, s], ([e, t]) => {
|
|
2144
|
-
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u =
|
|
2146
|
+
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = W(K(b(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
|
|
2145
2147
|
let t = /* @__PURE__ */ new Set();
|
|
2146
2148
|
for (let n of e.docs) {
|
|
2147
2149
|
let e = n.data().commentId;
|
|
@@ -2165,7 +2167,7 @@ function ur(e, t) {
|
|
|
2165
2167
|
if (l.value) return f(e);
|
|
2166
2168
|
l.value = e;
|
|
2167
2169
|
try {
|
|
2168
|
-
let i = he(r, n(), e, t), l = d(r, n(), e), u = await
|
|
2170
|
+
let i = he(r, n(), e, t), l = d(r, n(), e), u = await Ee(r, async (n) => {
|
|
2169
2171
|
let r = await n.get(i), c = await n.get(l);
|
|
2170
2172
|
if (!c.exists()) throw Error(a("likes.commentNotFound"));
|
|
2171
2173
|
let u = Number(c.data()?.likeCount ?? 0);
|
|
@@ -2175,7 +2177,7 @@ function ur(e, t) {
|
|
|
2175
2177
|
boardId: o.value,
|
|
2176
2178
|
commentId: e,
|
|
2177
2179
|
target: "comment",
|
|
2178
|
-
createdAt:
|
|
2180
|
+
createdAt: q()
|
|
2179
2181
|
}), n.update(l, { likeCount: u + 1 }), !0);
|
|
2180
2182
|
}), f = new Set(c.value);
|
|
2181
2183
|
return u ? f.add(e) : f.delete(e), c.value = f, u;
|
|
@@ -2191,7 +2193,7 @@ function ur(e, t) {
|
|
|
2191
2193
|
}
|
|
2192
2194
|
//#endregion
|
|
2193
2195
|
//#region src/composables/useMemiBoardReports.ts
|
|
2194
|
-
var
|
|
2196
|
+
var vr = 200, yr = [
|
|
2195
2197
|
{
|
|
2196
2198
|
value: "spam",
|
|
2197
2199
|
label: "스팸·광고"
|
|
@@ -2213,17 +2215,17 @@ var dr = 200, fr = [
|
|
|
2213
2215
|
label: "기타"
|
|
2214
2216
|
}
|
|
2215
2217
|
];
|
|
2216
|
-
function
|
|
2217
|
-
return
|
|
2218
|
+
function br(e, t) {
|
|
2219
|
+
return yr.some((t) => t.value === e) ? k(t, `reports.reason.${e}`) : e || k(t, "reports.fallbackLabel");
|
|
2218
2220
|
}
|
|
2219
|
-
function
|
|
2220
|
-
let n = () => j(), r = Z(), i =
|
|
2221
|
+
function xr(e, t) {
|
|
2222
|
+
let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = F(!1), s = F(!1), c;
|
|
2221
2223
|
L(i, (e) => {
|
|
2222
2224
|
if (c?.(), c = void 0, !e) {
|
|
2223
2225
|
o.value = !1;
|
|
2224
2226
|
return;
|
|
2225
2227
|
}
|
|
2226
|
-
c =
|
|
2228
|
+
c = W(u(r, n(), t, e.uid), (e) => {
|
|
2227
2229
|
o.value = e.exists();
|
|
2228
2230
|
}, (e) => {
|
|
2229
2231
|
console.error("[memi-board:reports] onSnapshot failed", e);
|
|
@@ -2234,13 +2236,13 @@ function mr(e, t) {
|
|
|
2234
2236
|
if (!l) throw Error(a("reports.signInRequired"));
|
|
2235
2237
|
if (s.value) return;
|
|
2236
2238
|
if (c.authorUid && c.authorUid === l) throw Error(a("reports.cannotReportOwn"));
|
|
2237
|
-
let d =
|
|
2239
|
+
let d = yr.some((e) => e.value === c.reason) ? c.reason : null;
|
|
2238
2240
|
if (!d) throw Error(a("reports.reasonRequired"));
|
|
2239
2241
|
let f = (c.detail || "").trim();
|
|
2240
2242
|
if (f.length > 200) throw Error(a("reports.detailTooLong", { max: 200 }));
|
|
2241
2243
|
s.value = !0;
|
|
2242
2244
|
try {
|
|
2243
|
-
await
|
|
2245
|
+
await De(u(r, n(), t, l), {
|
|
2244
2246
|
uid: l,
|
|
2245
2247
|
target: "post",
|
|
2246
2248
|
postId: t,
|
|
@@ -2251,7 +2253,7 @@ function mr(e, t) {
|
|
|
2251
2253
|
postTitle: (c.postTitle || "").trim().slice(0, 200),
|
|
2252
2254
|
authorUid: c.authorUid || "",
|
|
2253
2255
|
authorName: c.authorName ?? null,
|
|
2254
|
-
createdAt:
|
|
2256
|
+
createdAt: q()
|
|
2255
2257
|
}), o.value = !0;
|
|
2256
2258
|
} finally {
|
|
2257
2259
|
s.value = !1;
|
|
@@ -2263,9 +2265,9 @@ function mr(e, t) {
|
|
|
2263
2265
|
submitReport: l
|
|
2264
2266
|
};
|
|
2265
2267
|
}
|
|
2266
|
-
var
|
|
2267
|
-
function
|
|
2268
|
-
let t = () => j(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } =
|
|
2268
|
+
var Sr = 20;
|
|
2269
|
+
function Cr(e = {}) {
|
|
2270
|
+
let t = () => j(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Vt(), o = e.pageSize ?? Sr, s = N(() => I(e.boardId)?.trim() || ""), c = F([]), d = F(!0), f = F(!1), p = F(!1), m = F(""), h = null, g;
|
|
2269
2271
|
function _(e) {
|
|
2270
2272
|
return {
|
|
2271
2273
|
id: e.id,
|
|
@@ -2279,7 +2281,7 @@ function gr(e = {}) {
|
|
|
2279
2281
|
}
|
|
2280
2282
|
d.value = !0;
|
|
2281
2283
|
let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")];
|
|
2282
|
-
g =
|
|
2284
|
+
g = W(K(l(n, t()), ...e, G("createdAt", "desc"), G(z(), "desc"), U(o)), (e) => {
|
|
2283
2285
|
c.value = e.docs.map(_).filter((e) => a(e.boardId)), h = e.docs.at(-1) ?? null, p.value = e.docs.length >= o, d.value = !1;
|
|
2284
2286
|
}, (e) => {
|
|
2285
2287
|
console.error("[memi-board:report-queue] onSnapshot failed", e), m.value = e.message || String(e), d.value = !1;
|
|
@@ -2290,7 +2292,7 @@ function gr(e = {}) {
|
|
|
2290
2292
|
if (!f.value && p.value && h && i.value) {
|
|
2291
2293
|
f.value = !0, m.value = "";
|
|
2292
2294
|
try {
|
|
2293
|
-
let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await
|
|
2295
|
+
let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await V(K(l(n, t()), ...e, G("createdAt", "desc"), G(z(), "desc"), J(h), U(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
|
|
2294
2296
|
c.value.push(...i.map(_).filter((e) => !u.has(e.id) && a(e.boardId))), h = i.at(-1) ?? h, p.value = r.docs.length > o;
|
|
2295
2297
|
} catch (e) {
|
|
2296
2298
|
m.value = e.message || String(e);
|
|
@@ -2302,8 +2304,8 @@ function gr(e = {}) {
|
|
|
2302
2304
|
async function b(e, r) {
|
|
2303
2305
|
e.id && a(e.boardId) && await Y(u(n, t(), e.postId, e.uid), {
|
|
2304
2306
|
status: r,
|
|
2305
|
-
reviewedAt:
|
|
2306
|
-
reviewedBy:
|
|
2307
|
+
reviewedAt: q(),
|
|
2308
|
+
reviewedBy: Ae().value?.uid || ""
|
|
2307
2309
|
});
|
|
2308
2310
|
}
|
|
2309
2311
|
return {
|
|
@@ -2319,7 +2321,7 @@ function gr(e = {}) {
|
|
|
2319
2321
|
}
|
|
2320
2322
|
//#endregion
|
|
2321
2323
|
//#region src/composables/useMemiBoardViews.ts
|
|
2322
|
-
function
|
|
2324
|
+
function wr() {
|
|
2323
2325
|
let e = () => j(), t = Z();
|
|
2324
2326
|
function n(t, n) {
|
|
2325
2327
|
let { postsCollection: r } = e();
|
|
@@ -2343,7 +2345,7 @@ function _r() {
|
|
|
2343
2345
|
if (s && c && !r(s, c)) {
|
|
2344
2346
|
i(s, c);
|
|
2345
2347
|
try {
|
|
2346
|
-
await Y(y(t, e(), c), { viewCount:
|
|
2348
|
+
await Y(y(t, e(), c), { viewCount: H(1) });
|
|
2347
2349
|
} catch (e) {
|
|
2348
2350
|
try {
|
|
2349
2351
|
sessionStorage.removeItem(n(s, c));
|
|
@@ -2359,20 +2361,20 @@ function _r() {
|
|
|
2359
2361
|
}
|
|
2360
2362
|
//#endregion
|
|
2361
2363
|
//#region src/composables/useMemiBoardModeration.ts
|
|
2362
|
-
var
|
|
2364
|
+
var Tr = {
|
|
2363
2365
|
flagged: !1,
|
|
2364
2366
|
category: "none",
|
|
2365
2367
|
reason: "",
|
|
2366
2368
|
via: "empty"
|
|
2367
2369
|
};
|
|
2368
|
-
function
|
|
2369
|
-
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i =
|
|
2370
|
+
function Er(e = {}) {
|
|
2371
|
+
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Me(), a = (e) => k(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Vt(), l = An([...kn, ...r.localBlocklist ?? []]);
|
|
2370
2372
|
function u(e) {
|
|
2371
2373
|
let t = e.match(l);
|
|
2372
2374
|
return t ? {
|
|
2373
2375
|
flagged: !0,
|
|
2374
2376
|
category: "abuse",
|
|
2375
|
-
reason:
|
|
2377
|
+
reason: On({
|
|
2376
2378
|
via: "local",
|
|
2377
2379
|
localHit: t[0] || "",
|
|
2378
2380
|
category: "abuse"
|
|
@@ -2381,21 +2383,21 @@ function yr(e = {}) {
|
|
|
2381
2383
|
} : null;
|
|
2382
2384
|
}
|
|
2383
2385
|
function d() {
|
|
2384
|
-
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a =
|
|
2385
|
-
backend: new
|
|
2386
|
+
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Xe(i, {
|
|
2387
|
+
backend: new Je(),
|
|
2386
2388
|
useLimitedUseAppCheckTokens: t
|
|
2387
2389
|
});
|
|
2388
|
-
return
|
|
2390
|
+
return Ze(a, {
|
|
2389
2391
|
model: e,
|
|
2390
|
-
systemInstruction:
|
|
2392
|
+
systemInstruction: Cn(n.value),
|
|
2391
2393
|
generationConfig: {
|
|
2392
2394
|
temperature: 0,
|
|
2393
2395
|
maxOutputTokens: 256,
|
|
2394
2396
|
responseMimeType: "application/json",
|
|
2395
|
-
responseSchema:
|
|
2396
|
-
flagged:
|
|
2397
|
-
category:
|
|
2398
|
-
reason:
|
|
2397
|
+
responseSchema: Ye.object({ properties: {
|
|
2398
|
+
flagged: Ye.boolean(),
|
|
2399
|
+
category: Ye.string(),
|
|
2400
|
+
reason: Ye.string()
|
|
2399
2401
|
} })
|
|
2400
2402
|
}
|
|
2401
2403
|
});
|
|
@@ -2416,7 +2418,7 @@ function yr(e = {}) {
|
|
|
2416
2418
|
};
|
|
2417
2419
|
}
|
|
2418
2420
|
function p(e, t = "") {
|
|
2419
|
-
let n =
|
|
2421
|
+
let n = On({
|
|
2420
2422
|
reason: e.reason,
|
|
2421
2423
|
category: e.category,
|
|
2422
2424
|
via: e.via
|
|
@@ -2444,26 +2446,26 @@ function yr(e = {}) {
|
|
|
2444
2446
|
};
|
|
2445
2447
|
let i = e.trim();
|
|
2446
2448
|
if (!i) return {
|
|
2447
|
-
...
|
|
2449
|
+
...Tr,
|
|
2448
2450
|
via: "empty"
|
|
2449
2451
|
};
|
|
2450
2452
|
if (t?.skipFilter) return {
|
|
2451
|
-
...
|
|
2453
|
+
...Tr,
|
|
2452
2454
|
via: "admin-override"
|
|
2453
2455
|
};
|
|
2454
2456
|
let c = u(i);
|
|
2455
2457
|
if (c) return m(c);
|
|
2456
2458
|
if (r.enabled === !1) return {
|
|
2457
|
-
...
|
|
2459
|
+
...Tr,
|
|
2458
2460
|
via: "disabled"
|
|
2459
2461
|
};
|
|
2460
2462
|
try {
|
|
2461
|
-
let e =
|
|
2463
|
+
let e = Mn((await d().generateContent(wn(i, n.value))).response.text());
|
|
2462
2464
|
if (!e) return f();
|
|
2463
2465
|
let t = {
|
|
2464
2466
|
flagged: e.flagged,
|
|
2465
2467
|
category: e.category,
|
|
2466
|
-
reason:
|
|
2468
|
+
reason: On({
|
|
2467
2469
|
reason: e.reason,
|
|
2468
2470
|
category: e.category,
|
|
2469
2471
|
via: "ai"
|
|
@@ -2483,22 +2485,22 @@ function yr(e = {}) {
|
|
|
2483
2485
|
via: "restricted"
|
|
2484
2486
|
};
|
|
2485
2487
|
if (r.enabled === !1 || !r.moderateImages) return {
|
|
2486
|
-
...
|
|
2488
|
+
...Tr,
|
|
2487
2489
|
via: "disabled"
|
|
2488
2490
|
};
|
|
2489
2491
|
try {
|
|
2490
2492
|
let t = await new Promise((t, n) => {
|
|
2491
2493
|
let r = new FileReader();
|
|
2492
2494
|
r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
|
|
2493
|
-
}), r =
|
|
2495
|
+
}), r = Mn((await d().generateContent([{ inlineData: {
|
|
2494
2496
|
mimeType: e.type || "image/jpeg",
|
|
2495
2497
|
data: t
|
|
2496
|
-
} },
|
|
2498
|
+
} }, Tn(n.value)])).response.text());
|
|
2497
2499
|
if (!r) return f();
|
|
2498
2500
|
let i = {
|
|
2499
2501
|
flagged: r.flagged,
|
|
2500
2502
|
category: r.category,
|
|
2501
|
-
reason:
|
|
2503
|
+
reason: On({
|
|
2502
2504
|
reason: r.reason,
|
|
2503
2505
|
category: r.category,
|
|
2504
2506
|
via: "ai"
|
|
@@ -2517,7 +2519,7 @@ function yr(e = {}) {
|
|
|
2517
2519
|
}
|
|
2518
2520
|
//#endregion
|
|
2519
2521
|
//#region src/composables/useMemiBoardUsers.ts
|
|
2520
|
-
var
|
|
2522
|
+
var Dr = [
|
|
2521
2523
|
"owner",
|
|
2522
2524
|
"admin",
|
|
2523
2525
|
"manager",
|
|
@@ -2528,13 +2530,13 @@ var br = [
|
|
|
2528
2530
|
label: k("ko", `common.role.${e}`),
|
|
2529
2531
|
description: k("ko", `boardUsers.roleDescription.${e}`)
|
|
2530
2532
|
}));
|
|
2531
|
-
function
|
|
2532
|
-
let t = () => j(), n = Z(), { t: r } = A(), i = N(() => I(e.enabled) ?? !0 ? c(n, t()) : null), a =
|
|
2533
|
+
function Or(e = {}) {
|
|
2534
|
+
let t = () => j(), n = Z(), { t: r } = A(), i = N(() => I(e.enabled) ?? !0 ? c(n, t()) : null), a = ke(i, { ssrKey: p(t(), "users-management") });
|
|
2533
2535
|
async function o(e, i) {
|
|
2534
|
-
if (!
|
|
2536
|
+
if (!Dr.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
|
|
2535
2537
|
await Y(h(n, t(), e), {
|
|
2536
2538
|
role: i,
|
|
2537
|
-
updatedAt:
|
|
2539
|
+
updatedAt: q()
|
|
2538
2540
|
});
|
|
2539
2541
|
}
|
|
2540
2542
|
return {
|
|
@@ -2545,11 +2547,11 @@ function xr(e = {}) {
|
|
|
2545
2547
|
}
|
|
2546
2548
|
//#endregion
|
|
2547
2549
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2548
|
-
function
|
|
2550
|
+
function kr(e) {
|
|
2549
2551
|
let t = Z(), n = () => j(), r = N(() => I(e)), i = N(() => {
|
|
2550
2552
|
let e = r.value;
|
|
2551
2553
|
return e ? h(t, n(), e) : null;
|
|
2552
|
-
}), { data: a, pending: o } =
|
|
2554
|
+
}), { data: a, pending: o } = je(i);
|
|
2553
2555
|
return {
|
|
2554
2556
|
profile: a,
|
|
2555
2557
|
profilePending: o
|
|
@@ -2557,7 +2559,7 @@ function Sr(e) {
|
|
|
2557
2559
|
}
|
|
2558
2560
|
//#endregion
|
|
2559
2561
|
//#region src/utils/section.ts
|
|
2560
|
-
var
|
|
2562
|
+
var Ar = [
|
|
2561
2563
|
3,
|
|
2562
2564
|
4,
|
|
2563
2565
|
5,
|
|
@@ -2567,7 +2569,7 @@ var Cr = [
|
|
|
2567
2569
|
9,
|
|
2568
2570
|
12
|
|
2569
2571
|
];
|
|
2570
|
-
function
|
|
2572
|
+
function jr(e) {
|
|
2571
2573
|
return [
|
|
2572
2574
|
{
|
|
2573
2575
|
value: "list",
|
|
@@ -2583,7 +2585,7 @@ function wr(e) {
|
|
|
2583
2585
|
}
|
|
2584
2586
|
];
|
|
2585
2587
|
}
|
|
2586
|
-
function
|
|
2588
|
+
function Mr(e) {
|
|
2587
2589
|
return [{
|
|
2588
2590
|
value: "latest",
|
|
2589
2591
|
label: k(e, "sectionUtil.sort.latest")
|
|
@@ -2592,18 +2594,18 @@ function Tr(e) {
|
|
|
2592
2594
|
label: k(e, "sectionUtil.sort.likes")
|
|
2593
2595
|
}];
|
|
2594
2596
|
}
|
|
2595
|
-
var
|
|
2596
|
-
function
|
|
2597
|
+
var Nr = jr("ko"), Pr = Mr("ko");
|
|
2598
|
+
function Fr(e) {
|
|
2597
2599
|
return e === "list" ? 5 : 4;
|
|
2598
2600
|
}
|
|
2599
|
-
function
|
|
2601
|
+
function Ir(e, t) {
|
|
2600
2602
|
let n = Math.floor(Number(t));
|
|
2601
|
-
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) :
|
|
2603
|
+
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Fr(e);
|
|
2602
2604
|
}
|
|
2603
|
-
function
|
|
2605
|
+
function Lr(e) {
|
|
2604
2606
|
return e === 12 ? null : 12 - e;
|
|
2605
2607
|
}
|
|
2606
|
-
function
|
|
2608
|
+
function Rr(e) {
|
|
2607
2609
|
return {
|
|
2608
2610
|
3: "col-span-12 md:col-span-3",
|
|
2609
2611
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2615,36 +2617,36 @@ function jr(e) {
|
|
|
2615
2617
|
12: "col-span-12"
|
|
2616
2618
|
}[e];
|
|
2617
2619
|
}
|
|
2618
|
-
function
|
|
2620
|
+
function zr(e) {
|
|
2619
2621
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2620
2622
|
}
|
|
2621
|
-
function
|
|
2623
|
+
function Br(e) {
|
|
2622
2624
|
let t = e.boardId || e.category || "free";
|
|
2623
2625
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2624
2626
|
}
|
|
2625
|
-
function
|
|
2627
|
+
function Vr(e) {
|
|
2626
2628
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2627
2629
|
}
|
|
2628
|
-
function
|
|
2630
|
+
function Hr(e) {
|
|
2629
2631
|
if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2630
2632
|
}
|
|
2631
|
-
function
|
|
2633
|
+
function Ur(e) {
|
|
2632
2634
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2633
2635
|
}
|
|
2634
|
-
function
|
|
2635
|
-
return
|
|
2636
|
+
function Wr(e, t) {
|
|
2637
|
+
return gt(e, t).trim() || _t(e, t).trim() || k(t, "sectionUtil.untitledPost");
|
|
2636
2638
|
}
|
|
2637
|
-
function
|
|
2639
|
+
function Gr(e) {
|
|
2638
2640
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2639
2641
|
}
|
|
2640
|
-
function
|
|
2641
|
-
return k(t,
|
|
2642
|
+
function Kr(e, t) {
|
|
2643
|
+
return k(t, Gr(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
|
|
2642
2644
|
}
|
|
2643
|
-
function
|
|
2645
|
+
function qr(e, t) {
|
|
2644
2646
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2645
2647
|
}
|
|
2646
|
-
function
|
|
2647
|
-
let n = e.map((e) =>
|
|
2648
|
+
function Jr(e, t) {
|
|
2649
|
+
let n = e.map((e) => qr(e.boardId, t));
|
|
2648
2650
|
if (!n.length) return "default";
|
|
2649
2651
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2650
2652
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2652,27 +2654,27 @@ function Vr(e, t) {
|
|
|
2652
2654
|
}
|
|
2653
2655
|
//#endregion
|
|
2654
2656
|
//#region src/composables/useMemiBoardSections.ts
|
|
2655
|
-
var
|
|
2656
|
-
function
|
|
2657
|
+
var Yr = [];
|
|
2658
|
+
function Xr(e) {
|
|
2657
2659
|
let t = Number(e.cols);
|
|
2658
|
-
if (
|
|
2660
|
+
if (Ar.includes(t)) return t;
|
|
2659
2661
|
let n = e.width;
|
|
2660
2662
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2661
2663
|
}
|
|
2662
|
-
function
|
|
2664
|
+
function Zr(e) {
|
|
2663
2665
|
let t = e.height ?? e.size;
|
|
2664
2666
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2665
2667
|
}
|
|
2666
|
-
function
|
|
2668
|
+
function Qr(e) {
|
|
2667
2669
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2668
2670
|
}
|
|
2669
|
-
function
|
|
2671
|
+
function $r(e) {
|
|
2670
2672
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2671
2673
|
}
|
|
2672
|
-
function
|
|
2674
|
+
function ei(e) {
|
|
2673
2675
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2674
2676
|
}
|
|
2675
|
-
function
|
|
2677
|
+
function ti(e, t) {
|
|
2676
2678
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2677
2679
|
for (let e of n) {
|
|
2678
2680
|
let n = String(e || "").trim();
|
|
@@ -2680,34 +2682,34 @@ function Jr(e, t) {
|
|
|
2680
2682
|
}
|
|
2681
2683
|
return r;
|
|
2682
2684
|
}
|
|
2683
|
-
function
|
|
2685
|
+
function ni(e, t) {
|
|
2684
2686
|
return k(t, "sections.defaultTitle", { n: e + 1 });
|
|
2685
2687
|
}
|
|
2686
|
-
function
|
|
2687
|
-
let r =
|
|
2688
|
+
function ri(e, t, n) {
|
|
2689
|
+
let r = Qr(e), i = Ir(r, e.count), a = Q(e.i18n), o = ut(e.i18nSource);
|
|
2688
2690
|
return {
|
|
2689
2691
|
id: String(e.id || `box-${t + 1}`),
|
|
2690
|
-
title: String(e.title ||
|
|
2692
|
+
title: String(e.title || ni(t, n)),
|
|
2691
2693
|
showTitle: e.showTitle !== !1,
|
|
2692
2694
|
kind: r,
|
|
2693
|
-
boardId:
|
|
2695
|
+
boardId: ei(e),
|
|
2694
2696
|
count: i,
|
|
2695
|
-
postIds:
|
|
2696
|
-
sort:
|
|
2697
|
-
cols:
|
|
2698
|
-
height:
|
|
2697
|
+
postIds: ti(e, i),
|
|
2698
|
+
sort: $r(e),
|
|
2699
|
+
cols: Xr(e),
|
|
2700
|
+
height: Zr(e),
|
|
2699
2701
|
order: t,
|
|
2700
2702
|
...a ? { i18n: a } : {},
|
|
2701
2703
|
...o ? { i18nSource: o } : {}
|
|
2702
2704
|
};
|
|
2703
2705
|
}
|
|
2704
|
-
function
|
|
2705
|
-
let { locale: e } = A(), { translateEntries: t } =
|
|
2706
|
+
function ii() {
|
|
2707
|
+
let { locale: e } = A(), { translateEntries: t } = Bn(), r = Z(), i = R(r, "settings", "homeLayout"), { data: a, pending: o } = je(i), s = N(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : Yr).map((t, n) => ri(t, n, e.value)));
|
|
2706
2708
|
async function c(r) {
|
|
2707
2709
|
if (!n()) return r;
|
|
2708
2710
|
let i = e.value, a = r.filter((e) => {
|
|
2709
2711
|
let t = e.title.trim();
|
|
2710
|
-
return t ? !(e.i18nSource?.title === t &&
|
|
2712
|
+
return t ? !(e.i18nSource?.title === t && pt(e.i18n, ["title"])) : !1;
|
|
2711
2713
|
});
|
|
2712
2714
|
if (!a.length) return r;
|
|
2713
2715
|
let o = await t(a.map((e) => ({
|
|
@@ -2721,8 +2723,8 @@ function Zr() {
|
|
|
2721
2723
|
return r.map((e) => {
|
|
2722
2724
|
let t = o[e.id];
|
|
2723
2725
|
if (!t) return e;
|
|
2724
|
-
let n = e.title.trim(), r =
|
|
2725
|
-
return
|
|
2726
|
+
let n = e.title.trim(), r = ft(i, { title: n }, t);
|
|
2727
|
+
return pt(r, ["title"]) ? {
|
|
2726
2728
|
...e,
|
|
2727
2729
|
title: n,
|
|
2728
2730
|
i18n: r,
|
|
@@ -2734,18 +2736,18 @@ function Zr() {
|
|
|
2734
2736
|
});
|
|
2735
2737
|
}
|
|
2736
2738
|
function l(t, n) {
|
|
2737
|
-
let r = t, i =
|
|
2739
|
+
let r = t, i = Qr(r), a = Ir(i, t.count), o = t.title.trim() || ni(n, e.value);
|
|
2738
2740
|
return {
|
|
2739
2741
|
id: t.id,
|
|
2740
2742
|
title: o,
|
|
2741
2743
|
showTitle: t.showTitle !== !1,
|
|
2742
2744
|
kind: i,
|
|
2743
|
-
boardId:
|
|
2745
|
+
boardId: ei(r),
|
|
2744
2746
|
count: a,
|
|
2745
|
-
postIds:
|
|
2746
|
-
sort:
|
|
2747
|
-
cols:
|
|
2748
|
-
height:
|
|
2747
|
+
postIds: ti(r, a),
|
|
2748
|
+
sort: $r(r),
|
|
2749
|
+
cols: Xr(r),
|
|
2750
|
+
height: Zr(r),
|
|
2749
2751
|
order: n,
|
|
2750
2752
|
...t.i18n ? { i18n: t.i18n } : {},
|
|
2751
2753
|
...t.i18nSource?.title === o ? { i18nSource: t.i18nSource } : {}
|
|
@@ -2758,10 +2760,10 @@ function Zr() {
|
|
|
2758
2760
|
} catch (e) {
|
|
2759
2761
|
console.warn("[memi-board] section translation skipped", e);
|
|
2760
2762
|
}
|
|
2761
|
-
await
|
|
2763
|
+
await De(i, {
|
|
2762
2764
|
columns: 12,
|
|
2763
2765
|
blocks: t.map((e, t) => l(e, t)),
|
|
2764
|
-
updatedAt:
|
|
2766
|
+
updatedAt: q()
|
|
2765
2767
|
});
|
|
2766
2768
|
}
|
|
2767
2769
|
return {
|
|
@@ -2776,19 +2778,19 @@ function Zr() {
|
|
|
2776
2778
|
}
|
|
2777
2779
|
//#endregion
|
|
2778
2780
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2779
|
-
function
|
|
2781
|
+
function ai(e, t) {
|
|
2780
2782
|
let n = Z(), r = () => j(), i = I(e)?.trim() || "all", a = N(() => {
|
|
2781
2783
|
let i = Math.max(1, Math.floor(Number(I(t)) || 5)), a = I(e)?.trim() || "", s = [X("isPublished", "==", !0), X("listed", "==", !0)];
|
|
2782
|
-
return a && s.unshift(X("boardId", "==", a)),
|
|
2783
|
-
}), { data: s, pending: c, error: l } =
|
|
2784
|
+
return a && s.unshift(X("boardId", "==", a)), K(o(n, r()), ...s, G("createdAt", "desc"), U(i));
|
|
2785
|
+
}), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(r(), `mini-list/${i}`) });
|
|
2784
2786
|
return {
|
|
2785
2787
|
posts: N(() => s.value ?? []),
|
|
2786
2788
|
pending: c,
|
|
2787
2789
|
error: l
|
|
2788
2790
|
};
|
|
2789
2791
|
}
|
|
2790
|
-
function
|
|
2791
|
-
let t = Z(), n = () => j(), r = N(() => [...new Set(I(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = N(() => r.value.length ?
|
|
2792
|
+
function oi(e) {
|
|
2793
|
+
let t = Z(), n = () => j(), r = N(() => [...new Set(I(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = N(() => r.value.length ? K(o(t, n()), X(z(), "in", r.value)) : null), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(n(), `mini-picked/${i}`) });
|
|
2792
2794
|
return {
|
|
2793
2795
|
posts: N(() => {
|
|
2794
2796
|
let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
|
|
@@ -2804,7 +2806,22 @@ function $r(e) {
|
|
|
2804
2806
|
}
|
|
2805
2807
|
//#endregion
|
|
2806
2808
|
//#region src/data/versionHistory.ts
|
|
2807
|
-
var
|
|
2809
|
+
var si = [
|
|
2810
|
+
{
|
|
2811
|
+
version: "0.50.0",
|
|
2812
|
+
date: "2026-10-29",
|
|
2813
|
+
highlights: [
|
|
2814
|
+
"게시판 설정에서 고아 파일·댓글·좋아요를 조회하고 선택 삭제할 수 있어요",
|
|
2815
|
+
"최근 업로드를 보호하고, 삭제 전에 연결과 관리자 권한을 다시 확인해요",
|
|
2816
|
+
"파일 목록과 비공개 댓글 조회의 보안 규칙을 강화했어요"
|
|
2817
|
+
],
|
|
2818
|
+
note: "호스트 프로젝트의 Firestore·Storage 규칙도 함께 갱신해야 합니다."
|
|
2819
|
+
},
|
|
2820
|
+
{
|
|
2821
|
+
version: "0.49.0",
|
|
2822
|
+
date: "2026-10-29",
|
|
2823
|
+
highlights: ["보드 설정에서 글 작성 시 자동 번역을 켤 수 있어요. 기본값은 꺼짐이에요", "새 글의 제목·요약·본문을 10개 언어로 번역해 함께 저장해요"]
|
|
2824
|
+
},
|
|
2808
2825
|
{
|
|
2809
2826
|
version: "0.48.1",
|
|
2810
2827
|
date: "2026-10-29",
|
|
@@ -3149,7 +3166,7 @@ var ei = [
|
|
|
3149
3166
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
3150
3167
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
3151
3168
|
}
|
|
3152
|
-
],
|
|
3169
|
+
], ci = 10, li = 864e5, ui = 200, di = {
|
|
3153
3170
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
3154
3171
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
3155
3172
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -3158,7 +3175,7 @@ var ei = [
|
|
|
3158
3175
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
3159
3176
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
3160
3177
|
};
|
|
3161
|
-
function
|
|
3178
|
+
function fi(e) {
|
|
3162
3179
|
return {
|
|
3163
3180
|
proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
|
|
3164
3181
|
polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
|
|
@@ -3169,27 +3186,27 @@ function ai(e) {
|
|
|
3169
3186
|
title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
|
|
3170
3187
|
};
|
|
3171
3188
|
}
|
|
3172
|
-
function
|
|
3173
|
-
let r = t === "ko", i =
|
|
3189
|
+
function pi(e, t, n) {
|
|
3190
|
+
let r = t === "ko", i = xn(t);
|
|
3174
3191
|
if (e.action !== "custom") {
|
|
3175
|
-
if (r) return
|
|
3176
|
-
let t =
|
|
3192
|
+
if (r) return di[e.action];
|
|
3193
|
+
let t = fi(i)[e.action];
|
|
3177
3194
|
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}.`;
|
|
3178
3195
|
}
|
|
3179
|
-
let a = e.customInstruction?.trim().slice(0,
|
|
3196
|
+
let a = e.customInstruction?.trim().slice(0, ui);
|
|
3180
3197
|
if (!a) throw Error(n("writingAssistant.customRequired"));
|
|
3181
3198
|
return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
|
|
3182
3199
|
}
|
|
3183
|
-
function
|
|
3200
|
+
function mi(e) {
|
|
3184
3201
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
3185
3202
|
}
|
|
3186
|
-
function
|
|
3203
|
+
function hi(e) {
|
|
3187
3204
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
3188
3205
|
}
|
|
3189
|
-
function
|
|
3190
|
-
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = (e, t) => k(n.value, e, t), i =
|
|
3206
|
+
function gi(e = {}) {
|
|
3207
|
+
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = (e, t) => k(n.value, e, t), i = Me(), a = Z(), o = Ae(), s = v(), { translateEntries: c, translateHtml: l } = Bn();
|
|
3191
3208
|
function u(e, t) {
|
|
3192
|
-
let n =
|
|
3209
|
+
let n = hi(e.aiWritingWindowAt), r = n != null && t - n < li, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
|
|
3193
3210
|
return {
|
|
3194
3211
|
active: r,
|
|
3195
3212
|
windowAt: n,
|
|
@@ -3198,7 +3215,7 @@ function li(e = {}) {
|
|
|
3198
3215
|
};
|
|
3199
3216
|
}
|
|
3200
3217
|
function d(e, t) {
|
|
3201
|
-
let n = e +
|
|
3218
|
+
let n = e + li, i = Math.max(1, Math.ceil((n - t) / 36e5));
|
|
3202
3219
|
return Error(r("writingAssistant.dailyLimit", {
|
|
3203
3220
|
limit: 10,
|
|
3204
3221
|
hours: i
|
|
@@ -3207,13 +3224,13 @@ function li(e = {}) {
|
|
|
3207
3224
|
async function f(e) {
|
|
3208
3225
|
let t = o.value?.uid;
|
|
3209
3226
|
if (!t) throw Error(r("writingAssistant.loginRequired"));
|
|
3210
|
-
await
|
|
3227
|
+
await Ee(a, async (n) => {
|
|
3211
3228
|
let i = h(a, j(), t), o = await n.get(i);
|
|
3212
3229
|
if (!o.exists()) throw Error(r("writingAssistant.userMissing"));
|
|
3213
3230
|
let s = o.data(), c = e(u(s, Date.now()), s);
|
|
3214
3231
|
c && n.update(i, {
|
|
3215
3232
|
...c,
|
|
3216
|
-
updatedAt:
|
|
3233
|
+
updatedAt: q()
|
|
3217
3234
|
});
|
|
3218
3235
|
});
|
|
3219
3236
|
}
|
|
@@ -3223,7 +3240,7 @@ function li(e = {}) {
|
|
|
3223
3240
|
return e.active ? { aiWritingPending: e.pending + 1 } : {
|
|
3224
3241
|
aiWritingCount: 0,
|
|
3225
3242
|
aiWritingPending: 1,
|
|
3226
|
-
aiWritingWindowAt:
|
|
3243
|
+
aiWritingWindowAt: q()
|
|
3227
3244
|
};
|
|
3228
3245
|
});
|
|
3229
3246
|
}
|
|
@@ -3250,13 +3267,13 @@ function li(e = {}) {
|
|
|
3250
3267
|
}
|
|
3251
3268
|
async function y(e) {
|
|
3252
3269
|
(e.action === "translate-en" || e.action === "translate-ko") && D();
|
|
3253
|
-
let t = n.value, a = t === "ko", o =
|
|
3270
|
+
let t = n.value, a = t === "ko", o = pi(e, t, r);
|
|
3254
3271
|
if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
|
|
3255
3272
|
return _(async () => {
|
|
3256
|
-
let t = s.moderation ?? {}, n =
|
|
3257
|
-
backend: new
|
|
3273
|
+
let t = s.moderation ?? {}, n = Xe(i, {
|
|
3274
|
+
backend: new Je(),
|
|
3258
3275
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
3259
|
-
}), c =
|
|
3276
|
+
}), c = Ze(n, {
|
|
3260
3277
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
3261
3278
|
systemInstruction: (a ? [
|
|
3262
3279
|
"너는 게시판 글쓰기 편집 도우미다.",
|
|
@@ -3279,7 +3296,7 @@ function li(e = {}) {
|
|
|
3279
3296
|
`Task: ${o}`,
|
|
3280
3297
|
e.title ? `Current title: ${e.title}` : "",
|
|
3281
3298
|
`Body:\n${l.slice(0, 12e3)}`
|
|
3282
|
-
]).filter(Boolean).join("\n\n"), d =
|
|
3299
|
+
]).filter(Boolean).join("\n\n"), d = mi((await c.generateContent(u)).response.text());
|
|
3283
3300
|
if (!d) throw Error(r("writingAssistant.emptyResult"));
|
|
3284
3301
|
return d;
|
|
3285
3302
|
});
|
|
@@ -3288,9 +3305,9 @@ function li(e = {}) {
|
|
|
3288
3305
|
D();
|
|
3289
3306
|
let t = e.source ?? n.value, i = e.title?.trim() ?? "", a = e.summary?.trim() ?? "", o = e.content ?? "", s = {};
|
|
3290
3307
|
i && (s.title = i), a && (s.summary = a);
|
|
3291
|
-
let u = !!
|
|
3308
|
+
let u = !!on(o);
|
|
3292
3309
|
if (!Object.keys(s).length && !u) throw Error(r("writingAssistant.translateEmpty"));
|
|
3293
|
-
if (u && o.length > 12e3) throw Error(r("writingAssistant.translateTooLong", { limit:
|
|
3310
|
+
if (u && o.length > 12e3) throw Error(r("writingAssistant.translateTooLong", { limit: Fn }));
|
|
3294
3311
|
return _(async () => {
|
|
3295
3312
|
let [e, n] = await Promise.all([Object.keys(s).length ? c([{
|
|
3296
3313
|
id: "post",
|
|
@@ -3299,8 +3316,8 @@ function li(e = {}) {
|
|
|
3299
3316
|
}], {
|
|
3300
3317
|
source: t,
|
|
3301
3318
|
purpose: "message board post title and short summary"
|
|
3302
|
-
}) : Promise.resolve({}), u ? l(o, { source: t }) : Promise.resolve({})]), d =
|
|
3303
|
-
if (Object.keys(s).length && !
|
|
3319
|
+
}) : Promise.resolve({}), u ? l(o, { source: t }) : Promise.resolve({})]), d = ft(t, s, e.post);
|
|
3320
|
+
if (Object.keys(s).length && !pt(d, Object.keys(s))) throw Error(r("writingAssistant.translateIncomplete"));
|
|
3304
3321
|
let f = {
|
|
3305
3322
|
i18n: d,
|
|
3306
3323
|
i18nSource: {
|
|
@@ -3315,11 +3332,11 @@ function li(e = {}) {
|
|
|
3315
3332
|
let n = M(t);
|
|
3316
3333
|
n && typeof r == "string" && r.trim() && (e[n] = { content: r.trim() });
|
|
3317
3334
|
}
|
|
3318
|
-
let i =
|
|
3319
|
-
if (!
|
|
3335
|
+
let i = ft(t, { content: o }, e);
|
|
3336
|
+
if (!pt(i, ["content"])) throw Error(r("writingAssistant.translateIncomplete"));
|
|
3320
3337
|
f.contentI18n = i, f.contentI18nSource = {
|
|
3321
3338
|
locale: t,
|
|
3322
|
-
hash:
|
|
3339
|
+
hash: ct(o)
|
|
3323
3340
|
};
|
|
3324
3341
|
}
|
|
3325
3342
|
return f;
|
|
@@ -3332,14 +3349,14 @@ function li(e = {}) {
|
|
|
3332
3349
|
}
|
|
3333
3350
|
//#endregion
|
|
3334
3351
|
//#region src/utils/renderMarkdown.ts
|
|
3335
|
-
function
|
|
3352
|
+
function _i(e) {
|
|
3336
3353
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3337
3354
|
}
|
|
3338
|
-
function
|
|
3355
|
+
function vi(e) {
|
|
3339
3356
|
let t = e;
|
|
3340
3357
|
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;
|
|
3341
3358
|
}
|
|
3342
|
-
function
|
|
3359
|
+
function yi(e) {
|
|
3343
3360
|
if (!e?.trim()) return "";
|
|
3344
3361
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
3345
3362
|
function c() {
|
|
@@ -3348,7 +3365,7 @@ function fi(e) {
|
|
|
3348
3365
|
return;
|
|
3349
3366
|
}
|
|
3350
3367
|
let e = o;
|
|
3351
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
3368
|
+
n.push(`<${e}>${s.map((e) => `<li>${vi(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
3352
3369
|
}
|
|
3353
3370
|
for (; r < t.length;) {
|
|
3354
3371
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -3357,7 +3374,7 @@ function fi(e) {
|
|
|
3357
3374
|
continue;
|
|
3358
3375
|
}
|
|
3359
3376
|
if (i) {
|
|
3360
|
-
a.push(
|
|
3377
|
+
a.push(_i(e)), r++;
|
|
3361
3378
|
continue;
|
|
3362
3379
|
}
|
|
3363
3380
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -3368,24 +3385,24 @@ function fi(e) {
|
|
|
3368
3385
|
if (u) {
|
|
3369
3386
|
c();
|
|
3370
3387
|
let e = u[1].length;
|
|
3371
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${
|
|
3388
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${vi(_i(u[2]))}</h${e}>`), r++;
|
|
3372
3389
|
continue;
|
|
3373
3390
|
}
|
|
3374
3391
|
if (e.startsWith("> ")) {
|
|
3375
3392
|
c();
|
|
3376
3393
|
let e = [];
|
|
3377
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
3378
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
3394
|
+
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(_i((t[r] ?? "").slice(2))), r++;
|
|
3395
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${vi(e.join("<br />"))}</blockquote>`);
|
|
3379
3396
|
continue;
|
|
3380
3397
|
}
|
|
3381
3398
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
3382
3399
|
if (d) {
|
|
3383
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
3400
|
+
o && o !== "ul" && c(), o = "ul", s.push(_i(d[1])), r++;
|
|
3384
3401
|
continue;
|
|
3385
3402
|
}
|
|
3386
3403
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
3387
3404
|
if (f) {
|
|
3388
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
3405
|
+
o && o !== "ol" && c(), o = "ol", s.push(_i(f[1])), r++;
|
|
3389
3406
|
continue;
|
|
3390
3407
|
}
|
|
3391
3408
|
if (!e.trim()) {
|
|
@@ -3397,13 +3414,194 @@ function fi(e) {
|
|
|
3397
3414
|
for (; r < t.length;) {
|
|
3398
3415
|
let e = t[r] ?? "";
|
|
3399
3416
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
3400
|
-
p.push(
|
|
3417
|
+
p.push(_i(e)), r++;
|
|
3401
3418
|
}
|
|
3402
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
3419
|
+
n.push(`<p class="my-2 leading-relaxed">${vi(p.join("<br />"))}</p>`);
|
|
3403
3420
|
}
|
|
3404
3421
|
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");
|
|
3405
3422
|
}
|
|
3406
3423
|
//#endregion
|
|
3407
|
-
|
|
3424
|
+
//#region src/utils/orphanCleanup.ts
|
|
3425
|
+
function bi(e, t, n, r) {
|
|
3426
|
+
return typeof t.postId != "string" || !t.postId || t.postId.includes("/") ? !1 : !n.has(t.postId) || e === "like" && t.target === "comment" && typeof t.commentId == "string" && !!t.commentId && !t.commentId.includes("/") && !r.has(t.commentId);
|
|
3427
|
+
}
|
|
3428
|
+
function xi(e, t) {
|
|
3429
|
+
let n = Date.parse(e);
|
|
3430
|
+
return Number.isFinite(n) && t - n >= 864e5;
|
|
3431
|
+
}
|
|
3432
|
+
function Si(e, t) {
|
|
3433
|
+
if (!e.startsWith(`${t}/`)) return null;
|
|
3434
|
+
let n = e.slice(t.length + 1).split("/");
|
|
3435
|
+
if (n.some((e) => !e || e === "." || e === "..")) return null;
|
|
3436
|
+
let [r, i, a] = n;
|
|
3437
|
+
return i === "attachments" && n.length === 3 || i === "images" && (n.length === 3 || n.length === 4 && a === "thumbnails") ? { postId: r } : i === "comment-images" && n.length === 4 ? {
|
|
3438
|
+
postId: r,
|
|
3439
|
+
commentId: a
|
|
3440
|
+
} : null;
|
|
3441
|
+
}
|
|
3442
|
+
function Ci(e, t, n) {
|
|
3443
|
+
let r = `${t === "comment" ? n.commentsCollection : n.likesCollection}/`, i = e.startsWith(r) ? e.slice(r.length) : "";
|
|
3444
|
+
return !!i && !i.includes("/") && i !== "." && i !== "..";
|
|
3445
|
+
}
|
|
3446
|
+
//#endregion
|
|
3447
|
+
//#region src/composables/useMemiBoardCleanup.ts
|
|
3448
|
+
function wi() {
|
|
3449
|
+
let e = Z(), t = Me(), { isAdmin: n, rolePending: r } = Vt(), { t: i } = A(), a = F([]), o = F(!1), s = F(!1), c = F(""), l = F(""), u = [], d = "", f = /* @__PURE__ */ new Map();
|
|
3450
|
+
function p() {
|
|
3451
|
+
let e = Le(t).currentUser?.uid;
|
|
3452
|
+
if (!e) throw Error(i("settings.noAccess"));
|
|
3453
|
+
return JSON.stringify([e, j()]);
|
|
3454
|
+
}
|
|
3455
|
+
function m(e) {
|
|
3456
|
+
if (h(), p() !== e) throw Error(i("settings.cleanup.contextChanged"));
|
|
3457
|
+
}
|
|
3458
|
+
function h() {
|
|
3459
|
+
if (!n.value || r.value || !Le(t).currentUser) throw Error(i("settings.noAccess"));
|
|
3460
|
+
}
|
|
3461
|
+
async function g() {
|
|
3462
|
+
let t = j(), [n, r, i] = await Promise.all([
|
|
3463
|
+
we(ve(e, t.postsCollection)),
|
|
3464
|
+
we(ve(e, t.commentsCollection)),
|
|
3465
|
+
we(ve(e, t.likesCollection))
|
|
3466
|
+
]), a = /* @__PURE__ */ new Set();
|
|
3467
|
+
function o(e) {
|
|
3468
|
+
if (typeof e == "string") {
|
|
3469
|
+
e.startsWith(`${t.postsCollection}/`) && a.add(e);
|
|
3470
|
+
let n = Jt(e);
|
|
3471
|
+
n && a.add(n);
|
|
3472
|
+
for (let t of e.matchAll(/https?:\/\/[^\s"'<>]+/g)) {
|
|
3473
|
+
let e = Jt(t[0].replace(/&/g, "&"));
|
|
3474
|
+
e && a.add(e);
|
|
3475
|
+
}
|
|
3476
|
+
} else Array.isArray(e) ? e.forEach(o) : e && typeof e == "object" && Object.getPrototypeOf(e) === Object.prototype && Object.values(e).forEach(o);
|
|
3477
|
+
}
|
|
3478
|
+
n.docs.forEach((e) => o(e.data())), r.docs.forEach((e) => o(e.data()));
|
|
3479
|
+
for (let e of n.docs) (await we(ve(e.ref, "body"))).docs.forEach((e) => o(e.data()));
|
|
3480
|
+
for (let e of [...a]) {
|
|
3481
|
+
let t = e.match(/^(.*\/images\/)([^/]+)\.[^/.]+$/);
|
|
3482
|
+
t && a.add(`${t[1]}thumbnails/${t[2]}.jpg`);
|
|
3483
|
+
}
|
|
3484
|
+
return {
|
|
3485
|
+
cfg: t,
|
|
3486
|
+
posts: new Set(n.docs.map((e) => e.id)),
|
|
3487
|
+
comments: new Set(r.docs.map((e) => e.id)),
|
|
3488
|
+
commentDocs: r.docs,
|
|
3489
|
+
likes: i.docs,
|
|
3490
|
+
referenced: a
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
async function _() {
|
|
3494
|
+
if (!o.value) {
|
|
3495
|
+
o.value = !0, s.value = !1, a.value = [], u = [], d = "", f.clear(), c.value = "", l.value = "";
|
|
3496
|
+
try {
|
|
3497
|
+
h();
|
|
3498
|
+
let e = p(), n = await g();
|
|
3499
|
+
m(e);
|
|
3500
|
+
let r = [];
|
|
3501
|
+
for (let [e, t] of [["comment", n.commentDocs], ["like", n.likes]]) for (let i of t) {
|
|
3502
|
+
let t = i.data();
|
|
3503
|
+
bi(e, t, n.posts, n.comments) && r.push({
|
|
3504
|
+
kind: e,
|
|
3505
|
+
path: i.ref.path,
|
|
3506
|
+
postId: t.postId,
|
|
3507
|
+
commentId: t.commentId
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
let i = We(t), o = [qe(i, n.cfg.postsCollection)];
|
|
3511
|
+
for (; o.length;) {
|
|
3512
|
+
m(e);
|
|
3513
|
+
let t = o.pop(), i;
|
|
3514
|
+
do {
|
|
3515
|
+
let e = await Ge(t, {
|
|
3516
|
+
maxResults: 1e3,
|
|
3517
|
+
pageToken: i
|
|
3518
|
+
});
|
|
3519
|
+
o.push(...e.prefixes);
|
|
3520
|
+
for (let t of e.items) {
|
|
3521
|
+
let e = Si(t.fullPath, n.cfg.postsCollection);
|
|
3522
|
+
if (!e) continue;
|
|
3523
|
+
let { postId: i, commentId: a } = e;
|
|
3524
|
+
if (n.referenced.has(t.fullPath) || n.posts.has(i) && (!a || n.comments.has(a))) continue;
|
|
3525
|
+
let o = await Ue(t);
|
|
3526
|
+
xi(o.updated, Date.now()) && (f.set(t.fullPath, o.generation), r.push({
|
|
3527
|
+
kind: "file",
|
|
3528
|
+
path: t.fullPath,
|
|
3529
|
+
postId: i,
|
|
3530
|
+
commentId: a,
|
|
3531
|
+
size: o.size
|
|
3532
|
+
}));
|
|
3533
|
+
}
|
|
3534
|
+
i = e.nextPageToken;
|
|
3535
|
+
} while (i);
|
|
3536
|
+
}
|
|
3537
|
+
m(e), u = r.map((e) => Object.freeze({ ...e })), d = e, a.value = r, s.value = !0;
|
|
3538
|
+
} catch (e) {
|
|
3539
|
+
c.value = `${i("settings.cleanup.failed")} ${e instanceof Error ? e.message : String(e)}`;
|
|
3540
|
+
} finally {
|
|
3541
|
+
o.value = !1;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
async function v(n) {
|
|
3546
|
+
if (o.value || !s.value) return;
|
|
3547
|
+
o.value = !0, c.value = "", l.value = "";
|
|
3548
|
+
let r = 0, p = 0, h = 0;
|
|
3549
|
+
try {
|
|
3550
|
+
m(d);
|
|
3551
|
+
let o = await g();
|
|
3552
|
+
m(d);
|
|
3553
|
+
let s = new Set(n), c = new Set(a.value.map((e) => e.path));
|
|
3554
|
+
for (let n of u) if (s.has(n.path) && c.has(n.path)) {
|
|
3555
|
+
m(d);
|
|
3556
|
+
try {
|
|
3557
|
+
let i = !1;
|
|
3558
|
+
if (n.kind === "file") {
|
|
3559
|
+
if (!Si(n.path, o.cfg.postsCollection)) throw Error("Invalid cleanup path");
|
|
3560
|
+
let r = We(t), a = qe(r, n.path), [s, c, l] = await Promise.all([
|
|
3561
|
+
Ce(R(e, o.cfg.postsCollection, n.postId)),
|
|
3562
|
+
n.commentId ? Ce(R(e, o.cfg.commentsCollection, n.commentId)) : null,
|
|
3563
|
+
Ue(a)
|
|
3564
|
+
]);
|
|
3565
|
+
m(d), l.generation === f.get(n.path) && !o.referenced.has(n.path) && xi(l.updated, Date.now()) && (!s.exists() || n.commentId && !c?.exists()) && (await He(a), i = !0);
|
|
3566
|
+
} else {
|
|
3567
|
+
let t = n.kind;
|
|
3568
|
+
if (!Ci(n.path, t, o.cfg)) throw Error("Invalid cleanup path");
|
|
3569
|
+
i = await Ee(e, async (r) => {
|
|
3570
|
+
let i = await r.get(R(e, n.path));
|
|
3571
|
+
if (!i.exists()) return !0;
|
|
3572
|
+
let a = i.data();
|
|
3573
|
+
if (typeof a.postId != "string" || !a.postId || a.postId.includes("/")) return !1;
|
|
3574
|
+
let s = await r.get(R(e, o.cfg.postsCollection, a.postId)), c = n.kind === "like" && a.target === "comment" && typeof a.commentId == "string" && a.commentId && !a.commentId.includes("/") ? await r.get(R(e, o.cfg.commentsCollection, a.commentId)) : null;
|
|
3575
|
+
return bi(t, a, new Set(s.exists() ? [a.postId] : []), new Set(c?.exists() ? [a.commentId] : [])) ? (m(d), r.delete(i.ref), !0) : !1;
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3578
|
+
i ? r++ : p++, a.value = a.value.filter((e) => e.path !== n.path);
|
|
3579
|
+
} catch {
|
|
3580
|
+
h++;
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
l.value = i("settings.cleanup.result", {
|
|
3584
|
+
deleted: r,
|
|
3585
|
+
skipped: p,
|
|
3586
|
+
failed: h
|
|
3587
|
+
});
|
|
3588
|
+
} catch (e) {
|
|
3589
|
+
c.value = `${i("settings.cleanup.failed")} ${e instanceof Error ? e.message : String(e)}`;
|
|
3590
|
+
} finally {
|
|
3591
|
+
o.value = !1;
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
return {
|
|
3595
|
+
items: _e(a),
|
|
3596
|
+
busy: _e(o),
|
|
3597
|
+
scanned: _e(s),
|
|
3598
|
+
error: _e(c),
|
|
3599
|
+
result: _e(l),
|
|
3600
|
+
scan: _,
|
|
3601
|
+
remove: v
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
//#endregion
|
|
3605
|
+
export { ar as AUTHOR_MEMO_MAX_LENGTH, Ar as BOARD_SECTION_COLS, Nr as BOARD_SECTION_KINDS, Pr as BOARD_SECTION_SORTS, Dr as BOARD_USER_ROLES, dr as COMMENT_BODY_MAX_LENGTH, Nt as DEFAULT_BLOCK_BAN_DECAY_MS, Mt as DEFAULT_BLOCK_BAN_THRESHOLD, Vn as DEFAULT_BOARDS, Hn as DEFAULT_CATEGORIES, i as DEFAULT_COMMENTS_COLLECTION, r as DEFAULT_LIKES_COLLECTION, re as DEFAULT_MEMI_BOARD_LOCALE, m as DEFAULT_POSTS_COLLECTION, ae as DEFAULT_REPORTS_COLLECTION, e as DEFAULT_SETTINGS_COLLECTION, ie as DEFAULT_USERS_COLLECTION, O as FALLBACK_MEMI_BOARD_LOCALE, Fn as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, vr as REPORT_DETAIL_MAX_LENGTH, yr as REPORT_REASONS, ci as WRITING_ASSISTANT_DAILY_LIMIT, $e as asHttpUrl, yt as boardCopyIsTranslated, at as boardListOgDescription, it as boardListOgTitle, rt as boardPostOgDescription, nt as boardPostOgTitle, jr as boardSectionKinds, Mr as boardSectionSorts, p as boardSsrKey, h as boardUserDoc, c as boardUsersCol, mn as buildPostPreview, Dt as canManageBoardByRole, Ot as canWriteCommentByRole, Ir as clampSectionCount, d as commentDoc, g as commentsCol, me as configureMemiBoard, pt as coversLocales, Fr as defaultSectionCount, vt as displayPostContent, _t as displayPostSummary, gt as displayPostTitle, ht as displayStoredField, Pt as effectiveModerationBlockCount, qt as extractEditorImageUrls, wt as fetchPublicListForSeo, Ct as fetchPublicPostForSeo, ee as formatDate, oe as formatFullDate, fe as formatRelativeDate, Lt as formatRestrictedUntilLabel, te as formatTimestampDetails, ln as hasBodyImage, cn as hasBodyText, ct as hashText, Gt as imeSafeSubmitClick, Wt as imeSafeSubmitPointerDown, Et as isAssignedStaff, un as isContentEmpty, qn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, ne as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, It as isModerationWriteRestricted, he as likeDoc, t as likeDocId, b as likesCol, ce as loadMemiBoardLocale, mt as localizedText, gr as memiBoardCommentLikesKey, se as memiBoardHostLocaleKey, pe as memiBoardLocaleInfo, ft as mergeSourceFields, qr as miniBoardView, Hr as miniCommentLink, Vr as miniMoreLink, Ur as miniPostImage, Br as miniPostLink, Wr as miniPostTitle, Jr as miniPostsView, Gr as miniViewGroup, Kr as miniViewLabel, Ft as moderationWriteRestrictedUntilMs, ot as normalizeBasePath, ri as normalizeBoardSection, M as normalizeMemiBoardLocale, Lr as pairSectionCols, on as plainTextFromHtml, s as postBodyDoc, y as postDoc, Yt as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, ut as readI18nSource, Q as readLocalizedFields, yi as renderMarkdownToHtml, u as reportDoc, le as reportDocId, br as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, bt as resolvePublicSeoDb, Rr as sectionColClass, zr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Jt as storagePathFromDownloadUrl, sn as titleFromBody, tt as toAbsoluteUrl, et as toBoardOgImageUrl, k as translate, j as useBoardPathConfig, Vt as useMemiBoardAuth, sr as useMemiBoardAuthorMemo, Bn as useMemiBoardAutoTranslate, wi as useMemiBoardCleanup, ir as useMemiBoardCommentFeed, _r as useMemiBoardCommentLikes, pr as useMemiBoardComments, v as useMemiBoardConfig, A as useMemiBoardI18n, hr as useMemiBoardLikes, ai as useMemiBoardMiniLatest, oi as useMemiBoardMiniPicked, Er as useMemiBoardModeration, Qn as useMemiBoardPost, Zn as useMemiBoardPostList, Yn as useMemiBoardPosts, mr as useMemiBoardReplies, Cr as useMemiBoardReportQueue, xr as useMemiBoardReports, ii as useMemiBoardSections, Gn as useMemiBoardSettings, nr as useMemiBoardUserComments, er as useMemiBoardUserPosts, kr as useMemiBoardUserProfile, Or as useMemiBoardUsers, wr as useMemiBoardViews, gi as useMemiBoardWritingAssistant, si as versionHistory, pn as videoListCoverUrl, fn as youtubeId };
|
|
3408
3606
|
|
|
3409
3607
|
//# sourceMappingURL=index.js.map
|