memi-board 0.47.0 → 0.48.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 +86 -18
- package/dist/{ar-D_6hknjh.js → ar-C-X7I5_n.js} +9 -2
- package/dist/ar-C-X7I5_n.js.map +1 -0
- package/dist/{slugify-DzXnx16m.js → commentImages-D1PEZIU9.js} +65 -16
- package/dist/commentImages-D1PEZIU9.js.map +1 -0
- package/dist/composables/useMemiBoardComments.d.ts +20 -2
- package/dist/composables/useMemiBoardStorage.d.ts +4 -0
- package/dist/{de-DQqSsBsZ.js → de-clWR1v1K.js} +9 -2
- package/dist/de-clWR1v1K.js.map +1 -0
- package/dist/{en-Broh-9df.js → en-BSBqcgqW.js} +11 -4
- package/dist/en-BSBqcgqW.js.map +1 -0
- package/dist/{es-D3eEYN6w.js → es-D65HxdZE.js} +9 -2
- package/dist/es-D65HxdZE.js.map +1 -0
- package/dist/{fr-CbXPRKeU.js → fr-Cn_Ys4zN.js} +9 -2
- package/dist/fr-Cn_Ys4zN.js.map +1 -0
- package/dist/{id-DxVqvPpo.js → id-zjyRo6dg.js} +9 -2
- package/dist/id-zjyRo6dg.js.map +1 -0
- package/dist/index.js +1090 -1016
- package/dist/index.js.map +1 -1
- package/dist/{ja-D2dIWUse.js → ja-BOTpLmpf.js} +9 -2
- package/dist/ja-BOTpLmpf.js.map +1 -0
- package/dist/{pt-BAuHdglU.js → pt-M4T-sRwC.js} +9 -2
- package/dist/pt-M4T-sRwC.js.map +1 -0
- package/dist/runtime/components/CommentForm.vue +40 -6
- package/dist/runtime/components/CommentImageField.vue +201 -0
- package/dist/runtime/components/CommentItem.vue +56 -10
- package/dist/storage.d.ts +2 -1
- package/dist/storage.js +101 -62
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/utils/commentImages.d.ts +7 -0
- package/dist/utils/deletePostCascade.d.ts +4 -1
- package/dist/{zh-CMyzTdQ5.js → zh-CtswuQ57.js} +9 -2
- package/dist/zh-CtswuQ57.js.map +1 -0
- package/docs/firebase-setup.md +8 -9
- package/docs/firestore.rules.example +70 -1
- package/docs/storage.rules.example +17 -2
- package/package.json +1 -1
- package/dist/ar-D_6hknjh.js.map +0 -1
- package/dist/de-DQqSsBsZ.js.map +0 -1
- package/dist/en-Broh-9df.js.map +0 -1
- package/dist/es-D3eEYN6w.js.map +0 -1
- package/dist/fr-CbXPRKeU.js.map +0 -1
- package/dist/id-DxVqvPpo.js.map +0 -1
- package/dist/ja-D2dIWUse.js.map +0 -1
- package/dist/pt-BAuHdglU.js.map +0 -1
- package/dist/slugify-DzXnx16m.js.map +0 -1
- package/dist/zh-CMyzTdQ5.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,
|
|
2
|
-
import { computed as N, onScopeDispose as P, reactive as
|
|
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-D1PEZIU9.js";
|
|
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 _e, deleteField as ve, doc as ye, documentId as R, endAt as be, endBefore as xe, getDoc as z, getDocs as B, increment as V, limit as H, limitToLast as Se, onSnapshot as U, orderBy as W, query as G, runTransaction as Ce, serverTimestamp as K, setDoc as q, startAfter as J, updateDoc as Y, where as X, writeBatch as we } from "firebase/firestore";
|
|
4
|
+
import { useCollection as Te, useCurrentUser as Ee, useDocument as De, useFirebaseApp as Oe, useFirebaseAuth as ke, useFirestore as Z } from "vuefire";
|
|
5
|
+
import { GoogleAuthProvider as Ae, OAuthProvider as je, createUserWithEmailAndPassword as Me, signInWithEmailAndPassword as Ne, signInWithPopup as Pe, signOut as Fe, updateProfile as Ie } from "firebase/auth";
|
|
6
|
+
import { deleteObject as Le, getStorage as Re, listAll as ze, ref as Be } from "firebase/storage";
|
|
7
|
+
import { GoogleAIBackend as Ve, Schema as He, getAI as Ue, getGenerativeModel as We } from "firebase/ai";
|
|
8
8
|
//#region src/utils/boardSeo.ts
|
|
9
|
-
function
|
|
10
|
-
return !e || e === "전체" || e ===
|
|
9
|
+
function Ge(e, t) {
|
|
10
|
+
return !e || e === "전체" || e === k(t, "seo.allCategory");
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function Ke(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 qe(e) {
|
|
18
|
+
let t = Ke(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 Ge(e) {
|
|
|
32
32
|
return t;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function Je(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 ? Je(n, t) : r;
|
|
38
38
|
}
|
|
39
|
-
function
|
|
40
|
-
let a = r?.trim().slice(0, 40), o = e.trim() || a ||
|
|
39
|
+
function Ye(e, t, n, r, i) {
|
|
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 Xe(e, t) {
|
|
44
44
|
let n = [];
|
|
45
|
-
return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(
|
|
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 Ze(e, t, n) {
|
|
48
48
|
let r = t.trim() || "Board", i = e?.trim();
|
|
49
|
-
return
|
|
49
|
+
return Ge(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 Qe(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 = Ge(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 $e(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 et = [
|
|
66
66
|
"label",
|
|
67
67
|
"description",
|
|
68
68
|
"title",
|
|
69
69
|
"summary",
|
|
70
70
|
"contentHash"
|
|
71
71
|
];
|
|
72
|
-
function
|
|
72
|
+
function tt(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 nt(e, t) {
|
|
78
78
|
let n = e?.[t];
|
|
79
79
|
return typeof n == "string" ? n.trim() : "";
|
|
80
80
|
}
|
|
@@ -82,7 +82,7 @@ function Q(e) {
|
|
|
82
82
|
if (!e || typeof e != "object") return;
|
|
83
83
|
let t = {};
|
|
84
84
|
for (let [n, r] of Object.entries(e)) {
|
|
85
|
-
let e =
|
|
85
|
+
let e = M(n);
|
|
86
86
|
if (!e || !r || typeof r != "object") continue;
|
|
87
87
|
let i = {};
|
|
88
88
|
for (let [e, t] of Object.entries(r)) typeof t == "string" && t.trim() && (i[e] = t.trim());
|
|
@@ -90,21 +90,21 @@ function Q(e) {
|
|
|
90
90
|
}
|
|
91
91
|
return Object.keys(t).length ? t : void 0;
|
|
92
92
|
}
|
|
93
|
-
function
|
|
93
|
+
function rt(e) {
|
|
94
94
|
if (!e || typeof e != "object") return;
|
|
95
|
-
let t = e, n = {}, r =
|
|
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 et) typeof t[e] == "string" && (n[e] = t[e]);
|
|
98
98
|
return Object.keys(n).length ? n : void 0;
|
|
99
99
|
}
|
|
100
|
-
function
|
|
100
|
+
function it(e) {
|
|
101
101
|
if (!e || typeof e != "object") return;
|
|
102
|
-
let t = e, n = {}, r =
|
|
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 at(e, t, n) {
|
|
106
106
|
let r = {};
|
|
107
|
-
if (n) for (let e of
|
|
107
|
+
if (n) for (let e of T) {
|
|
108
108
|
let t = n[e.code];
|
|
109
109
|
if (!t) continue;
|
|
110
110
|
let i = {};
|
|
@@ -121,58 +121,58 @@ function rt(e, t, n) {
|
|
|
121
121
|
...i
|
|
122
122
|
}), r;
|
|
123
123
|
}
|
|
124
|
-
function
|
|
125
|
-
return !e || !t.length ? !1 :
|
|
126
|
-
}
|
|
127
|
-
function
|
|
128
|
-
if (!
|
|
129
|
-
let
|
|
130
|
-
if (!
|
|
131
|
-
let
|
|
132
|
-
if (
|
|
133
|
-
if (
|
|
134
|
-
let t =
|
|
124
|
+
function ot(e, t) {
|
|
125
|
+
return !e || !t.length ? !1 : T.every((n) => t.every((t) => !!nt(e[n.code], t)));
|
|
126
|
+
}
|
|
127
|
+
function st(e, t, r, i, a) {
|
|
128
|
+
if (!n()) return i;
|
|
129
|
+
let o = M(t), s = M(a);
|
|
130
|
+
if (!o || s && o === s) return i;
|
|
131
|
+
let c = nt(e?.[o], r);
|
|
132
|
+
if (c) return c;
|
|
133
|
+
if (o !== "en" && s !== "en") {
|
|
134
|
+
let t = nt(e?.en, r);
|
|
135
135
|
if (t) return t;
|
|
136
136
|
}
|
|
137
|
-
return
|
|
137
|
+
return i;
|
|
138
138
|
}
|
|
139
|
-
function
|
|
140
|
-
return r ? !t || t[n] !== r ? r :
|
|
139
|
+
function ct(e, t, n, r, i) {
|
|
140
|
+
return r ? !t || t[n] !== r ? r : st(e, i, n, r, t.locale) : "";
|
|
141
141
|
}
|
|
142
|
-
function
|
|
143
|
-
return
|
|
142
|
+
function lt(e, t) {
|
|
143
|
+
return ct(e.i18n, e.i18nSource, "title", e.title ?? "", t);
|
|
144
144
|
}
|
|
145
|
-
function
|
|
146
|
-
return
|
|
145
|
+
function ut(e, t) {
|
|
146
|
+
return ct(e.i18n, e.i18nSource, "summary", e.summary ?? "", t);
|
|
147
147
|
}
|
|
148
|
-
function
|
|
148
|
+
function dt(e, t) {
|
|
149
149
|
let n = e.content || "", r = e.contentI18nSource?.hash;
|
|
150
|
-
return !n || !r || r !==
|
|
150
|
+
return !n || !r || r !== tt(n) ? n : st(e.contentI18n, t, "content", n, e.contentI18nSource?.locale) || n;
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function ft(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 : ot(e.i18n, n ? ["label", "description"] : ["label"]);
|
|
155
155
|
}
|
|
156
156
|
//#endregion
|
|
157
157
|
//#region src/composables/fetchPublicSeo.ts
|
|
158
|
-
function
|
|
158
|
+
function pt() {
|
|
159
159
|
return {
|
|
160
160
|
db: Z(),
|
|
161
|
-
paths:
|
|
161
|
+
paths: j()
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
-
async function
|
|
164
|
+
async function mt(e, t, n) {
|
|
165
165
|
try {
|
|
166
|
-
let r = await z(
|
|
166
|
+
let r = await z(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 = rt(i.i18nSource);
|
|
173
173
|
return {
|
|
174
|
-
label:
|
|
175
|
-
description:
|
|
174
|
+
label: ct(s, c, "label", a, n) || a,
|
|
175
|
+
description: ct(s, c, "description", o, n),
|
|
176
176
|
hidden: i.visibility === "hidden"
|
|
177
177
|
};
|
|
178
178
|
} catch {
|
|
@@ -183,7 +183,7 @@ async function ft(e, t, n) {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
-
function
|
|
186
|
+
function ht(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 pt(e) {
|
|
|
192
192
|
}
|
|
193
193
|
return null;
|
|
194
194
|
}
|
|
195
|
-
async function
|
|
195
|
+
async function gt(e, t, n, r) {
|
|
196
196
|
let i = e.trim(), a = t.trim();
|
|
197
197
|
if (!i || !a) return null;
|
|
198
|
-
let
|
|
199
|
-
|
|
200
|
-
let c = await
|
|
198
|
+
let s = n ?? pt();
|
|
199
|
+
s.paths = C(s.paths), r && await ce(r);
|
|
200
|
+
let c = await mt(s, i, r);
|
|
201
201
|
if (c.hidden) return null;
|
|
202
202
|
try {
|
|
203
|
-
let e = (await B(G(s
|
|
203
|
+
let e = (await B(G(o(s.db, s.paths), X("boardId", "==", i), X("slug", "==", a), X("listed", "==", !0), X("isPublished", "==", !0), H(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 = rt(t.i18nSource), f = lt({
|
|
206
206
|
title: n,
|
|
207
207
|
i18n: u,
|
|
208
208
|
i18nSource: d
|
|
@@ -213,24 +213,24 @@ async function mt(e, t, n, r) {
|
|
|
213
213
|
category: i,
|
|
214
214
|
categoryLabel: c.label,
|
|
215
215
|
title: f,
|
|
216
|
-
summary:
|
|
216
|
+
summary: ut({
|
|
217
217
|
summary: l,
|
|
218
218
|
i18n: u,
|
|
219
219
|
i18nSource: d
|
|
220
220
|
}, r).trim().slice(0, 200),
|
|
221
|
-
previewImage:
|
|
221
|
+
previewImage: qe(t.previewImage),
|
|
222
222
|
authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
|
|
223
|
-
createdAt:
|
|
223
|
+
createdAt: ht(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
|
|
230
|
-
n && await
|
|
231
|
-
let r = e?.trim() || null, i = t ??
|
|
232
|
-
if (i.paths =
|
|
233
|
-
let e = await
|
|
229
|
+
async function _t(e, t, n) {
|
|
230
|
+
n && await ce(n);
|
|
231
|
+
let r = e?.trim() || null, i = t ?? pt();
|
|
232
|
+
if (i.paths = C(i.paths), r) {
|
|
233
|
+
let e = await mt(i, r, n);
|
|
234
234
|
if (e.hidden) return {
|
|
235
235
|
kind: "category",
|
|
236
236
|
category: r,
|
|
@@ -239,12 +239,12 @@ async function ht(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 vt(i, r, n);
|
|
243
243
|
return {
|
|
244
244
|
kind: "category",
|
|
245
245
|
category: r,
|
|
246
246
|
categoryLabel: e.label,
|
|
247
|
-
description: e.description ||
|
|
247
|
+
description: e.description || k(n, "seo.boardDescription", { name: e.label }),
|
|
248
248
|
ogImage: a,
|
|
249
249
|
recentTitles: t
|
|
250
250
|
};
|
|
@@ -252,23 +252,23 @@ async function ht(e, t, n) {
|
|
|
252
252
|
return {
|
|
253
253
|
kind: "all",
|
|
254
254
|
category: null,
|
|
255
|
-
categoryLabel:
|
|
256
|
-
description:
|
|
255
|
+
categoryLabel: k(n, "seo.allCategory"),
|
|
256
|
+
description: k(n, "seo.allDescription"),
|
|
257
257
|
ogImage: null,
|
|
258
258
|
recentTitles: []
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
-
async function
|
|
261
|
+
async function vt(e, t, n) {
|
|
262
262
|
let r = [], i = null;
|
|
263
263
|
try {
|
|
264
|
-
let a = await B(G(
|
|
264
|
+
let a = await B(G(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), W("createdAt", "desc"), H(12)));
|
|
265
265
|
for (let e of a.docs) {
|
|
266
|
-
let t = e.data() ?? {}, a =
|
|
266
|
+
let t = e.data() ?? {}, a = lt({
|
|
267
267
|
title: typeof t.title == "string" ? t.title : "",
|
|
268
268
|
i18n: Q(t.i18n),
|
|
269
|
-
i18nSource:
|
|
269
|
+
i18nSource: rt(t.i18nSource)
|
|
270
270
|
}, n).trim();
|
|
271
|
-
a && r.length < 5 && r.push(a), i ||=
|
|
271
|
+
a && r.length < 5 && r.push(a), i ||= qe(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 gt(e, t, n) {
|
|
|
280
280
|
}
|
|
281
281
|
//#endregion
|
|
282
282
|
//#region src/utils/boardAccess.ts
|
|
283
|
-
function
|
|
283
|
+
function yt(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 bt(e, t, n, r) {
|
|
287
|
+
return e ? t ? !0 : n ? yt(e, r) : !1 : !1;
|
|
288
288
|
}
|
|
289
|
-
function
|
|
289
|
+
function xt(e, t, n, r) {
|
|
290
290
|
if (!e) return !1;
|
|
291
|
-
if (r?.visibility === "hidden") return
|
|
291
|
+
if (r?.visibility === "hidden") return bt(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" || bt(e, t, n, r);
|
|
294
294
|
}
|
|
295
295
|
//#endregion
|
|
296
296
|
//#region src/composables/boardLookup.ts
|
|
297
|
-
var
|
|
298
|
-
function
|
|
299
|
-
|
|
297
|
+
var St;
|
|
298
|
+
function Ct(e) {
|
|
299
|
+
St = e;
|
|
300
300
|
}
|
|
301
|
-
function
|
|
302
|
-
return
|
|
301
|
+
function wt(e) {
|
|
302
|
+
return St?.(e);
|
|
303
303
|
}
|
|
304
304
|
//#endregion
|
|
305
305
|
//#region src/utils/moderation-strike.ts
|
|
306
|
-
var
|
|
307
|
-
function
|
|
306
|
+
var Tt = 10, Et = 864e5;
|
|
307
|
+
function Dt(e, t, n = Date.now(), r = Et) {
|
|
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 Tt(e, t, n = Date.now(), r = wt) {
|
|
|
312
312
|
let o = Math.floor((n - a) / r);
|
|
313
313
|
return Math.max(0, i - o);
|
|
314
314
|
}
|
|
315
|
-
function
|
|
315
|
+
function Ot(e, t, n = Date.now(), r = 10, i = Et) {
|
|
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,20 +320,20 @@ function Et(e, t, n = Date.now(), r = 10, i = wt) {
|
|
|
320
320
|
let s = o + (a - (r - 1)) * i;
|
|
321
321
|
return s > n ? s : null;
|
|
322
322
|
}
|
|
323
|
-
function
|
|
324
|
-
return
|
|
323
|
+
function kt(e, t, n = Date.now(), r = 10, i = Et) {
|
|
324
|
+
return Ot(e, t, n, r, i) != null;
|
|
325
325
|
}
|
|
326
|
-
function
|
|
326
|
+
function At(e, t = Date.now(), n) {
|
|
327
327
|
let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
|
|
328
|
-
if (i <= 0) return
|
|
329
|
-
if (i < 24) return
|
|
328
|
+
if (i <= 0) return k(n, "moderationStrike.liftSoon");
|
|
329
|
+
if (i < 24) return k(n, "moderationStrike.liftInHours", { hours: i });
|
|
330
330
|
let a = Math.floor(i / 24), o = i % 24;
|
|
331
|
-
return o === 0 ?
|
|
331
|
+
return o === 0 ? k(n, "moderationStrike.liftInDays", { days: a }) : k(n, "moderationStrike.liftInDaysHours", {
|
|
332
332
|
days: a,
|
|
333
333
|
hours: o
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
|
-
function
|
|
336
|
+
function jt(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,45 +345,45 @@ function kt(e) {
|
|
|
345
345
|
}
|
|
346
346
|
//#endregion
|
|
347
347
|
//#region src/composables/useMemiBoardAuth.ts
|
|
348
|
-
var
|
|
349
|
-
function
|
|
348
|
+
var Mt = "__MEMI_BOARD_AUTH_STATE__";
|
|
349
|
+
function Nt() {
|
|
350
350
|
let e = globalThis;
|
|
351
|
-
return e[
|
|
351
|
+
return e[Mt] || (e[Mt] = {
|
|
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[Mt];
|
|
358
358
|
}
|
|
359
|
-
function
|
|
360
|
-
let e =
|
|
361
|
-
function
|
|
359
|
+
function Pt() {
|
|
360
|
+
let e = v(), { t, locale: n } = A(), r = Nt(), i = Z(), o = ke(), s = Ee(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
|
|
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 : Et;
|
|
368
368
|
}
|
|
369
369
|
function m(e) {
|
|
370
|
-
return
|
|
370
|
+
return h(i, j(), e);
|
|
371
371
|
}
|
|
372
|
-
function
|
|
373
|
-
if (
|
|
372
|
+
function g(e) {
|
|
373
|
+
if (c.value = [
|
|
374
374
|
"owner",
|
|
375
375
|
"admin",
|
|
376
376
|
"manager",
|
|
377
377
|
"staff",
|
|
378
378
|
"user"
|
|
379
379
|
].includes(String(e?.role)) ? e?.role : "user", !e) {
|
|
380
|
-
|
|
380
|
+
u.value = 0, d.value = null;
|
|
381
381
|
return;
|
|
382
382
|
}
|
|
383
|
-
|
|
383
|
+
u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = jt(e.moderationBlockAt);
|
|
384
384
|
}
|
|
385
|
-
async function
|
|
386
|
-
let t = m(e.uid), n =
|
|
385
|
+
async function _(e) {
|
|
386
|
+
let t = m(e.uid), n = a(i, j(), e.uid), r = await z(t);
|
|
387
387
|
if (!r.exists()) return await Promise.all([q(t, {
|
|
388
388
|
role: "user",
|
|
389
389
|
displayName: e.displayName,
|
|
@@ -391,58 +391,58 @@ function Mt() {
|
|
|
391
391
|
joinedAt: K(),
|
|
392
392
|
lastVisitAt: K(),
|
|
393
393
|
updatedAt: K()
|
|
394
|
-
}), q(n, { email: e.email })]),
|
|
395
|
-
let
|
|
396
|
-
|
|
397
|
-
let
|
|
394
|
+
}), q(n, { email: e.email })]), g({ moderationBlockCount: 0 }), "user";
|
|
395
|
+
let o = r.data();
|
|
396
|
+
g(o);
|
|
397
|
+
let s = typeof o.displayName == "string" && o.displayName.trim(), c = typeof o.photoURL == "string" && o.photoURL.trim(), l = {
|
|
398
398
|
lastVisitAt: K(),
|
|
399
399
|
updatedAt: K()
|
|
400
400
|
};
|
|
401
|
-
return !
|
|
401
|
+
return !s && e.displayName && (l.displayName = e.displayName), !c && e.photoURL && (l.photoURL = e.photoURL), await Promise.all([Y(t, l), q(n, { email: e.email }, { merge: !0 })]), [
|
|
402
402
|
"owner",
|
|
403
403
|
"admin",
|
|
404
404
|
"manager",
|
|
405
405
|
"staff",
|
|
406
406
|
"user"
|
|
407
|
-
].includes(String(
|
|
407
|
+
].includes(String(o.role)) ? o.role : "user";
|
|
408
408
|
}
|
|
409
|
-
async function
|
|
410
|
-
let e =
|
|
409
|
+
async function y() {
|
|
410
|
+
let e = s.value?.uid;
|
|
411
411
|
if (!e) {
|
|
412
|
-
|
|
412
|
+
g(void 0);
|
|
413
413
|
return;
|
|
414
414
|
}
|
|
415
415
|
let t = await z(m(e));
|
|
416
|
-
|
|
416
|
+
g(t.exists() ? t.data() : void 0);
|
|
417
417
|
}
|
|
418
|
-
r.watchStarted || (r.watchStarted = !0, L(
|
|
419
|
-
if (
|
|
420
|
-
|
|
418
|
+
r.watchStarted || (r.watchStarted = !0, L(s, async (e) => {
|
|
419
|
+
if (l.value = !0, !e) {
|
|
420
|
+
c.value = "user", g(void 0), l.value = !1;
|
|
421
421
|
return;
|
|
422
422
|
}
|
|
423
423
|
try {
|
|
424
|
-
|
|
424
|
+
c.value = await _(e);
|
|
425
425
|
} catch (e) {
|
|
426
|
-
console.error("[memi-board] ensureUserDoc failed", e),
|
|
426
|
+
console.error("[memi-board] ensureUserDoc failed", e), c.value = "user", g(void 0);
|
|
427
427
|
} finally {
|
|
428
|
-
|
|
428
|
+
l.value = !1;
|
|
429
429
|
}
|
|
430
430
|
}, { immediate: !0 }));
|
|
431
|
-
let
|
|
432
|
-
let e =
|
|
433
|
-
return e <= 0 ? !1 :
|
|
434
|
-
}),
|
|
435
|
-
let e =
|
|
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(() => Dt(u.value, d.value, Date.now(), p())), D = N(() => {
|
|
432
|
+
let e = f();
|
|
433
|
+
return e <= 0 ? !1 : kt(u.value, d.value, Date.now(), e, p());
|
|
434
|
+
}), ee = N(() => {
|
|
435
|
+
let e = f();
|
|
436
436
|
if (e <= 0) return null;
|
|
437
|
-
let t =
|
|
438
|
-
return t == null ? null :
|
|
439
|
-
}),
|
|
440
|
-
if (!
|
|
441
|
-
let e =
|
|
437
|
+
let t = Ot(u.value, d.value, Date.now(), e, p());
|
|
438
|
+
return t == null ? null : At(t, Date.now(), n.value);
|
|
439
|
+
}), te = N(() => {
|
|
440
|
+
if (!D.value) return null;
|
|
441
|
+
let e = ee.value;
|
|
442
442
|
return e ? t("auth.writeRestrictedUntil", { until: e }) : t("auth.writeRestricted");
|
|
443
443
|
});
|
|
444
|
-
async function
|
|
445
|
-
let e =
|
|
444
|
+
async function ne() {
|
|
445
|
+
let e = s.value?.uid, r = f();
|
|
446
446
|
if (!e || r <= 0) return {
|
|
447
447
|
effectiveCount: 0,
|
|
448
448
|
restricted: !1,
|
|
@@ -455,125 +455,125 @@ function Mt() {
|
|
|
455
455
|
updatedAt: K()
|
|
456
456
|
}) : await q(i, {
|
|
457
457
|
role: "user",
|
|
458
|
-
displayName:
|
|
458
|
+
displayName: s.value?.displayName ?? null,
|
|
459
459
|
moderationBlockCount: 1,
|
|
460
460
|
moderationBlockAt: K(),
|
|
461
461
|
updatedAt: K()
|
|
462
462
|
});
|
|
463
|
-
let
|
|
464
|
-
|
|
465
|
-
let
|
|
466
|
-
count:
|
|
463
|
+
let o = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
|
|
464
|
+
u.value = o, d.value = c;
|
|
465
|
+
let l = Dt(o, c, c, p()), h = kt(o, c, c, r, p()), g = t("auth.strikeSuffix", {
|
|
466
|
+
count: l,
|
|
467
467
|
threshold: r
|
|
468
468
|
});
|
|
469
469
|
if (h) {
|
|
470
|
-
let e =
|
|
470
|
+
let e = Ot(o, c, c, r, p()), i = e == null ? null : At(e, c, n.value);
|
|
471
471
|
g = i ? t("auth.strikeRestrictedUntil", {
|
|
472
|
-
count:
|
|
472
|
+
count: l,
|
|
473
473
|
until: i
|
|
474
|
-
}) : t("auth.strikeRestricted", { count:
|
|
474
|
+
}) : t("auth.strikeRestricted", { count: l });
|
|
475
475
|
}
|
|
476
476
|
return {
|
|
477
|
-
effectiveCount:
|
|
477
|
+
effectiveCount: l,
|
|
478
478
|
restricted: h,
|
|
479
479
|
messageSuffix: g
|
|
480
480
|
};
|
|
481
481
|
}
|
|
482
|
-
function
|
|
483
|
-
if (!
|
|
484
|
-
return
|
|
485
|
-
}
|
|
486
|
-
async function te() {
|
|
487
|
-
return Me(j(), new Oe());
|
|
482
|
+
function O() {
|
|
483
|
+
if (!o) throw Error(t("auth.notInitialized"));
|
|
484
|
+
return o;
|
|
488
485
|
}
|
|
489
|
-
async function
|
|
490
|
-
|
|
491
|
-
return e.addScope("email"), e.addScope("name"), Me(j(), e);
|
|
486
|
+
async function re() {
|
|
487
|
+
return Pe(O(), new Ae());
|
|
492
488
|
}
|
|
493
|
-
async function
|
|
494
|
-
|
|
489
|
+
async function ie() {
|
|
490
|
+
let e = new je("apple.com");
|
|
491
|
+
return e.addScope("email"), e.addScope("name"), Pe(O(), e);
|
|
495
492
|
}
|
|
496
|
-
async function
|
|
497
|
-
|
|
498
|
-
return n && await Pe(r.user, { displayName: n }), r;
|
|
493
|
+
async function ae(e, t) {
|
|
494
|
+
return Ne(O(), e, t);
|
|
499
495
|
}
|
|
500
|
-
async function
|
|
501
|
-
await
|
|
496
|
+
async function oe(e, t, n) {
|
|
497
|
+
let r = await Me(O(), e, t);
|
|
498
|
+
return n && await Ie(r.user, { displayName: n }), r;
|
|
502
499
|
}
|
|
503
|
-
function
|
|
504
|
-
|
|
500
|
+
async function k() {
|
|
501
|
+
await Fe(O());
|
|
505
502
|
}
|
|
506
503
|
function se(e) {
|
|
507
|
-
return
|
|
504
|
+
return bt(s.value?.uid, C.value, w.value, e ? wt(e) : void 0);
|
|
508
505
|
}
|
|
509
506
|
function ce(e) {
|
|
510
|
-
return
|
|
507
|
+
return s.value ? C.value ? !0 : S.value && !!e && (wt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
|
|
511
508
|
}
|
|
512
509
|
function le(e) {
|
|
513
|
-
return
|
|
510
|
+
return b.value && (s.value?.uid === e.authorUid || se(e.boardId));
|
|
514
511
|
}
|
|
515
512
|
function ue(e) {
|
|
516
|
-
return
|
|
513
|
+
return b.value && s.value?.uid === e.authorUid;
|
|
514
|
+
}
|
|
515
|
+
function de(e) {
|
|
516
|
+
return b.value && (s.value?.uid === e.authorUid || se(e.boardId));
|
|
517
517
|
}
|
|
518
518
|
return {
|
|
519
|
-
user:
|
|
520
|
-
role:
|
|
521
|
-
isOwner:
|
|
522
|
-
isManager:
|
|
523
|
-
canConfigureBoard:
|
|
524
|
-
isSignedIn:
|
|
525
|
-
isAdmin:
|
|
526
|
-
isStaff:
|
|
527
|
-
canManageContent:
|
|
528
|
-
canManageBoard:
|
|
529
|
-
rolePending:
|
|
530
|
-
moderationBlockCount:
|
|
531
|
-
isWriteRestricted:
|
|
532
|
-
restrictedUntilLabel:
|
|
533
|
-
restrictedMessage:
|
|
534
|
-
signInWithGoogle:
|
|
535
|
-
signInWithApple:
|
|
536
|
-
signInWithEmail:
|
|
537
|
-
signUpWithEmail:
|
|
538
|
-
signOut:
|
|
539
|
-
canEdit:
|
|
540
|
-
canDelete:
|
|
541
|
-
canEditComment:
|
|
542
|
-
canDeleteComment:
|
|
543
|
-
recordContentModerationBlock:
|
|
544
|
-
refreshBoardUser:
|
|
519
|
+
user: s,
|
|
520
|
+
role: c,
|
|
521
|
+
isOwner: x,
|
|
522
|
+
isManager: S,
|
|
523
|
+
canConfigureBoard: ce,
|
|
524
|
+
isSignedIn: b,
|
|
525
|
+
isAdmin: C,
|
|
526
|
+
isStaff: w,
|
|
527
|
+
canManageContent: T,
|
|
528
|
+
canManageBoard: se,
|
|
529
|
+
rolePending: l,
|
|
530
|
+
moderationBlockCount: E,
|
|
531
|
+
isWriteRestricted: D,
|
|
532
|
+
restrictedUntilLabel: ee,
|
|
533
|
+
restrictedMessage: te,
|
|
534
|
+
signInWithGoogle: re,
|
|
535
|
+
signInWithApple: ie,
|
|
536
|
+
signInWithEmail: ae,
|
|
537
|
+
signUpWithEmail: oe,
|
|
538
|
+
signOut: k,
|
|
539
|
+
canEdit: le,
|
|
540
|
+
canDelete: le,
|
|
541
|
+
canEditComment: ue,
|
|
542
|
+
canDeleteComment: de,
|
|
543
|
+
recordContentModerationBlock: ne,
|
|
544
|
+
refreshBoardUser: y
|
|
545
545
|
};
|
|
546
546
|
}
|
|
547
547
|
//#endregion
|
|
548
548
|
//#region src/utils/imeSafeSubmit.ts
|
|
549
|
-
var
|
|
550
|
-
function
|
|
549
|
+
var Ft = /* @__PURE__ */ new WeakMap();
|
|
550
|
+
function It(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 Lt(e) {
|
|
555
555
|
if (e.pointerType !== "mouse" || e.button !== 0) return;
|
|
556
|
-
let t =
|
|
556
|
+
let t = It(e), n = t?.form;
|
|
557
557
|
if (!t || !n || t.disabled) return;
|
|
558
|
-
|
|
558
|
+
Ft.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 Rt(e) {
|
|
563
|
+
let t = It(e);
|
|
564
564
|
if (!t) return;
|
|
565
|
-
let n =
|
|
566
|
-
n && Date.now() < n && (e.preventDefault(),
|
|
565
|
+
let n = Ft.get(t);
|
|
566
|
+
n && Date.now() < n && (e.preventDefault(), Ft.delete(t));
|
|
567
567
|
}
|
|
568
568
|
//#endregion
|
|
569
569
|
//#region src/utils/findReadablePost.ts
|
|
570
|
-
async function
|
|
571
|
-
let
|
|
572
|
-
return a ? (await B(G(
|
|
570
|
+
async function zt(e, t, n, r, i, a) {
|
|
571
|
+
let s = [X("boardId", "==", n), X("slug", "==", r)];
|
|
572
|
+
return a ? (await B(G(o(e, t), ...s, H(1)))).docs[0] : (await Promise.all([B(G(o(e, t), ...s, X("listed", "==", !0), X("isPublished", "==", !0), H(1))), ...i ? [B(G(o(e, t), ...s, X("authorUid", "==", i), H(1)))] : []])).flatMap((e) => e.docs)[0];
|
|
573
573
|
}
|
|
574
574
|
//#endregion
|
|
575
575
|
//#region src/utils/extractEditorImageUrls.ts
|
|
576
|
-
function
|
|
576
|
+
function Bt(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 Rt(e) {
|
|
|
582
582
|
}
|
|
583
583
|
//#endregion
|
|
584
584
|
//#region src/utils/storagePath.ts
|
|
585
|
-
function
|
|
585
|
+
function Vt(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,106 +592,107 @@ function zt(e) {
|
|
|
592
592
|
return null;
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
|
-
function
|
|
595
|
+
function Ht(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 Ut(e) {
|
|
601
601
|
try {
|
|
602
|
-
let t = await
|
|
603
|
-
await Promise.all([...t.items.map((e) =>
|
|
602
|
+
let t = await ze(e);
|
|
603
|
+
await Promise.all([...t.items.map((e) => Le(e).catch(() => {})), ...t.prefixes.map((e) => Ut(e))]);
|
|
604
604
|
} catch {}
|
|
605
605
|
}
|
|
606
|
-
async function
|
|
607
|
-
await
|
|
606
|
+
async function Wt(e, t) {
|
|
607
|
+
await Ut(Be(e, `${t}/images`)), await Ut(Be(e, `${t}/attachments`)), await Ut(Be(e, `${t}/comment-images`));
|
|
608
608
|
}
|
|
609
|
-
|
|
609
|
+
var Gt = 450;
|
|
610
|
+
async function Kt(e, t, n) {
|
|
611
|
+
return (await B(G(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => de(e.data().images, `${x(t, n)}/comment-images/`));
|
|
612
|
+
}
|
|
613
|
+
async function qt(e, t) {
|
|
610
614
|
let n = [...new Set([...t].filter(Boolean))];
|
|
611
|
-
await Promise.all(n.map((t) =>
|
|
615
|
+
await Promise.all(n.map((t) => Le(Be(e, t)).catch(() => {})));
|
|
612
616
|
}
|
|
613
|
-
|
|
614
|
-
async function Gt(e, t) {
|
|
617
|
+
async function Jt(e, t) {
|
|
615
618
|
let n = await B(t);
|
|
616
|
-
for (let t = 0; t < n.docs.length; t +=
|
|
617
|
-
let r =
|
|
618
|
-
n.docs.slice(t, t +
|
|
619
|
+
for (let t = 0; t < n.docs.length; t += Gt) {
|
|
620
|
+
let r = we(e);
|
|
621
|
+
n.docs.slice(t, t + Gt).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
619
622
|
}
|
|
620
623
|
}
|
|
621
|
-
async function
|
|
622
|
-
await
|
|
623
|
-
}
|
|
624
|
-
async function
|
|
625
|
-
await
|
|
626
|
-
}
|
|
627
|
-
async function
|
|
628
|
-
let
|
|
629
|
-
if (!
|
|
630
|
-
let
|
|
631
|
-
|
|
632
|
-
let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
633
|
-
for (let e of u.attachments ?? []) {
|
|
624
|
+
async function Yt(e, t, n) {
|
|
625
|
+
await Jt(e, G(b(e, t), X("postId", "==", n)));
|
|
626
|
+
}
|
|
627
|
+
async function Xt(e, t, n) {
|
|
628
|
+
await Jt(e, G(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
|
|
629
|
+
}
|
|
630
|
+
async function Zt(e, t, n, r) {
|
|
631
|
+
let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([z(i), z(a)]);
|
|
632
|
+
if (!o.exists()) return;
|
|
633
|
+
let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await Kt(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
634
|
+
for (let e of l.attachments ?? []) {
|
|
634
635
|
if (!e?.path) continue;
|
|
635
636
|
f.add(e.path);
|
|
636
|
-
let t =
|
|
637
|
-
t && t !==
|
|
637
|
+
let t = Ht(e.path);
|
|
638
|
+
t && t !== r && p.add(t);
|
|
638
639
|
}
|
|
639
|
-
for (let e of
|
|
640
|
-
let t =
|
|
640
|
+
for (let e of Bt(u)) {
|
|
641
|
+
let t = Vt(e);
|
|
641
642
|
if (!t) continue;
|
|
642
643
|
f.add(t);
|
|
643
644
|
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
644
645
|
n !== t && n.includes("/images/thumbnails/") && f.add(n);
|
|
645
|
-
let
|
|
646
|
-
|
|
647
|
-
let a =
|
|
648
|
-
a && a !==
|
|
646
|
+
let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
647
|
+
i && !t.includes("/thumbnails/") && f.add(`${i[1]}thumbnails/${i[2]}.jpg`);
|
|
648
|
+
let a = Ht(t);
|
|
649
|
+
a && a !== r && p.add(a);
|
|
649
650
|
}
|
|
650
|
-
await
|
|
651
|
-
for (let e of p) await
|
|
652
|
-
await
|
|
651
|
+
await Wt(t, x(n, r));
|
|
652
|
+
for (let e of p) await Wt(t, x(n, e));
|
|
653
|
+
await qt(t, d), await qt(t, f), await Jt(e, G(g(e, n), X("postId", "==", r))), await Yt(e, n, r), await _e(a), await _e(i);
|
|
653
654
|
}
|
|
654
655
|
//#endregion
|
|
655
656
|
//#region src/utils/postBody.ts
|
|
656
|
-
function
|
|
657
|
+
function Qt(e) {
|
|
657
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();
|
|
658
659
|
}
|
|
659
|
-
function
|
|
660
|
-
let n =
|
|
660
|
+
function $t(e, t = 40) {
|
|
661
|
+
let n = Qt(e);
|
|
661
662
|
if (!n) return "";
|
|
662
663
|
if (n.length <= t) return n;
|
|
663
664
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
664
665
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
665
666
|
}
|
|
666
|
-
function
|
|
667
|
-
return
|
|
667
|
+
function en(e) {
|
|
668
|
+
return Qt(e).length > 0;
|
|
668
669
|
}
|
|
669
|
-
function
|
|
670
|
+
function tn(e, t) {
|
|
670
671
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
671
672
|
}
|
|
672
|
-
function
|
|
673
|
-
return !
|
|
673
|
+
function nn(e) {
|
|
674
|
+
return !en(e);
|
|
674
675
|
}
|
|
675
676
|
//#endregion
|
|
676
677
|
//#region src/utils/postPreview.ts
|
|
677
|
-
function
|
|
678
|
+
function rn(e) {
|
|
678
679
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
679
680
|
}
|
|
680
|
-
function
|
|
681
|
+
function an(e) {
|
|
681
682
|
if (e) try {
|
|
682
|
-
let t = new URL(
|
|
683
|
+
let t = new URL(rn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
683
684
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
684
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;
|
|
685
686
|
} catch {
|
|
686
687
|
return;
|
|
687
688
|
}
|
|
688
689
|
}
|
|
689
|
-
function
|
|
690
|
-
let t =
|
|
690
|
+
function on(e) {
|
|
691
|
+
let t = an(e.videoUrl);
|
|
691
692
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
692
693
|
}
|
|
693
|
-
function
|
|
694
|
-
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 sn(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 ? rn(r[1]) : a, s = o ? an(o) : void 0, c = n ? rn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
695
696
|
return {
|
|
696
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),
|
|
697
698
|
...c ? { previewImage: c } : {},
|
|
@@ -700,33 +701,33 @@ function rn(e, t = []) {
|
|
|
700
701
|
}
|
|
701
702
|
//#endregion
|
|
702
703
|
//#region src/utils/saveBoardVisibility.ts
|
|
703
|
-
function
|
|
704
|
+
function cn(e) {
|
|
704
705
|
return e === "hidden" ? "hidden" : "public";
|
|
705
706
|
}
|
|
706
|
-
async function
|
|
707
|
+
async function ln(e, t, n, r) {
|
|
707
708
|
let i;
|
|
708
709
|
for (;;) {
|
|
709
710
|
let a = await B(G(t, X("boardId", "==", n), W("__name__"), ...i ? [J(i)] : [], H(400)));
|
|
710
711
|
if (a.empty) break;
|
|
711
|
-
let o =
|
|
712
|
+
let o = we(e), s = 0;
|
|
712
713
|
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
713
714
|
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
714
715
|
}
|
|
715
716
|
}
|
|
716
|
-
async function
|
|
717
|
-
await
|
|
717
|
+
async function un(e, t, n, r) {
|
|
718
|
+
await ln(e, o(e, t), n, r), await ln(e, g(e, t), n, r);
|
|
718
719
|
}
|
|
719
|
-
async function
|
|
720
|
-
let i =
|
|
721
|
-
await
|
|
720
|
+
async function dn(e, t, n, r) {
|
|
721
|
+
let i = w(e, t, n), a = cn(r.visibility);
|
|
722
|
+
await Ce(e, async (e) => {
|
|
722
723
|
let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
|
|
723
724
|
if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
|
|
724
|
-
return !t.exists() || !o &&
|
|
725
|
+
return !t.exists() || !o && cn(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
|
|
725
726
|
...r,
|
|
726
|
-
visibility: a === "public" ? "public" :
|
|
727
|
+
visibility: a === "public" ? "public" : cn(n?.visibility),
|
|
727
728
|
visibilityTransition: a
|
|
728
729
|
}, { merge: !0 }), !0);
|
|
729
|
-
}) && (await
|
|
730
|
+
}) && (await un(e, t, n, a !== "hidden"), await Ce(e, async (e) => {
|
|
730
731
|
if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
|
|
731
732
|
e.update(i, {
|
|
732
733
|
visibility: a,
|
|
@@ -737,7 +738,7 @@ async function cn(e, t, n, r) {
|
|
|
737
738
|
}
|
|
738
739
|
//#endregion
|
|
739
740
|
//#region src/utils/moderation-prompt.ts
|
|
740
|
-
var
|
|
741
|
+
var fn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", pn = {
|
|
741
742
|
ko: "Korean",
|
|
742
743
|
en: "English",
|
|
743
744
|
ja: "Japanese",
|
|
@@ -749,12 +750,12 @@ var ln = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
749
750
|
ar: "Arabic",
|
|
750
751
|
id: "Indonesian"
|
|
751
752
|
};
|
|
752
|
-
function
|
|
753
|
-
let t =
|
|
753
|
+
function mn(e) {
|
|
754
|
+
let t = pn[e] ?? "English", n = pe(e).name;
|
|
754
755
|
return t === n ? t : `${t} (${n})`;
|
|
755
756
|
}
|
|
756
|
-
function
|
|
757
|
-
let t =
|
|
757
|
+
function hn(e) {
|
|
758
|
+
let t = mn(e);
|
|
758
759
|
return `You are a strict moderator reviewing content for a message board.
|
|
759
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).
|
|
760
761
|
|
|
@@ -781,17 +782,17 @@ Output JSON only:
|
|
|
781
782
|
Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
|
|
782
783
|
- If flagged=false, category is "none" and reason is ""`;
|
|
783
784
|
}
|
|
784
|
-
function
|
|
785
|
-
return e === "ko" ?
|
|
785
|
+
function gn(e = "ko") {
|
|
786
|
+
return e === "ko" ? fn : hn(e);
|
|
786
787
|
}
|
|
787
|
-
function
|
|
788
|
+
function _n(e, t = "ko") {
|
|
788
789
|
let n = `"""${e.slice(0, 4e3)}"""`;
|
|
789
790
|
return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
|
|
790
791
|
}
|
|
791
|
-
function
|
|
792
|
+
function vn(e = "ko") {
|
|
792
793
|
return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
|
|
793
794
|
}
|
|
794
|
-
var
|
|
795
|
+
var yn = {
|
|
795
796
|
abuse: "욕설·비속어·모욕",
|
|
796
797
|
spam: "스팸·광고",
|
|
797
798
|
adult: "선정적·성인 내용",
|
|
@@ -799,21 +800,21 @@ var gn = {
|
|
|
799
800
|
other: "부적절한 내용",
|
|
800
801
|
none: ""
|
|
801
802
|
};
|
|
802
|
-
function
|
|
803
|
-
return !e || e === "none" ? "" : e in
|
|
803
|
+
function bn(e, t = "ko") {
|
|
804
|
+
return !e || e === "none" ? "" : e in yn ? k(t, `moderationPrompt.categories.${e}`) : e;
|
|
804
805
|
}
|
|
805
|
-
function
|
|
806
|
+
function xn(e) {
|
|
806
807
|
let t = e.locale ?? "ko";
|
|
807
808
|
if (e.via === "local" && e.localHit) {
|
|
808
809
|
let n = e.localHit.trim();
|
|
809
|
-
return n.length > 0 && n.length <= 12 ?
|
|
810
|
+
return n.length > 0 && n.length <= 12 ? k(t, "moderationPrompt.localHit", { hit: n }) : k(t, "moderationPrompt.localGeneric");
|
|
810
811
|
}
|
|
811
812
|
let n = (e.reason || "").trim();
|
|
812
813
|
if (n) return n;
|
|
813
|
-
let r = e.category ?
|
|
814
|
-
return r ?
|
|
814
|
+
let r = e.category ? bn(e.category, t) : "";
|
|
815
|
+
return r ? k(t, "moderationPrompt.categoryBlocked", { category: r }) : k(t, "moderationPrompt.genericBlocked");
|
|
815
816
|
}
|
|
816
|
-
var
|
|
817
|
+
var Sn = [
|
|
817
818
|
"씨발",
|
|
818
819
|
"시발",
|
|
819
820
|
"시이발",
|
|
@@ -839,10 +840,10 @@ var yn = [
|
|
|
839
840
|
"bitch",
|
|
840
841
|
"asshole"
|
|
841
842
|
];
|
|
842
|
-
function
|
|
843
|
+
function Cn(e) {
|
|
843
844
|
return new RegExp(e.join("|"), "i");
|
|
844
845
|
}
|
|
845
|
-
var
|
|
846
|
+
var wn = /* @__PURE__ */ new Set([
|
|
846
847
|
"none",
|
|
847
848
|
"abuse",
|
|
848
849
|
"spam",
|
|
@@ -850,7 +851,7 @@ var xn = /* @__PURE__ */ new Set([
|
|
|
850
851
|
"violence",
|
|
851
852
|
"other"
|
|
852
853
|
]);
|
|
853
|
-
function
|
|
854
|
+
function Tn(e) {
|
|
854
855
|
let t = e.trim();
|
|
855
856
|
if (!t) return null;
|
|
856
857
|
let n;
|
|
@@ -868,7 +869,7 @@ function Sn(e) {
|
|
|
868
869
|
if (typeof n != "object" || !n) return null;
|
|
869
870
|
let r = n;
|
|
870
871
|
if (typeof r.flagged != "boolean") return null;
|
|
871
|
-
let i = typeof r.category == "string" &&
|
|
872
|
+
let i = typeof r.category == "string" && wn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
872
873
|
return {
|
|
873
874
|
flagged: r.flagged,
|
|
874
875
|
category: i,
|
|
@@ -877,11 +878,11 @@ function Sn(e) {
|
|
|
877
878
|
}
|
|
878
879
|
//#endregion
|
|
879
880
|
//#region src/composables/useMemiBoardAutoTranslate.ts
|
|
880
|
-
var
|
|
881
|
-
function
|
|
881
|
+
var En = 3e4, Dn = 45e3, On = 12e3, kn = 3;
|
|
882
|
+
function An(e) {
|
|
882
883
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
883
884
|
}
|
|
884
|
-
async function
|
|
885
|
+
async function jn(e, t, n) {
|
|
885
886
|
let r = Array(e.length), i = 0;
|
|
886
887
|
async function a() {
|
|
887
888
|
for (; i < e.length;) {
|
|
@@ -891,7 +892,7 @@ async function On(e, t, n) {
|
|
|
891
892
|
}
|
|
892
893
|
return await Promise.all(Array.from({ length: Math.min(t, e.length) }, () => a())), r;
|
|
893
894
|
}
|
|
894
|
-
function
|
|
895
|
+
function Mn(e, t) {
|
|
895
896
|
return new Promise((n, r) => {
|
|
896
897
|
let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
|
|
897
898
|
e.then((e) => {
|
|
@@ -901,14 +902,14 @@ function kn(e, t) {
|
|
|
901
902
|
});
|
|
902
903
|
});
|
|
903
904
|
}
|
|
904
|
-
function
|
|
905
|
-
let e =
|
|
905
|
+
function Nn() {
|
|
906
|
+
let e = Oe(), { moderation: t } = v();
|
|
906
907
|
function n(n, r) {
|
|
907
|
-
let i =
|
|
908
|
-
backend: new
|
|
908
|
+
let i = Ue(e, {
|
|
909
|
+
backend: new Ve(),
|
|
909
910
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
910
|
-
}), a =
|
|
911
|
-
return
|
|
911
|
+
}), a = He.object({ properties: Object.fromEntries(r.map((e) => [e, He.string()])) });
|
|
912
|
+
return We(i, {
|
|
912
913
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
913
914
|
systemInstruction: [
|
|
914
915
|
"You translate short user-interface strings for a website.",
|
|
@@ -920,19 +921,19 @@ function An() {
|
|
|
920
921
|
temperature: .2,
|
|
921
922
|
maxOutputTokens: 2048,
|
|
922
923
|
responseMimeType: "application/json",
|
|
923
|
-
responseSchema:
|
|
924
|
+
responseSchema: He.object({ properties: Object.fromEntries(n.map((e) => [e, a])) })
|
|
924
925
|
}
|
|
925
926
|
});
|
|
926
927
|
}
|
|
927
928
|
async function r(e, t, r, i) {
|
|
928
929
|
let a = Object.keys(e.fields), o = [
|
|
929
930
|
`Purpose: ${i}`,
|
|
930
|
-
`Source language: ${
|
|
931
|
-
`Translate into: ${r.map((e) => `${e} = ${
|
|
931
|
+
`Source language: ${mn(t)} (${t})`,
|
|
932
|
+
`Translate into: ${r.map((e) => `${e} = ${mn(e)}`).join(", ")}`,
|
|
932
933
|
e.context ? `Context (do not translate): ${e.context}` : "",
|
|
933
934
|
"Text (JSON):",
|
|
934
935
|
JSON.stringify(e.fields)
|
|
935
|
-
].filter(Boolean).join("\n"), s = await
|
|
936
|
+
].filter(Boolean).join("\n"), s = await Mn(n(r, a).generateContent(o), En), c = JSON.parse(s.response.text()), l = {};
|
|
936
937
|
for (let e of r) {
|
|
937
938
|
let t = c?.[e];
|
|
938
939
|
if (!t) continue;
|
|
@@ -946,8 +947,8 @@ function An() {
|
|
|
946
947
|
return l;
|
|
947
948
|
}
|
|
948
949
|
async function i(e, t = {}) {
|
|
949
|
-
|
|
950
|
-
let n = t.source ?? "ko", i = (t.targets ??
|
|
950
|
+
D();
|
|
951
|
+
let n = t.source ?? "ko", i = (t.targets ?? T.map((e) => e.code)).filter((e) => e !== n), a = t.purpose ?? "website UI labels", o = e.map((e) => ({
|
|
951
952
|
...e,
|
|
952
953
|
fields: Object.fromEntries(Object.entries(e.fields).map(([e, t]) => [e, t.trim()]).filter(([, e]) => e))
|
|
953
954
|
})).filter((e) => Object.keys(e.fields).length);
|
|
@@ -956,11 +957,11 @@ function An() {
|
|
|
956
957
|
return Object.fromEntries(o.map((e, t) => [e.id, s[t]]));
|
|
957
958
|
}
|
|
958
959
|
function a() {
|
|
959
|
-
let n =
|
|
960
|
-
backend: new
|
|
960
|
+
let n = Ue(e, {
|
|
961
|
+
backend: new Ve(),
|
|
961
962
|
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
962
963
|
});
|
|
963
|
-
return
|
|
964
|
+
return We(n, {
|
|
964
965
|
model: t.model ?? "gemini-3.5-flash-lite",
|
|
965
966
|
systemInstruction: [
|
|
966
967
|
"You translate an HTML fragment of a message-board post.",
|
|
@@ -975,17 +976,17 @@ function An() {
|
|
|
975
976
|
});
|
|
976
977
|
}
|
|
977
978
|
async function o(e, t = {}) {
|
|
978
|
-
|
|
979
|
-
let n = t.source ?? "ko", r = (t.targets ??
|
|
979
|
+
D();
|
|
980
|
+
let n = t.source ?? "ko", r = (t.targets ?? T.map((e) => e.code)).filter((e) => e !== n), i = e.trim();
|
|
980
981
|
if (!i || !r.length) return {};
|
|
981
|
-
if (i.length > 12e3) throw Error(`translation is limited to ${
|
|
982
|
-
let o = await
|
|
982
|
+
if (i.length > 12e3) throw Error(`translation is limited to ${On} characters`);
|
|
983
|
+
let o = await jn(r, kn, async (e) => {
|
|
983
984
|
let t = [
|
|
984
|
-
`Source language: ${
|
|
985
|
-
`Translate into: ${e} = ${
|
|
985
|
+
`Source language: ${mn(n)} (${n})`,
|
|
986
|
+
`Translate into: ${e} = ${mn(e)}`,
|
|
986
987
|
"HTML:",
|
|
987
988
|
i
|
|
988
|
-
].join("\n"), r =
|
|
989
|
+
].join("\n"), r = An((await Mn(a().generateContent(t), Dn)).response.text());
|
|
989
990
|
if (!r) throw Error(`empty translation for ${e}`);
|
|
990
991
|
return [e, r];
|
|
991
992
|
});
|
|
@@ -998,7 +999,7 @@ function An() {
|
|
|
998
999
|
}
|
|
999
1000
|
//#endregion
|
|
1000
1001
|
//#region src/composables/useMemiBoardSettings.ts
|
|
1001
|
-
var
|
|
1002
|
+
var Pn = [
|
|
1002
1003
|
{
|
|
1003
1004
|
id: "free",
|
|
1004
1005
|
label: "자유",
|
|
@@ -1026,17 +1027,17 @@ var jn = [
|
|
|
1026
1027
|
visibility: "public",
|
|
1027
1028
|
order: 2
|
|
1028
1029
|
}
|
|
1029
|
-
],
|
|
1030
|
-
function
|
|
1030
|
+
], Fn = Pn;
|
|
1031
|
+
function In(e) {
|
|
1031
1032
|
return e === "hidden" ? "hidden" : "public";
|
|
1032
1033
|
}
|
|
1033
|
-
function
|
|
1034
|
+
function Ln(e, t, n) {
|
|
1034
1035
|
return {
|
|
1035
1036
|
id: e,
|
|
1036
1037
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
1037
1038
|
order: typeof t.order == "number" ? t.order : n,
|
|
1038
1039
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
1039
|
-
visibility:
|
|
1040
|
+
visibility: In(t.visibility),
|
|
1040
1041
|
visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
|
|
1041
1042
|
listView: t.listView ?? "default",
|
|
1042
1043
|
editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
|
|
@@ -1044,54 +1045,54 @@ function Pn(e, t, n) {
|
|
|
1044
1045
|
commentWriteRole: t.commentWriteRole ?? "user",
|
|
1045
1046
|
allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : [],
|
|
1046
1047
|
allowedManagerUids: Array.isArray(t.allowedManagerUids) ? t.allowedManagerUids : [],
|
|
1047
|
-
locale:
|
|
1048
|
+
locale: M(t.locale) ?? "",
|
|
1048
1049
|
autoTranslate: t.autoTranslate === !0,
|
|
1049
1050
|
i18n: Q(t.i18n),
|
|
1050
|
-
i18nSource:
|
|
1051
|
+
i18nSource: rt(t.i18nSource)
|
|
1051
1052
|
};
|
|
1052
1053
|
}
|
|
1053
|
-
function
|
|
1054
|
-
let
|
|
1054
|
+
function Rn() {
|
|
1055
|
+
let e = () => j(), t = Z(), r = Oe(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Pt(), { t: l, locale: u } = A(), { translateEntries: d } = Nn(), f = N(() => G(S(t, e()), W("order", "asc"))), { data: m, pending: h } = Te(f, { ssrKey: p(e(), "boards") }), g = N(() => m.value.map((e, t) => Ln(e.id, e, t))), _ = g, v = N(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
|
|
1055
1056
|
function b(e, t) {
|
|
1056
1057
|
if (!e) return;
|
|
1057
1058
|
let n = g.value.find((t) => t.id === e);
|
|
1058
|
-
return n ?
|
|
1059
|
+
return n ? ct(n.i18n, n.i18nSource, "label", n.label, t ?? u.value) || n.label : e;
|
|
1059
1060
|
}
|
|
1060
1061
|
let x = b;
|
|
1061
|
-
function
|
|
1062
|
+
function C(e, t) {
|
|
1062
1063
|
if (!e) return;
|
|
1063
1064
|
let n = g.value.find((t) => t.id === e), r = n?.description?.trim();
|
|
1064
|
-
if (n && r) return
|
|
1065
|
+
if (n && r) return ct(n.i18n, n.i18nSource, "description", r, t ?? u.value) || void 0;
|
|
1065
1066
|
}
|
|
1066
|
-
let
|
|
1067
|
-
function T(e) {
|
|
1068
|
-
return e ? g.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
|
|
1069
|
-
}
|
|
1070
|
-
let E = T;
|
|
1067
|
+
let T = C;
|
|
1071
1068
|
function D(e) {
|
|
1072
|
-
return
|
|
1069
|
+
return e ? g.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
|
|
1073
1070
|
}
|
|
1074
1071
|
let ee = D;
|
|
1075
|
-
function
|
|
1072
|
+
function te(e) {
|
|
1073
|
+
return D(e) === "hidden";
|
|
1074
|
+
}
|
|
1075
|
+
let ne = te;
|
|
1076
|
+
function O(e) {
|
|
1076
1077
|
if (e) return g.value.find((t) => t.id === e);
|
|
1077
1078
|
}
|
|
1078
|
-
|
|
1079
|
-
function
|
|
1080
|
-
return
|
|
1079
|
+
Ct((e) => O(e));
|
|
1080
|
+
function re(e) {
|
|
1081
|
+
return bt(c.value?.uid, a.value, s.value, e ? O(e) : void 0);
|
|
1081
1082
|
}
|
|
1082
|
-
let
|
|
1083
|
-
function
|
|
1083
|
+
let ie = N(() => g.value.filter((e) => re(e.id)));
|
|
1084
|
+
function ae(e, t) {
|
|
1084
1085
|
return {
|
|
1085
1086
|
label: e.label.trim(),
|
|
1086
1087
|
description: (e.description ?? "").trim(),
|
|
1087
|
-
visibility:
|
|
1088
|
+
visibility: In(e.visibility),
|
|
1088
1089
|
listView: e.listView ?? "default",
|
|
1089
1090
|
editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
|
|
1090
1091
|
writeRole: e.writeRole ?? "user",
|
|
1091
1092
|
commentWriteRole: e.commentWriteRole ?? "user",
|
|
1092
1093
|
allowedStaffUids: e.allowedStaffUids ?? [],
|
|
1093
1094
|
allowedManagerUids: e.allowedManagerUids ?? [],
|
|
1094
|
-
locale:
|
|
1095
|
+
locale: M(e.locale) ?? "",
|
|
1095
1096
|
autoTranslate: e.autoTranslate === !0,
|
|
1096
1097
|
...e.i18n ? { i18n: e.i18n } : {},
|
|
1097
1098
|
...e.i18nSource ? { i18nSource: e.i18nSource } : {},
|
|
@@ -1099,70 +1100,70 @@ function Fn() {
|
|
|
1099
1100
|
updatedAt: K()
|
|
1100
1101
|
};
|
|
1101
1102
|
}
|
|
1102
|
-
async function
|
|
1103
|
-
if (!
|
|
1104
|
-
let t = e.label.trim(),
|
|
1103
|
+
async function oe(e) {
|
|
1104
|
+
if (!n()) return e;
|
|
1105
|
+
let t = e.label.trim(), r = (e.description ?? "").trim();
|
|
1105
1106
|
if (!t) return e;
|
|
1106
|
-
if (
|
|
1107
|
+
if (ft({
|
|
1107
1108
|
...e,
|
|
1108
1109
|
label: t,
|
|
1109
|
-
description:
|
|
1110
|
+
description: r
|
|
1110
1111
|
})) return {
|
|
1111
1112
|
...e,
|
|
1112
1113
|
label: t,
|
|
1113
|
-
description:
|
|
1114
|
+
description: r
|
|
1114
1115
|
};
|
|
1115
|
-
let
|
|
1116
|
-
|
|
1117
|
-
let
|
|
1116
|
+
let i = u.value, a = { label: t };
|
|
1117
|
+
r && (a.description = r);
|
|
1118
|
+
let o = at(i, a, (await d([{
|
|
1118
1119
|
id: e.id || "board",
|
|
1119
|
-
fields:
|
|
1120
|
+
fields: a,
|
|
1120
1121
|
context: e.id
|
|
1121
1122
|
}], {
|
|
1122
|
-
source:
|
|
1123
|
+
source: i,
|
|
1123
1124
|
purpose: "board name and short description shown in navigation and page headers"
|
|
1124
1125
|
}))[e.id || "board"]);
|
|
1125
|
-
if (!
|
|
1126
|
+
if (!ot(o, Object.keys(a))) throw Error(l("settings.autoTranslate.failed"));
|
|
1126
1127
|
return {
|
|
1127
1128
|
...e,
|
|
1128
1129
|
label: t,
|
|
1129
|
-
description:
|
|
1130
|
-
i18n:
|
|
1130
|
+
description: r,
|
|
1131
|
+
i18n: o,
|
|
1131
1132
|
i18nSource: {
|
|
1132
|
-
locale:
|
|
1133
|
+
locale: i,
|
|
1133
1134
|
label: t,
|
|
1134
|
-
description:
|
|
1135
|
+
description: r
|
|
1135
1136
|
}
|
|
1136
1137
|
};
|
|
1137
1138
|
}
|
|
1138
|
-
async function
|
|
1139
|
-
if (!i.value) throw Error(
|
|
1140
|
-
let a =
|
|
1141
|
-
if (!a || !o) throw Error(
|
|
1142
|
-
let s =
|
|
1143
|
-
...
|
|
1139
|
+
async function k(n, r = n.order ?? 0) {
|
|
1140
|
+
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
1141
|
+
let a = n.id.trim(), o = n.label.trim();
|
|
1142
|
+
if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
|
|
1143
|
+
let s = In(n.visibility), c = ae({
|
|
1144
|
+
...n,
|
|
1144
1145
|
label: o,
|
|
1145
1146
|
visibility: s
|
|
1146
1147
|
}, r);
|
|
1147
|
-
await
|
|
1148
|
+
await dn(t, e(), a, c);
|
|
1148
1149
|
}
|
|
1149
|
-
let se =
|
|
1150
|
+
let se = k;
|
|
1150
1151
|
async function ce(e) {
|
|
1151
|
-
if (!i.value) throw Error(
|
|
1152
|
-
for (let t of e) if (!t.id.trim() || !t.label.trim()) throw Error(
|
|
1153
|
-
for (let [t, n] of e.entries()) await
|
|
1152
|
+
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
1153
|
+
for (let t of e) if (!t.id.trim() || !t.label.trim()) throw Error(l("boardSettings.idAndNameRequired"));
|
|
1154
|
+
for (let [t, n] of e.entries()) await k(n, t);
|
|
1154
1155
|
}
|
|
1155
1156
|
let le = ce;
|
|
1156
|
-
async function ue(
|
|
1157
|
-
if (!i.value) throw Error(
|
|
1158
|
-
let a =
|
|
1159
|
-
if (!a) throw Error(
|
|
1157
|
+
async function ue(n) {
|
|
1158
|
+
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
1159
|
+
let a = n.trim();
|
|
1160
|
+
if (!a) throw Error(l("boardSettings.deleteIdRequired"));
|
|
1160
1161
|
for (;;) {
|
|
1161
|
-
let
|
|
1162
|
-
if (
|
|
1163
|
-
for (let i of
|
|
1162
|
+
let n = await B(G(o(t, e()), X("boardId", "==", a), H(25)));
|
|
1163
|
+
if (n.empty) break;
|
|
1164
|
+
for (let i of n.docs) await Zt(t, Re(r), e(), i.id);
|
|
1164
1165
|
}
|
|
1165
|
-
await
|
|
1166
|
+
await _e(w(t, e(), n));
|
|
1166
1167
|
}
|
|
1167
1168
|
let de = ue;
|
|
1168
1169
|
async function fe() {
|
|
@@ -1170,13 +1171,13 @@ function Fn() {
|
|
|
1170
1171
|
}
|
|
1171
1172
|
async function pe(e) {
|
|
1172
1173
|
let t = e.trim();
|
|
1173
|
-
if (!t) throw Error(
|
|
1174
|
-
if (!i.value) throw Error(
|
|
1174
|
+
if (!t) throw Error(l("boardSettings.nameRequired"));
|
|
1175
|
+
if (!i.value) throw Error(l("boardSettings.signInRequired"));
|
|
1175
1176
|
let n = g.value.find((e) => e.label === t);
|
|
1176
1177
|
if (n) return n.id;
|
|
1177
|
-
let r =
|
|
1178
|
+
let r = E(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(g.value.map((e) => e.id));
|
|
1178
1179
|
for (; s.has(a);) a = `${r}-${o++}`;
|
|
1179
|
-
return await
|
|
1180
|
+
return await k({
|
|
1180
1181
|
id: a,
|
|
1181
1182
|
label: t,
|
|
1182
1183
|
visibility: "public",
|
|
@@ -1191,67 +1192,67 @@ function Fn() {
|
|
|
1191
1192
|
boards: g,
|
|
1192
1193
|
publicBoards: v,
|
|
1193
1194
|
boardLabel: b,
|
|
1194
|
-
boardDescription:
|
|
1195
|
-
boardVisibility:
|
|
1196
|
-
isBoardHidden:
|
|
1197
|
-
getBoard:
|
|
1198
|
-
canManageBoard:
|
|
1199
|
-
managedBoards:
|
|
1195
|
+
boardDescription: C,
|
|
1196
|
+
boardVisibility: D,
|
|
1197
|
+
isBoardHidden: te,
|
|
1198
|
+
getBoard: O,
|
|
1199
|
+
canManageBoard: re,
|
|
1200
|
+
managedBoards: ie,
|
|
1200
1201
|
categories: _,
|
|
1201
1202
|
publicCategories: y,
|
|
1202
1203
|
categoryLabel: x,
|
|
1203
|
-
categoryDescription:
|
|
1204
|
-
categoryVisibility:
|
|
1205
|
-
isCategoryHidden:
|
|
1204
|
+
categoryDescription: T,
|
|
1205
|
+
categoryVisibility: ee,
|
|
1206
|
+
isCategoryHidden: ne,
|
|
1206
1207
|
settingsPending: h,
|
|
1207
1208
|
ensureSettings: fe,
|
|
1208
1209
|
addBoard: pe,
|
|
1209
1210
|
addCategory: pe,
|
|
1210
|
-
saveBoard:
|
|
1211
|
+
saveBoard: k,
|
|
1211
1212
|
saveCategory: se,
|
|
1212
|
-
translateBoard:
|
|
1213
|
+
translateBoard: oe,
|
|
1213
1214
|
saveBoards: ce,
|
|
1214
1215
|
saveCategories: le,
|
|
1215
1216
|
deleteBoard: ue,
|
|
1216
1217
|
deleteCategory: de,
|
|
1217
|
-
DEFAULT_BOARDS:
|
|
1218
|
-
DEFAULT_CATEGORIES:
|
|
1218
|
+
DEFAULT_BOARDS: Pn,
|
|
1219
|
+
DEFAULT_CATEGORIES: Fn
|
|
1219
1220
|
};
|
|
1220
1221
|
}
|
|
1221
1222
|
//#endregion
|
|
1222
1223
|
//#region src/composables/useMemiBoardPosts.ts
|
|
1223
|
-
var
|
|
1224
|
-
function
|
|
1224
|
+
var zn = /* @__PURE__ */ new Map();
|
|
1225
|
+
function Bn(e) {
|
|
1225
1226
|
if (!e || typeof e != "object") return !1;
|
|
1226
1227
|
let t = "code" in e ? String(e.code) : "";
|
|
1227
1228
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
1228
1229
|
}
|
|
1229
|
-
function
|
|
1230
|
+
function Vn(e, t) {
|
|
1230
1231
|
let n = I(e)?.trim();
|
|
1231
|
-
if (!n) throw Error(
|
|
1232
|
+
if (!n) throw Error(k(t, "posts.boardIdRequired"));
|
|
1232
1233
|
return n;
|
|
1233
1234
|
}
|
|
1234
|
-
function
|
|
1235
|
-
let
|
|
1235
|
+
function Hn(e) {
|
|
1236
|
+
let t = Z(), n = Oe(), { isBoardHidden: r, getBoard: i } = Rn(), { user: a, canManageBoard: c } = Pt(), { t: l, locale: u } = A(), d = () => j(), f = () => Vn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
|
|
1236
1237
|
function g(e, t, n) {
|
|
1237
1238
|
return {
|
|
1238
1239
|
id: e,
|
|
1239
1240
|
...t,
|
|
1240
1241
|
i18n: Q(t.i18n),
|
|
1241
|
-
i18nSource:
|
|
1242
|
+
i18nSource: rt(t.i18nSource),
|
|
1242
1243
|
content: typeof n?.content == "string" ? n.content : "",
|
|
1243
1244
|
contentI18n: Q(n?.i18n),
|
|
1244
|
-
contentI18nSource:
|
|
1245
|
+
contentI18nSource: it(n?.i18nSource)
|
|
1245
1246
|
};
|
|
1246
1247
|
}
|
|
1247
1248
|
function _() {
|
|
1248
|
-
return !
|
|
1249
|
+
return !r(f());
|
|
1249
1250
|
}
|
|
1250
1251
|
function v() {
|
|
1251
|
-
return
|
|
1252
|
+
return i(f())?.editorType === "image";
|
|
1252
1253
|
}
|
|
1253
|
-
function
|
|
1254
|
-
let t = v(), n =
|
|
1254
|
+
function b(e) {
|
|
1255
|
+
let t = v(), n = en(e.content), r = tn(e.content, e.attachments);
|
|
1255
1256
|
if (t) {
|
|
1256
1257
|
if (!r) throw Error(l("posts.photoRequired"));
|
|
1257
1258
|
return;
|
|
@@ -1259,11 +1260,11 @@ function zn(e) {
|
|
|
1259
1260
|
if (!n && !r) throw Error(l("posts.bodyRequired"));
|
|
1260
1261
|
if (!e.title?.trim()) throw Error(l("posts.titleRequired"));
|
|
1261
1262
|
}
|
|
1262
|
-
function
|
|
1263
|
-
return
|
|
1263
|
+
function x() {
|
|
1264
|
+
return ye(p()).id;
|
|
1264
1265
|
}
|
|
1265
|
-
function
|
|
1266
|
-
return
|
|
1266
|
+
function S(e) {
|
|
1267
|
+
return zt(t, d(), f(), e, a.value?.uid, c(f()));
|
|
1267
1268
|
}
|
|
1268
1269
|
async function C(e = {}) {
|
|
1269
1270
|
let t = e.pageSize ?? 20, n = [X("boardId", "==", f()), X("isPublished", "==", !0)];
|
|
@@ -1284,7 +1285,7 @@ function zn(e) {
|
|
|
1284
1285
|
}
|
|
1285
1286
|
async function T(e) {
|
|
1286
1287
|
try {
|
|
1287
|
-
let t = await
|
|
1288
|
+
let t = await S(e.trim());
|
|
1288
1289
|
if (!t) return { status: "not-found" };
|
|
1289
1290
|
try {
|
|
1290
1291
|
let e = await w(t.id);
|
|
@@ -1293,41 +1294,41 @@ function zn(e) {
|
|
|
1293
1294
|
post: e
|
|
1294
1295
|
} : { status: "not-found" };
|
|
1295
1296
|
} catch (e) {
|
|
1296
|
-
return
|
|
1297
|
+
return Bn(e) ? { status: "permission-denied" } : {
|
|
1297
1298
|
status: "error",
|
|
1298
1299
|
message: e instanceof Error ? e.message : String(e)
|
|
1299
1300
|
};
|
|
1300
1301
|
}
|
|
1301
1302
|
} catch (e) {
|
|
1302
|
-
return
|
|
1303
|
+
return Bn(e) ? { status: "permission-denied" } : {
|
|
1303
1304
|
status: "error",
|
|
1304
1305
|
message: e instanceof Error ? e.message : String(e)
|
|
1305
1306
|
};
|
|
1306
1307
|
}
|
|
1307
1308
|
}
|
|
1308
|
-
async function
|
|
1309
|
-
let t = e.trim(), n =
|
|
1309
|
+
async function ee(e) {
|
|
1310
|
+
let t = e.trim(), n = zn.get(`${f()}/${t}`);
|
|
1310
1311
|
if (n) return {
|
|
1311
1312
|
status: "ok",
|
|
1312
1313
|
id: n
|
|
1313
1314
|
};
|
|
1314
1315
|
try {
|
|
1315
|
-
let e = await
|
|
1316
|
+
let e = await S(t);
|
|
1316
1317
|
return e ? {
|
|
1317
1318
|
status: "ok",
|
|
1318
1319
|
id: e.id
|
|
1319
|
-
} : { status:
|
|
1320
|
+
} : { status: r(f()) ? "permission-denied" : "not-found" };
|
|
1320
1321
|
} catch (e) {
|
|
1321
|
-
return
|
|
1322
|
+
return Bn(e) ? { status: "permission-denied" } : {
|
|
1322
1323
|
status: "error",
|
|
1323
1324
|
message: e instanceof Error ? e.message : String(e)
|
|
1324
1325
|
};
|
|
1325
1326
|
}
|
|
1326
1327
|
}
|
|
1327
|
-
async function
|
|
1328
|
-
return `${
|
|
1328
|
+
async function te(e) {
|
|
1329
|
+
return `${E(e.trim()) || "post"}-${x()}`;
|
|
1329
1330
|
}
|
|
1330
|
-
async function
|
|
1331
|
+
async function ne(e) {
|
|
1331
1332
|
if (!e.createdAt) return {
|
|
1332
1333
|
previous: null,
|
|
1333
1334
|
next: null
|
|
@@ -1338,7 +1339,7 @@ function zn(e) {
|
|
|
1338
1339
|
X("isPublished", "==", !0),
|
|
1339
1340
|
...e.listed === !1 ? [] : [X("listed", "==", !0)],
|
|
1340
1341
|
W("createdAt", "desc")
|
|
1341
|
-
], [n, r] = await Promise.all([B(G(p(), ...t, J(e.createdAt), H(1))), B(G(p(), ...t,
|
|
1342
|
+
], [n, r] = await Promise.all([B(G(p(), ...t, J(e.createdAt), H(1))), B(G(p(), ...t, xe(e.createdAt), Se(1)))]), i = (e) => {
|
|
1342
1343
|
let t = e.docs[0];
|
|
1343
1344
|
return t ? {
|
|
1344
1345
|
id: t.id,
|
|
@@ -1356,12 +1357,12 @@ function zn(e) {
|
|
|
1356
1357
|
};
|
|
1357
1358
|
}
|
|
1358
1359
|
}
|
|
1359
|
-
async function
|
|
1360
|
-
|
|
1361
|
-
let
|
|
1362
|
-
r ? (i =
|
|
1363
|
-
let o =
|
|
1364
|
-
return c.set(m(
|
|
1360
|
+
async function O(e) {
|
|
1361
|
+
b(e);
|
|
1362
|
+
let n = e.postId || x(), r = v(), i, a;
|
|
1363
|
+
r ? (i = n, a = $t(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await te(a));
|
|
1364
|
+
let o = sn(e.content, e.attachments), s = _(), c = we(t);
|
|
1365
|
+
return c.set(m(n), {
|
|
1365
1366
|
boardId: f(),
|
|
1366
1367
|
slug: i,
|
|
1367
1368
|
title: a,
|
|
@@ -1384,22 +1385,22 @@ function zn(e) {
|
|
|
1384
1385
|
i18n: e.translations.i18n,
|
|
1385
1386
|
i18nSource: e.translations.i18nSource
|
|
1386
1387
|
} : {}
|
|
1387
|
-
}), c.set(h(
|
|
1388
|
+
}), c.set(h(n), {
|
|
1388
1389
|
content: e.content,
|
|
1389
1390
|
...e.translations?.contentI18n ? {
|
|
1390
1391
|
i18n: e.translations.contentI18n,
|
|
1391
1392
|
i18nSource: e.translations.contentI18nSource
|
|
1392
1393
|
} : {}
|
|
1393
|
-
}), await c.commit(),
|
|
1394
|
+
}), await c.commit(), zn.set(`${f()}/${i}`, n), i;
|
|
1394
1395
|
}
|
|
1395
|
-
async function
|
|
1396
|
-
|
|
1397
|
-
let n = v() ?
|
|
1396
|
+
async function re(e, t) {
|
|
1397
|
+
b(t);
|
|
1398
|
+
let n = v() ? $t(t.content) : t.title.trim(), r = sn(t.content, t.attachments), i = _();
|
|
1398
1399
|
await Promise.all([Y(m(e), {
|
|
1399
1400
|
title: n,
|
|
1400
1401
|
...r,
|
|
1401
|
-
previewImage: r.previewImage ? r.previewImage :
|
|
1402
|
-
videoUrl: r.videoUrl ? r.videoUrl :
|
|
1402
|
+
previewImage: r.previewImage ? r.previewImage : ve(),
|
|
1403
|
+
videoUrl: r.videoUrl ? r.videoUrl : ve(),
|
|
1403
1404
|
tags: t.tags ?? [],
|
|
1404
1405
|
listed: i,
|
|
1405
1406
|
attachments: t.attachments ?? [],
|
|
@@ -1414,13 +1415,13 @@ function zn(e) {
|
|
|
1414
1415
|
i18n: t.translations.contentI18n,
|
|
1415
1416
|
i18nSource: t.translations.contentI18nSource
|
|
1416
1417
|
} : {
|
|
1417
|
-
i18n:
|
|
1418
|
-
i18nSource:
|
|
1418
|
+
i18n: ve(),
|
|
1419
|
+
i18nSource: ve()
|
|
1419
1420
|
} : {}
|
|
1420
1421
|
}, { merge: !0 })]);
|
|
1421
1422
|
}
|
|
1422
|
-
async function
|
|
1423
|
-
|
|
1423
|
+
async function ie(e, t) {
|
|
1424
|
+
D(), await Promise.all([Y(m(e), {
|
|
1424
1425
|
i18n: t.i18n,
|
|
1425
1426
|
i18nSource: t.i18nSource,
|
|
1426
1427
|
updatedAt: K()
|
|
@@ -1428,39 +1429,39 @@ function zn(e) {
|
|
|
1428
1429
|
i18n: t.contentI18n,
|
|
1429
1430
|
i18nSource: t.contentI18nSource
|
|
1430
1431
|
} : {
|
|
1431
|
-
i18n:
|
|
1432
|
-
i18nSource:
|
|
1432
|
+
i18n: ve(),
|
|
1433
|
+
i18nSource: ve()
|
|
1433
1434
|
}, { merge: !0 })]);
|
|
1434
1435
|
}
|
|
1435
|
-
async function
|
|
1436
|
+
async function ae(e) {
|
|
1436
1437
|
await Y(m(e), {
|
|
1437
1438
|
isPublished: !0,
|
|
1438
1439
|
publishedAt: K(),
|
|
1439
1440
|
updatedAt: K()
|
|
1440
1441
|
});
|
|
1441
1442
|
}
|
|
1442
|
-
async function
|
|
1443
|
-
await
|
|
1444
|
-
for (let [t, n] of
|
|
1443
|
+
async function oe(e) {
|
|
1444
|
+
await Zt(t, Re(n), d(), e);
|
|
1445
|
+
for (let [t, n] of zn) n === e && zn.delete(t);
|
|
1445
1446
|
}
|
|
1446
1447
|
return {
|
|
1447
|
-
createPostId:
|
|
1448
|
+
createPostId: x,
|
|
1448
1449
|
getPosts: C,
|
|
1449
1450
|
getPost: w,
|
|
1450
1451
|
getPostBySlug: T,
|
|
1451
|
-
resolvePostIdBySlug:
|
|
1452
|
-
resolveUniqueSlug:
|
|
1453
|
-
getAdjacentPosts:
|
|
1454
|
-
createPost:
|
|
1455
|
-
updatePost:
|
|
1456
|
-
savePostTranslations:
|
|
1457
|
-
publishPost:
|
|
1458
|
-
deletePost:
|
|
1452
|
+
resolvePostIdBySlug: ee,
|
|
1453
|
+
resolveUniqueSlug: te,
|
|
1454
|
+
getAdjacentPosts: ne,
|
|
1455
|
+
createPost: O,
|
|
1456
|
+
updatePost: re,
|
|
1457
|
+
savePostTranslations: ie,
|
|
1458
|
+
publishPost: ae,
|
|
1459
|
+
deletePost: oe
|
|
1459
1460
|
};
|
|
1460
1461
|
}
|
|
1461
|
-
var
|
|
1462
|
-
function
|
|
1463
|
-
let n = Z(), { isAdmin: r, user: i, canManageBoard: a } =
|
|
1462
|
+
var Un = 10;
|
|
1463
|
+
function Wn(e, t = {}) {
|
|
1464
|
+
let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Pt(), s = () => j(), c = t.pageSize ?? Un, l = N(() => i.value?.uid ?? ""), u = N(() => Vn(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;
|
|
1464
1465
|
function w(e) {
|
|
1465
1466
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1466
1467
|
}
|
|
@@ -1487,7 +1488,7 @@ function Vn(e, t = {}) {
|
|
|
1487
1488
|
console.error("[memi-board:postList] onSnapshot failed", e), y.value = e.message || String(e), g.value = !1;
|
|
1488
1489
|
});
|
|
1489
1490
|
}
|
|
1490
|
-
function
|
|
1491
|
+
function ee() {
|
|
1491
1492
|
C?.();
|
|
1492
1493
|
let e = l.value;
|
|
1493
1494
|
if (!e) {
|
|
@@ -1503,7 +1504,7 @@ function Vn(e, t = {}) {
|
|
|
1503
1504
|
console.error("[memi-board:postList] own draft onSnapshot failed", e);
|
|
1504
1505
|
});
|
|
1505
1506
|
}
|
|
1506
|
-
async function
|
|
1507
|
+
async function te() {
|
|
1507
1508
|
if (!(v.value || !_.value || g.value)) {
|
|
1508
1509
|
v.value = !0, y.value = "";
|
|
1509
1510
|
try {
|
|
@@ -1525,14 +1526,14 @@ function Vn(e, t = {}) {
|
|
|
1525
1526
|
}
|
|
1526
1527
|
}
|
|
1527
1528
|
}
|
|
1528
|
-
function
|
|
1529
|
-
S?.(), p.value = [], m.value = [], b = null, x = null, _.value = !0, y.value = "", g.value = !0, D(),
|
|
1529
|
+
function ne() {
|
|
1530
|
+
S?.(), p.value = [], m.value = [], b = null, x = null, _.value = !0, y.value = "", g.value = !0, D(), ee();
|
|
1530
1531
|
}
|
|
1531
1532
|
return L([
|
|
1532
1533
|
u,
|
|
1533
1534
|
f,
|
|
1534
1535
|
l
|
|
1535
|
-
],
|
|
1536
|
+
], ne, { immediate: !0 }), P(() => {
|
|
1536
1537
|
S?.(), C?.();
|
|
1537
1538
|
}), {
|
|
1538
1539
|
posts: T,
|
|
@@ -1540,23 +1541,23 @@ function Vn(e, t = {}) {
|
|
|
1540
1541
|
hasMore: _,
|
|
1541
1542
|
loadingMore: v,
|
|
1542
1543
|
loadError: y,
|
|
1543
|
-
loadMore:
|
|
1544
|
+
loadMore: te
|
|
1544
1545
|
};
|
|
1545
1546
|
}
|
|
1546
|
-
function
|
|
1547
|
-
let
|
|
1547
|
+
function Gn(e, t) {
|
|
1548
|
+
let n = Z(), r = () => j(), i = N(() => Vn(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 } = De(o), { data: d, pending: f } = De(c);
|
|
1548
1549
|
return {
|
|
1549
1550
|
post: N(() => {
|
|
1550
|
-
if (!l.value || l.value.boardId !==
|
|
1551
|
+
if (!l.value || l.value.boardId !== i.value) return null;
|
|
1551
1552
|
let e = d.value;
|
|
1552
1553
|
return {
|
|
1553
1554
|
...l.value,
|
|
1554
|
-
id:
|
|
1555
|
+
id: a.value,
|
|
1555
1556
|
i18n: Q(l.value.i18n),
|
|
1556
|
-
i18nSource:
|
|
1557
|
+
i18nSource: rt(l.value.i18nSource),
|
|
1557
1558
|
content: d.value?.content ?? "",
|
|
1558
1559
|
contentI18n: Q(e?.i18n),
|
|
1559
|
-
contentI18nSource:
|
|
1560
|
+
contentI18nSource: it(e?.i18nSource)
|
|
1560
1561
|
};
|
|
1561
1562
|
}),
|
|
1562
1563
|
pending: N(() => u.value || f.value)
|
|
@@ -1564,9 +1565,9 @@ function Hn(e, n) {
|
|
|
1564
1565
|
}
|
|
1565
1566
|
//#endregion
|
|
1566
1567
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
1567
|
-
var
|
|
1568
|
-
function
|
|
1569
|
-
let n = Z(), r = () =>
|
|
1568
|
+
var Kn = 10;
|
|
1569
|
+
function qn(e, t = {}) {
|
|
1570
|
+
let n = Z(), r = () => j(), i = () => o(n, r()), a = t.pageSize ?? Kn, 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;
|
|
1570
1571
|
function _(e) {
|
|
1571
1572
|
return {
|
|
1572
1573
|
id: e.id,
|
|
@@ -1583,13 +1584,13 @@ function Wn(e, t = {}) {
|
|
|
1583
1584
|
});
|
|
1584
1585
|
function b() {
|
|
1585
1586
|
return [
|
|
1586
|
-
X("authorUid", "==",
|
|
1587
|
+
X("authorUid", "==", s.value),
|
|
1587
1588
|
X("isPublished", "==", !0),
|
|
1588
1589
|
X("listed", "==", !0)
|
|
1589
1590
|
];
|
|
1590
1591
|
}
|
|
1591
1592
|
function x() {
|
|
1592
|
-
if (g?.(), !
|
|
1593
|
+
if (g?.(), !s.value) {
|
|
1593
1594
|
u.value = !1;
|
|
1594
1595
|
return;
|
|
1595
1596
|
}
|
|
@@ -1600,7 +1601,7 @@ function Wn(e, t = {}) {
|
|
|
1600
1601
|
});
|
|
1601
1602
|
}
|
|
1602
1603
|
async function S() {
|
|
1603
|
-
if (!f.value && d.value && !u.value &&
|
|
1604
|
+
if (!f.value && d.value && !u.value && s.value) {
|
|
1604
1605
|
f.value = !0, p.value = "";
|
|
1605
1606
|
try {
|
|
1606
1607
|
let e = h ?? m, t = [
|
|
@@ -1621,7 +1622,7 @@ function Wn(e, t = {}) {
|
|
|
1621
1622
|
function C() {
|
|
1622
1623
|
g?.(), c.value = [], l.value = [], m = null, h = null, d.value = !0, p.value = "", u.value = !0, x();
|
|
1623
1624
|
}
|
|
1624
|
-
return L(
|
|
1625
|
+
return L(s, C, { immediate: !0 }), P(() => g?.()), {
|
|
1625
1626
|
posts: y,
|
|
1626
1627
|
postsPending: u,
|
|
1627
1628
|
hasMore: d,
|
|
@@ -1632,9 +1633,9 @@ function Wn(e, t = {}) {
|
|
|
1632
1633
|
}
|
|
1633
1634
|
//#endregion
|
|
1634
1635
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1635
|
-
var
|
|
1636
|
-
function
|
|
1637
|
-
let n = Z(), r = () =>
|
|
1636
|
+
var Jn = 10;
|
|
1637
|
+
function Yn(e, t = {}) {
|
|
1638
|
+
let n = Z(), r = () => j(), i = () => g(n, r()), a = t.pageSize ?? Jn, 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;
|
|
1638
1639
|
function _(e) {
|
|
1639
1640
|
return {
|
|
1640
1641
|
id: e.id,
|
|
@@ -1646,62 +1647,62 @@ function Kn(e, t = {}) {
|
|
|
1646
1647
|
}
|
|
1647
1648
|
let y = N(() => {
|
|
1648
1649
|
let e = /* @__PURE__ */ new Map();
|
|
1649
|
-
for (let t of [...
|
|
1650
|
+
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1650
1651
|
return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
|
|
1651
1652
|
});
|
|
1652
1653
|
function b() {
|
|
1653
|
-
return [X("authorUid", "==",
|
|
1654
|
+
return [X("authorUid", "==", o.value), X("listed", "==", !0)];
|
|
1654
1655
|
}
|
|
1655
1656
|
function x() {
|
|
1656
|
-
if (
|
|
1657
|
-
|
|
1657
|
+
if (h?.(), !o.value) {
|
|
1658
|
+
l.value = !1;
|
|
1658
1659
|
return;
|
|
1659
1660
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1661
|
+
h = U(G(i(), ...b(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
|
|
1662
|
+
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1662
1663
|
}, (e) => {
|
|
1663
|
-
console.error("[memi-board:userComments] onSnapshot failed", e),
|
|
1664
|
+
console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
1664
1665
|
});
|
|
1665
1666
|
}
|
|
1666
1667
|
async function S() {
|
|
1667
|
-
if (!
|
|
1668
|
-
|
|
1668
|
+
if (!d.value && u.value && !l.value && o.value) {
|
|
1669
|
+
d.value = !0, f.value = "";
|
|
1669
1670
|
try {
|
|
1670
|
-
let e =
|
|
1671
|
+
let e = m ?? p, t = [
|
|
1671
1672
|
...b(),
|
|
1672
1673
|
W("createdAt", "desc"),
|
|
1673
1674
|
W(R(), "desc"),
|
|
1674
1675
|
...e ? [J(e)] : [],
|
|
1675
|
-
H(
|
|
1676
|
-
], n = await B(G(i(), ...t)), r = n.docs.slice(0,
|
|
1677
|
-
|
|
1676
|
+
H(a + 1)
|
|
1677
|
+
], n = await B(G(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1678
|
+
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1678
1679
|
} catch (e) {
|
|
1679
|
-
|
|
1680
|
+
f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
1680
1681
|
} finally {
|
|
1681
|
-
|
|
1682
|
+
d.value = !1;
|
|
1682
1683
|
}
|
|
1683
1684
|
}
|
|
1684
1685
|
}
|
|
1685
1686
|
function C() {
|
|
1686
|
-
|
|
1687
|
+
h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, x();
|
|
1687
1688
|
}
|
|
1688
|
-
return L(
|
|
1689
|
+
return L(o, C, { immediate: !0 }), P(() => h?.()), {
|
|
1689
1690
|
comments: y,
|
|
1690
|
-
commentsPending:
|
|
1691
|
-
hasMore:
|
|
1692
|
-
loadingMore:
|
|
1693
|
-
loadError:
|
|
1691
|
+
commentsPending: l,
|
|
1692
|
+
hasMore: u,
|
|
1693
|
+
loadingMore: d,
|
|
1694
|
+
loadError: f,
|
|
1694
1695
|
loadMore: S
|
|
1695
1696
|
};
|
|
1696
1697
|
}
|
|
1697
1698
|
//#endregion
|
|
1698
1699
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1699
|
-
var
|
|
1700
|
-
function
|
|
1701
|
-
let n = Z(), r = () =>
|
|
1700
|
+
var Xn = 10;
|
|
1701
|
+
function Zn(e = "latest", t = {}) {
|
|
1702
|
+
let n = Z(), r = () => j(), i = () => g(n, r()), a = N(() => {
|
|
1702
1703
|
let e = Math.floor(Number(I(t.pageSize)));
|
|
1703
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1704
|
-
}),
|
|
1704
|
+
return Number.isFinite(e) && e > 0 ? e : Xn;
|
|
1705
|
+
}), 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;
|
|
1705
1706
|
function _(e) {
|
|
1706
1707
|
return {
|
|
1707
1708
|
id: e.id,
|
|
@@ -1712,7 +1713,7 @@ function Jn(e = "latest", t = {}) {
|
|
|
1712
1713
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1713
1714
|
}
|
|
1714
1715
|
function y(e, t) {
|
|
1715
|
-
if (
|
|
1716
|
+
if (o.value === "likes") {
|
|
1716
1717
|
let n = (t.likeCount ?? 0) - (e.likeCount ?? 0);
|
|
1717
1718
|
if (n) return n;
|
|
1718
1719
|
}
|
|
@@ -1720,11 +1721,11 @@ function Jn(e = "latest", t = {}) {
|
|
|
1720
1721
|
}
|
|
1721
1722
|
let b = N(() => {
|
|
1722
1723
|
let e = /* @__PURE__ */ new Map();
|
|
1723
|
-
for (let t of [...
|
|
1724
|
+
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1724
1725
|
return [...e.values()].sort(y);
|
|
1725
1726
|
});
|
|
1726
1727
|
function x() {
|
|
1727
|
-
return
|
|
1728
|
+
return o.value === "likes" ? [
|
|
1728
1729
|
X("listed", "==", !0),
|
|
1729
1730
|
W("likeCount", "desc"),
|
|
1730
1731
|
W("createdAt", "desc"),
|
|
@@ -1736,63 +1737,63 @@ function Jn(e = "latest", t = {}) {
|
|
|
1736
1737
|
];
|
|
1737
1738
|
}
|
|
1738
1739
|
function S() {
|
|
1739
|
-
|
|
1740
|
-
|
|
1740
|
+
h?.(), h = U(G(i(), ...x(), H(a.value)), (e) => {
|
|
1741
|
+
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
|
|
1741
1742
|
}, (e) => {
|
|
1742
|
-
console.error("[memi-board:commentFeed] onSnapshot failed", e),
|
|
1743
|
+
console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
1743
1744
|
});
|
|
1744
1745
|
}
|
|
1745
1746
|
async function C() {
|
|
1746
|
-
if (!(
|
|
1747
|
-
|
|
1747
|
+
if (!(d.value || !u.value || l.value)) {
|
|
1748
|
+
d.value = !0, f.value = "";
|
|
1748
1749
|
try {
|
|
1749
|
-
let e =
|
|
1750
|
-
|
|
1750
|
+
let e = m ?? p, t = await B(G(i(), ...x(), ...e ? [J(e)] : [], H(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
|
+
c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
|
|
1751
1752
|
} catch (e) {
|
|
1752
|
-
|
|
1753
|
+
f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
1753
1754
|
} finally {
|
|
1754
|
-
|
|
1755
|
+
d.value = !1;
|
|
1755
1756
|
}
|
|
1756
1757
|
}
|
|
1757
1758
|
}
|
|
1758
1759
|
function w() {
|
|
1759
|
-
|
|
1760
|
+
h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, S();
|
|
1760
1761
|
}
|
|
1761
|
-
return L([
|
|
1762
|
+
return L([o, a], w, { immediate: !0 }), P(() => h?.()), {
|
|
1762
1763
|
comments: b,
|
|
1763
|
-
commentsPending:
|
|
1764
|
-
hasMore:
|
|
1765
|
-
loadingMore:
|
|
1766
|
-
loadError:
|
|
1764
|
+
commentsPending: l,
|
|
1765
|
+
hasMore: u,
|
|
1766
|
+
loadingMore: d,
|
|
1767
|
+
loadError: f,
|
|
1767
1768
|
loadMore: C,
|
|
1768
|
-
sort:
|
|
1769
|
+
sort: o
|
|
1769
1770
|
};
|
|
1770
1771
|
}
|
|
1771
1772
|
//#endregion
|
|
1772
1773
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1773
|
-
var
|
|
1774
|
+
var Qn = 100, $n = /* @__PURE__ */ new Map();
|
|
1774
1775
|
function $(e) {
|
|
1775
|
-
let t =
|
|
1776
|
-
return t || (t =
|
|
1776
|
+
let t = $n.get(e);
|
|
1777
|
+
return t || (t = ge({
|
|
1777
1778
|
text: "",
|
|
1778
1779
|
sentiment: null,
|
|
1779
1780
|
loaded: !1,
|
|
1780
1781
|
pending: !1,
|
|
1781
1782
|
saving: !1,
|
|
1782
1783
|
error: ""
|
|
1783
|
-
}),
|
|
1784
|
+
}), $n.set(e, t)), t;
|
|
1784
1785
|
}
|
|
1785
|
-
function
|
|
1786
|
-
let t = Z(), n = () =>
|
|
1786
|
+
function er(e) {
|
|
1787
|
+
let t = Z(), n = () => j(), { user: r } = Pt(), { t: i, formatNumber: a } = A(), o = N(() => I(e)), s = N(() => `${r.value?.uid ?? ""}:${o.value}`), c = N(() => {
|
|
1787
1788
|
let e = r.value?.uid, i = o.value;
|
|
1788
|
-
return !e || !i ? null :
|
|
1789
|
+
return !e || !i ? null : f(t, n(), e, i);
|
|
1789
1790
|
}), l = N({
|
|
1790
1791
|
get: () => $(s.value).text,
|
|
1791
1792
|
set: (e) => {
|
|
1792
1793
|
$(s.value).text = e;
|
|
1793
1794
|
}
|
|
1794
|
-
}), u = N(() => $(s.value).sentiment), d = N(() => $(s.value).loaded),
|
|
1795
|
-
async function
|
|
1795
|
+
}), u = N(() => $(s.value).sentiment), d = N(() => $(s.value).loaded), p = N(() => $(s.value).pending), m = N(() => $(s.value).saving), h = N(() => $(s.value).error);
|
|
1796
|
+
async function g() {
|
|
1796
1797
|
let e = c.value;
|
|
1797
1798
|
if (!e) return;
|
|
1798
1799
|
let t = $(s.value);
|
|
@@ -1806,7 +1807,7 @@ function Zn(e) {
|
|
|
1806
1807
|
t.pending = !1;
|
|
1807
1808
|
}
|
|
1808
1809
|
}
|
|
1809
|
-
async function
|
|
1810
|
+
async function _(e, t) {
|
|
1810
1811
|
let n = c.value;
|
|
1811
1812
|
if (!n) return;
|
|
1812
1813
|
let r = e.trim();
|
|
@@ -1825,13 +1826,13 @@ function Zn(e) {
|
|
|
1825
1826
|
o.saving = !1;
|
|
1826
1827
|
}
|
|
1827
1828
|
}
|
|
1828
|
-
async function
|
|
1829
|
+
async function v() {
|
|
1829
1830
|
let e = c.value;
|
|
1830
1831
|
if (!e) return;
|
|
1831
1832
|
let t = $(s.value);
|
|
1832
1833
|
t.saving = !0, t.error = "";
|
|
1833
1834
|
try {
|
|
1834
|
-
await
|
|
1835
|
+
await _e(e), t.text = "", t.sentiment = null, t.loaded = !0;
|
|
1835
1836
|
} catch (e) {
|
|
1836
1837
|
throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
|
|
1837
1838
|
} finally {
|
|
@@ -1842,75 +1843,75 @@ function Zn(e) {
|
|
|
1842
1843
|
text: l,
|
|
1843
1844
|
sentiment: u,
|
|
1844
1845
|
loaded: d,
|
|
1845
|
-
pending:
|
|
1846
|
-
saving:
|
|
1847
|
-
error:
|
|
1848
|
-
load:
|
|
1849
|
-
save:
|
|
1850
|
-
remove:
|
|
1846
|
+
pending: p,
|
|
1847
|
+
saving: m,
|
|
1848
|
+
error: h,
|
|
1849
|
+
load: g,
|
|
1850
|
+
save: _,
|
|
1851
|
+
remove: v
|
|
1851
1852
|
};
|
|
1852
1853
|
}
|
|
1853
1854
|
//#endregion
|
|
1854
1855
|
//#region src/utils/subscribeCommentRange.ts
|
|
1855
|
-
function
|
|
1856
|
-
return U(G(
|
|
1856
|
+
function tr(e, t, n, r, i, a) {
|
|
1857
|
+
return U(G(g(e, t), X("postId", "==", n), ...r.rootId ? [X("rootId", "==", r.rootId), X("isReply", "==", !0)] : [X("parentId", "==", null)], W("createdAt", "asc"), W(R(), "asc"), ...r.hasMore && r.cursor ? [be(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
|
|
1857
1858
|
...e.data(),
|
|
1858
1859
|
id: e.id
|
|
1859
|
-
}))),
|
|
1860
|
+
}))), a);
|
|
1860
1861
|
}
|
|
1861
1862
|
//#endregion
|
|
1862
1863
|
//#region src/composables/useMemiBoardComments.ts
|
|
1863
|
-
var
|
|
1864
|
-
function
|
|
1864
|
+
var nr = 10, rr = 5, ir = 1e3;
|
|
1865
|
+
function ar(e, { t, formatNumber: n }) {
|
|
1865
1866
|
let r = e.trim();
|
|
1866
1867
|
if (!r) throw Error(t("comments.bodyRequired"));
|
|
1867
|
-
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(
|
|
1868
|
+
if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(ir) }));
|
|
1868
1869
|
return r;
|
|
1869
1870
|
}
|
|
1870
|
-
function
|
|
1871
|
-
let
|
|
1872
|
-
function
|
|
1871
|
+
function or(e, t, n = {}) {
|
|
1872
|
+
let r = Z(), i = Oe(), a = () => j(), { isBoardHidden: o } = Rn(), 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
|
+
function D(e) {
|
|
1873
1874
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1874
1875
|
}
|
|
1875
|
-
let
|
|
1876
|
+
let ee = N(() => {
|
|
1876
1877
|
let e = /* @__PURE__ */ new Map();
|
|
1877
1878
|
for (let t of [...m.value, ..._.value]) t.id && e.set(t.id, t);
|
|
1878
|
-
return [...e.values()].sort((e, t) =>
|
|
1879
|
+
return [...e.values()].sort((e, t) => D(e) - D(t) || String(e.id).localeCompare(String(t.id)));
|
|
1879
1880
|
});
|
|
1880
|
-
L([f,
|
|
1881
|
-
let t = ++
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1881
|
+
L([f, u], async ([e]) => {
|
|
1882
|
+
let t = ++w;
|
|
1883
|
+
if (T++, v.value = !1, _.value = [], m.value = [], C = null, E?.(), E = void 0, b.value = n.subscribe !== !1, n.subscribe !== !1) {
|
|
1884
|
+
h.value = !0, S.value = !0;
|
|
1884
1885
|
try {
|
|
1885
|
-
let n = B(G(
|
|
1886
|
+
let n = B(G(l(), X("postId", "==", e), W("createdAt", "desc"), W(R(), "desc")));
|
|
1886
1887
|
if (await Promise.all([n.then((e) => {
|
|
1887
|
-
if (t !==
|
|
1888
|
+
if (t !== w) return;
|
|
1888
1889
|
let n = e.docs.map((e) => ({
|
|
1889
1890
|
id: e.id,
|
|
1890
1891
|
...e.data()
|
|
1891
1892
|
}));
|
|
1892
1893
|
m.value = n.filter((e) => e.parentId === void 0);
|
|
1893
|
-
}),
|
|
1894
|
+
}), ne()]), t !== w) return;
|
|
1894
1895
|
} finally {
|
|
1895
|
-
t ===
|
|
1896
|
+
t === w && (h.value = !1, S.value = !1);
|
|
1896
1897
|
}
|
|
1897
1898
|
}
|
|
1898
1899
|
}, { immediate: !0 });
|
|
1899
|
-
function
|
|
1900
|
-
|
|
1901
|
-
let e = ++
|
|
1902
|
-
|
|
1903
|
-
cursor:
|
|
1904
|
-
hasMore:
|
|
1900
|
+
function te() {
|
|
1901
|
+
E?.();
|
|
1902
|
+
let e = ++T;
|
|
1903
|
+
E = tr(r, a(), f.value, {
|
|
1904
|
+
cursor: C,
|
|
1905
|
+
hasMore: b.value
|
|
1905
1906
|
}, (t) => {
|
|
1906
|
-
e ===
|
|
1907
|
+
e === T && (_.value = t);
|
|
1907
1908
|
}, (e) => {
|
|
1908
1909
|
console.error("[memi-board:comments] onSnapshot failed", e);
|
|
1909
1910
|
});
|
|
1910
1911
|
}
|
|
1911
|
-
async function
|
|
1912
|
-
if (v.value || !
|
|
1913
|
-
let e =
|
|
1912
|
+
async function ne() {
|
|
1913
|
+
if (v.value || !b.value) return;
|
|
1914
|
+
let e = w;
|
|
1914
1915
|
v.value = !0;
|
|
1915
1916
|
try {
|
|
1916
1917
|
let t = [
|
|
@@ -1918,250 +1919,268 @@ function rr(e, n, r = {}) {
|
|
|
1918
1919
|
X("parentId", "==", null),
|
|
1919
1920
|
W("createdAt", "asc"),
|
|
1920
1921
|
W(R(), "asc"),
|
|
1921
|
-
...
|
|
1922
|
+
...C ? [J(C)] : [],
|
|
1922
1923
|
H(11)
|
|
1923
|
-
], n = await B(G(
|
|
1924
|
-
if (e !==
|
|
1925
|
-
let r = n.docs.slice(0,
|
|
1924
|
+
], n = await B(G(l(), ...t));
|
|
1925
|
+
if (e !== w) return;
|
|
1926
|
+
let r = n.docs.slice(0, nr), i = r.map((e) => ({
|
|
1926
1927
|
id: e.id,
|
|
1927
1928
|
...e.data()
|
|
1928
1929
|
})), a = new Set(_.value.map((e) => e.id));
|
|
1929
|
-
_.value.push(...i.filter((e) => !a.has(e.id))),
|
|
1930
|
+
_.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size > nr, te();
|
|
1930
1931
|
} finally {
|
|
1931
|
-
e ===
|
|
1932
|
+
e === w && (v.value = !1);
|
|
1932
1933
|
}
|
|
1933
1934
|
}
|
|
1934
1935
|
P(() => {
|
|
1935
|
-
|
|
1936
|
+
w++, T++, E?.();
|
|
1936
1937
|
});
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1938
|
+
function O() {
|
|
1939
|
+
return ye(l()).id;
|
|
1940
|
+
}
|
|
1941
|
+
function re(e) {
|
|
1942
|
+
if (!e) return ye(l());
|
|
1943
|
+
if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(c("commentForm.uploadFailed"));
|
|
1944
|
+
return d(r, a(), e);
|
|
1945
|
+
}
|
|
1946
|
+
function ie(e, t) {
|
|
1947
|
+
let n = `${x(a(), f.value)}/comment-images/${e}/`;
|
|
1948
|
+
return ue(t).filter((e) => e.path.startsWith(n));
|
|
1949
|
+
}
|
|
1950
|
+
function ae(e, t) {
|
|
1951
|
+
let n = ie(t, e.images);
|
|
1952
|
+
return {
|
|
1940
1953
|
postId: f.value,
|
|
1941
|
-
boardId:
|
|
1954
|
+
boardId: u.value,
|
|
1942
1955
|
listed: p(),
|
|
1943
|
-
body:
|
|
1956
|
+
body: ar(e.body, s),
|
|
1944
1957
|
authorUid: e.authorUid,
|
|
1945
1958
|
authorName: e.authorName,
|
|
1946
1959
|
authorPhoto: e.authorPhoto,
|
|
1947
1960
|
moderationStatus: "approved",
|
|
1961
|
+
replyCount: 0,
|
|
1962
|
+
likeCount: 0,
|
|
1963
|
+
createdAt: K(),
|
|
1964
|
+
updatedAt: K(),
|
|
1965
|
+
...n.length ? { images: n } : {}
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
async function oe(e) {
|
|
1969
|
+
if (!e.length) return;
|
|
1970
|
+
let t = Re(i);
|
|
1971
|
+
await Promise.all(e.map((e) => Le(Be(t, e)).catch(() => {})));
|
|
1972
|
+
}
|
|
1973
|
+
async function k(e) {
|
|
1974
|
+
let t = we(r), n = re(e.id);
|
|
1975
|
+
t.set(n, {
|
|
1976
|
+
...ae(e, n.id),
|
|
1948
1977
|
parentId: null,
|
|
1949
|
-
rootId:
|
|
1978
|
+
rootId: n.id,
|
|
1950
1979
|
depth: 0,
|
|
1951
1980
|
replyToUid: null,
|
|
1952
1981
|
replyToName: null,
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
let r = Se(i), a = _e(u()), s = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1964
|
-
if (!s) throw Error(l("comments.threadNotFound"));
|
|
1965
|
-
let m = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1966
|
-
r.set(a, {
|
|
1967
|
-
postId: f.value,
|
|
1968
|
-
boardId: d.value,
|
|
1969
|
-
listed: p(),
|
|
1970
|
-
body: n,
|
|
1971
|
-
authorUid: e.authorUid,
|
|
1972
|
-
authorName: e.authorName,
|
|
1973
|
-
authorPhoto: e.authorPhoto,
|
|
1974
|
-
moderationStatus: "approved",
|
|
1982
|
+
isReply: !1
|
|
1983
|
+
}), t.update(y(r, a(), f.value), { commentCount: V(1) }), await t.commit();
|
|
1984
|
+
}
|
|
1985
|
+
async function se(e) {
|
|
1986
|
+
if (!e.parent.id) throw Error(c("comments.replyTargetNotFound"));
|
|
1987
|
+
let t = we(r), n = re(e.id), i = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1988
|
+
if (!i) throw Error(c("comments.threadNotFound"));
|
|
1989
|
+
let o = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1990
|
+
t.set(n, {
|
|
1991
|
+
...ae(e, n.id),
|
|
1975
1992
|
parentId: e.parent.id,
|
|
1976
|
-
rootId:
|
|
1977
|
-
depth:
|
|
1993
|
+
rootId: i,
|
|
1994
|
+
depth: o,
|
|
1978
1995
|
replyToUid: e.parent.authorUid,
|
|
1979
1996
|
replyToName: e.parent.authorName,
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
isReply: !0,
|
|
1983
|
-
createdAt: K(),
|
|
1984
|
-
updatedAt: K()
|
|
1985
|
-
}), r.update(h(i, o(), s), { replyCount: V(1) }), r.update(t(i, o(), f.value), { commentCount: V(1) }), await r.commit();
|
|
1997
|
+
isReply: !0
|
|
1998
|
+
}), t.update(d(r, a(), i), { replyCount: V(1) }), t.update(y(r, a(), f.value), { commentCount: V(1) }), await t.commit();
|
|
1986
1999
|
}
|
|
1987
|
-
async function
|
|
2000
|
+
async function ce(e) {
|
|
1988
2001
|
if (!e.id) return;
|
|
1989
|
-
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
body:
|
|
2002
|
+
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(c("comments.hasRepliesCannotDelete"));
|
|
2003
|
+
let t = de(e.images, `${x(a(), f.value)}/comment-images/${e.id}/`);
|
|
2004
|
+
await Xt(r, a(), e.id);
|
|
2005
|
+
let n = we(r);
|
|
2006
|
+
n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount: V(-1) }), n.update(y(r, a(), f.value), { commentCount: V(-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
|
+
}
|
|
2008
|
+
async function le(e, t, n) {
|
|
2009
|
+
let i = {
|
|
2010
|
+
body: ar(t, s),
|
|
1998
2011
|
updatedAt: K()
|
|
1999
|
-
}
|
|
2012
|
+
};
|
|
2013
|
+
if (n !== void 0) {
|
|
2014
|
+
let t = ie(e, n);
|
|
2015
|
+
i.images = t.length ? t : ve();
|
|
2016
|
+
}
|
|
2017
|
+
await Y(d(r, a(), e), i);
|
|
2000
2018
|
}
|
|
2001
|
-
async function
|
|
2002
|
-
await Y(
|
|
2019
|
+
async function fe(e, t, n) {
|
|
2020
|
+
await Y(d(r, a(), e), {
|
|
2003
2021
|
isBlinded: t,
|
|
2004
2022
|
moderatedAt: K(),
|
|
2005
2023
|
moderatedBy: n
|
|
2006
2024
|
});
|
|
2007
2025
|
}
|
|
2008
2026
|
return {
|
|
2009
|
-
comments:
|
|
2010
|
-
commentsPending: N(() =>
|
|
2011
|
-
hasMore:
|
|
2027
|
+
comments: ee,
|
|
2028
|
+
commentsPending: N(() => S.value || h.value),
|
|
2029
|
+
hasMore: b,
|
|
2012
2030
|
loadingMore: v,
|
|
2013
|
-
loadMore:
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2031
|
+
loadMore: ne,
|
|
2032
|
+
newCommentId: O,
|
|
2033
|
+
addComment: k,
|
|
2034
|
+
addReply: se,
|
|
2035
|
+
updateComment: le,
|
|
2036
|
+
setCommentBlinded: fe,
|
|
2037
|
+
deleteComment: ce
|
|
2019
2038
|
};
|
|
2020
2039
|
}
|
|
2021
|
-
function
|
|
2022
|
-
let r = Z(), i = () =>
|
|
2023
|
-
function
|
|
2040
|
+
function sr(e, t, n) {
|
|
2041
|
+
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
|
+
function h(e) {
|
|
2024
2043
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
2025
2044
|
}
|
|
2026
2045
|
function _() {
|
|
2027
|
-
|
|
2028
|
-
let e = ++
|
|
2029
|
-
|
|
2046
|
+
f?.();
|
|
2047
|
+
let e = ++m;
|
|
2048
|
+
f = tr(r, i(), o, {
|
|
2030
2049
|
rootId: n,
|
|
2031
|
-
cursor:
|
|
2032
|
-
hasMore:
|
|
2050
|
+
cursor: d,
|
|
2051
|
+
hasMore: u.value
|
|
2033
2052
|
}, (t) => {
|
|
2034
|
-
e ===
|
|
2053
|
+
e === m && (s.value = t);
|
|
2035
2054
|
}, (e) => {
|
|
2036
2055
|
console.error("[memi-board:replies] onSnapshot failed", e);
|
|
2037
2056
|
});
|
|
2038
2057
|
}
|
|
2039
2058
|
async function v() {
|
|
2040
|
-
if (
|
|
2041
|
-
let e =
|
|
2042
|
-
|
|
2059
|
+
if (c.value || !u.value) return;
|
|
2060
|
+
let e = p;
|
|
2061
|
+
c.value = !0;
|
|
2043
2062
|
try {
|
|
2044
2063
|
let t = [
|
|
2045
|
-
X("postId", "==",
|
|
2064
|
+
X("postId", "==", o),
|
|
2046
2065
|
X("rootId", "==", n),
|
|
2047
2066
|
X("isReply", "==", !0),
|
|
2048
2067
|
W("createdAt", "asc"),
|
|
2049
2068
|
W(R(), "asc"),
|
|
2050
|
-
...
|
|
2069
|
+
...d ? [J(d)] : [],
|
|
2051
2070
|
H(6)
|
|
2052
|
-
], r = await B(G(
|
|
2053
|
-
if (e !==
|
|
2054
|
-
let i = new Set(
|
|
2071
|
+
], r = await B(G(a(), ...t));
|
|
2072
|
+
if (e !== p) return;
|
|
2073
|
+
let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, rr), f = c.map((e) => ({
|
|
2055
2074
|
id: e.id,
|
|
2056
2075
|
...e.data()
|
|
2057
2076
|
})).filter((e) => !i.has(e.id));
|
|
2058
|
-
|
|
2077
|
+
s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > rr, _();
|
|
2059
2078
|
} finally {
|
|
2060
|
-
e ===
|
|
2079
|
+
e === p && (c.value = !1);
|
|
2061
2080
|
}
|
|
2062
2081
|
}
|
|
2063
2082
|
function y() {
|
|
2064
|
-
|
|
2083
|
+
p++, m++, c.value = !1, f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
|
|
2065
2084
|
}
|
|
2066
2085
|
async function b() {
|
|
2067
2086
|
y(), await v();
|
|
2068
2087
|
}
|
|
2069
2088
|
return P(() => {
|
|
2070
|
-
|
|
2089
|
+
p++, m++, f?.();
|
|
2071
2090
|
}), {
|
|
2072
|
-
replies:
|
|
2073
|
-
loading:
|
|
2074
|
-
loaded:
|
|
2075
|
-
hasMore:
|
|
2091
|
+
replies: s,
|
|
2092
|
+
loading: c,
|
|
2093
|
+
loaded: l,
|
|
2094
|
+
hasMore: u,
|
|
2076
2095
|
loadMore: v,
|
|
2077
2096
|
refresh: b
|
|
2078
2097
|
};
|
|
2079
2098
|
}
|
|
2080
2099
|
//#endregion
|
|
2081
2100
|
//#region src/composables/useMemiBoardLikes.ts
|
|
2082
|
-
function
|
|
2083
|
-
let
|
|
2084
|
-
function
|
|
2085
|
-
return
|
|
2101
|
+
function cr(e, t) {
|
|
2102
|
+
let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = F(!1), s = F(!1), c;
|
|
2103
|
+
function l(e) {
|
|
2104
|
+
return he(r, n(), t, e);
|
|
2086
2105
|
}
|
|
2087
|
-
L(
|
|
2088
|
-
if (
|
|
2089
|
-
|
|
2106
|
+
L(i, (e) => {
|
|
2107
|
+
if (c?.(), c = void 0, !e) {
|
|
2108
|
+
o.value = !1;
|
|
2090
2109
|
return;
|
|
2091
2110
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2111
|
+
c = U(l(e.uid), (e) => {
|
|
2112
|
+
o.value = e.exists();
|
|
2094
2113
|
}, (e) => {
|
|
2095
2114
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
2096
2115
|
});
|
|
2097
|
-
}, { immediate: !0 }), P(() =>
|
|
2098
|
-
async function
|
|
2099
|
-
let c =
|
|
2100
|
-
if (!c) throw Error(
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2116
|
+
}, { immediate: !0 }), P(() => c?.());
|
|
2117
|
+
async function u() {
|
|
2118
|
+
let c = i.value?.uid;
|
|
2119
|
+
if (!c) throw Error(a("likes.signInRequired"));
|
|
2120
|
+
if (s.value) return o.value;
|
|
2121
|
+
s.value = !0;
|
|
2103
2122
|
try {
|
|
2104
|
-
let
|
|
2105
|
-
return await
|
|
2123
|
+
let i = l(c), a = y(r, n(), t);
|
|
2124
|
+
return await Ce(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: V(-1) }), !1) : (n.set(i, {
|
|
2106
2125
|
uid: c,
|
|
2107
|
-
postId:
|
|
2126
|
+
postId: t,
|
|
2108
2127
|
boardId: e,
|
|
2109
2128
|
createdAt: K()
|
|
2110
|
-
}),
|
|
2129
|
+
}), n.update(a, { likeCount: V(1) }), !0));
|
|
2111
2130
|
} finally {
|
|
2112
|
-
|
|
2131
|
+
s.value = !1;
|
|
2113
2132
|
}
|
|
2114
2133
|
}
|
|
2115
2134
|
return {
|
|
2116
|
-
isLiked:
|
|
2117
|
-
likePending:
|
|
2118
|
-
toggleLike:
|
|
2135
|
+
isLiked: o,
|
|
2136
|
+
likePending: s,
|
|
2137
|
+
toggleLike: u
|
|
2119
2138
|
};
|
|
2120
2139
|
}
|
|
2121
|
-
var
|
|
2122
|
-
function
|
|
2123
|
-
let n = () =>
|
|
2140
|
+
var lr = Symbol.for("memi-board:comment-likes");
|
|
2141
|
+
function ur(e, t) {
|
|
2142
|
+
let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = N(() => I(e)), s = N(() => I(t)), c = F(/* @__PURE__ */ new Set()), l = F(null), u;
|
|
2124
2143
|
L([i, s], ([e, t]) => {
|
|
2125
|
-
|
|
2144
|
+
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = U(G(b(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
|
|
2126
2145
|
let t = /* @__PURE__ */ new Set();
|
|
2127
2146
|
for (let n of e.docs) {
|
|
2128
2147
|
let e = n.data().commentId;
|
|
2129
2148
|
typeof e == "string" && e && t.add(e);
|
|
2130
2149
|
}
|
|
2131
|
-
|
|
2150
|
+
c.value = t;
|
|
2132
2151
|
}, (e) => {
|
|
2133
2152
|
console.error("[memi-board:comment-likes] onSnapshot failed", e);
|
|
2134
2153
|
}));
|
|
2135
|
-
}, { immediate: !0 }), P(() =>
|
|
2154
|
+
}, { immediate: !0 }), P(() => u?.());
|
|
2136
2155
|
function f(e) {
|
|
2137
|
-
return !!e &&
|
|
2156
|
+
return !!e && c.value.has(e);
|
|
2138
2157
|
}
|
|
2139
2158
|
function p(e) {
|
|
2140
|
-
return !!e &&
|
|
2159
|
+
return !!e && l.value === e;
|
|
2141
2160
|
}
|
|
2142
2161
|
async function m(e) {
|
|
2143
2162
|
let t = i.value?.uid;
|
|
2144
2163
|
if (!t) throw Error(a("likes.signInRequired"));
|
|
2145
2164
|
if (!e) return !1;
|
|
2146
|
-
if (
|
|
2147
|
-
|
|
2165
|
+
if (l.value) return f(e);
|
|
2166
|
+
l.value = e;
|
|
2148
2167
|
try {
|
|
2149
|
-
let i =
|
|
2150
|
-
let r = await n.get(i), c = await n.get(
|
|
2168
|
+
let i = he(r, n(), e, t), l = d(r, n(), e), u = await Ce(r, async (n) => {
|
|
2169
|
+
let r = await n.get(i), c = await n.get(l);
|
|
2151
2170
|
if (!c.exists()) throw Error(a("likes.commentNotFound"));
|
|
2152
|
-
let
|
|
2153
|
-
return r.exists() ? (n.delete(i), n.update(
|
|
2171
|
+
let u = Number(c.data()?.likeCount ?? 0);
|
|
2172
|
+
return r.exists() ? (n.delete(i), n.update(l, { likeCount: Math.max(0, u - 1) }), !1) : (n.set(i, {
|
|
2154
2173
|
uid: t,
|
|
2155
2174
|
postId: s.value,
|
|
2156
2175
|
boardId: o.value,
|
|
2157
2176
|
commentId: e,
|
|
2158
2177
|
target: "comment",
|
|
2159
2178
|
createdAt: K()
|
|
2160
|
-
}), n.update(
|
|
2161
|
-
}), f = new Set(
|
|
2162
|
-
return
|
|
2179
|
+
}), n.update(l, { likeCount: u + 1 }), !0);
|
|
2180
|
+
}), f = new Set(c.value);
|
|
2181
|
+
return u ? f.add(e) : f.delete(e), c.value = f, u;
|
|
2163
2182
|
} finally {
|
|
2164
|
-
|
|
2183
|
+
l.value = null;
|
|
2165
2184
|
}
|
|
2166
2185
|
}
|
|
2167
2186
|
return {
|
|
@@ -2172,7 +2191,7 @@ function sr(e, t) {
|
|
|
2172
2191
|
}
|
|
2173
2192
|
//#endregion
|
|
2174
2193
|
//#region src/composables/useMemiBoardReports.ts
|
|
2175
|
-
var
|
|
2194
|
+
var dr = 200, fr = [
|
|
2176
2195
|
{
|
|
2177
2196
|
value: "spam",
|
|
2178
2197
|
label: "스팸·광고"
|
|
@@ -2194,17 +2213,17 @@ var cr = 200, lr = [
|
|
|
2194
2213
|
label: "기타"
|
|
2195
2214
|
}
|
|
2196
2215
|
];
|
|
2197
|
-
function
|
|
2198
|
-
return
|
|
2216
|
+
function pr(e, t) {
|
|
2217
|
+
return fr.some((t) => t.value === e) ? k(t, `reports.reason.${e}`) : e || k(t, "reports.fallbackLabel");
|
|
2199
2218
|
}
|
|
2200
|
-
function
|
|
2201
|
-
let n = () =>
|
|
2219
|
+
function mr(e, t) {
|
|
2220
|
+
let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = F(!1), s = F(!1), c;
|
|
2202
2221
|
L(i, (e) => {
|
|
2203
2222
|
if (c?.(), c = void 0, !e) {
|
|
2204
2223
|
o.value = !1;
|
|
2205
2224
|
return;
|
|
2206
2225
|
}
|
|
2207
|
-
c = U(
|
|
2226
|
+
c = U(u(r, n(), t, e.uid), (e) => {
|
|
2208
2227
|
o.value = e.exists();
|
|
2209
2228
|
}, (e) => {
|
|
2210
2229
|
console.error("[memi-board:reports] onSnapshot failed", e);
|
|
@@ -2215,19 +2234,19 @@ function dr(e, t) {
|
|
|
2215
2234
|
if (!l) throw Error(a("reports.signInRequired"));
|
|
2216
2235
|
if (s.value) return;
|
|
2217
2236
|
if (c.authorUid && c.authorUid === l) throw Error(a("reports.cannotReportOwn"));
|
|
2218
|
-
let
|
|
2219
|
-
if (!
|
|
2220
|
-
let
|
|
2221
|
-
if (
|
|
2237
|
+
let d = fr.some((e) => e.value === c.reason) ? c.reason : null;
|
|
2238
|
+
if (!d) throw Error(a("reports.reasonRequired"));
|
|
2239
|
+
let f = (c.detail || "").trim();
|
|
2240
|
+
if (f.length > 200) throw Error(a("reports.detailTooLong", { max: 200 }));
|
|
2222
2241
|
s.value = !0;
|
|
2223
2242
|
try {
|
|
2224
|
-
await q(
|
|
2243
|
+
await q(u(r, n(), t, l), {
|
|
2225
2244
|
uid: l,
|
|
2226
2245
|
target: "post",
|
|
2227
2246
|
postId: t,
|
|
2228
2247
|
boardId: e,
|
|
2229
|
-
reason:
|
|
2230
|
-
detail:
|
|
2248
|
+
reason: d,
|
|
2249
|
+
detail: f,
|
|
2231
2250
|
status: "open",
|
|
2232
2251
|
postTitle: (c.postTitle || "").trim().slice(0, 200),
|
|
2233
2252
|
authorUid: c.authorUid || "",
|
|
@@ -2244,116 +2263,116 @@ function dr(e, t) {
|
|
|
2244
2263
|
submitReport: l
|
|
2245
2264
|
};
|
|
2246
2265
|
}
|
|
2247
|
-
var
|
|
2248
|
-
function
|
|
2249
|
-
let t = () =>
|
|
2250
|
-
function
|
|
2266
|
+
var hr = 20;
|
|
2267
|
+
function gr(e = {}) {
|
|
2268
|
+
let t = () => j(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Pt(), o = e.pageSize ?? hr, s = N(() => I(e.boardId)?.trim() || ""), c = F([]), d = F(!0), f = F(!1), p = F(!1), m = F(""), h = null, g;
|
|
2269
|
+
function _(e) {
|
|
2251
2270
|
return {
|
|
2252
2271
|
id: e.id,
|
|
2253
2272
|
...e.data()
|
|
2254
2273
|
};
|
|
2255
2274
|
}
|
|
2256
|
-
function
|
|
2257
|
-
if (
|
|
2258
|
-
|
|
2275
|
+
function v() {
|
|
2276
|
+
if (g?.(), !i.value || !r.value && !s.value) {
|
|
2277
|
+
c.value = [], d.value = !1;
|
|
2259
2278
|
return;
|
|
2260
2279
|
}
|
|
2261
|
-
|
|
2262
|
-
let e =
|
|
2263
|
-
|
|
2264
|
-
|
|
2280
|
+
d.value = !0;
|
|
2281
|
+
let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")];
|
|
2282
|
+
g = U(G(l(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), H(o)), (e) => {
|
|
2283
|
+
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;
|
|
2265
2284
|
}, (e) => {
|
|
2266
|
-
console.error("[memi-board:report-queue] onSnapshot failed", e),
|
|
2285
|
+
console.error("[memi-board:report-queue] onSnapshot failed", e), m.value = e.message || String(e), d.value = !1;
|
|
2267
2286
|
});
|
|
2268
2287
|
}
|
|
2269
|
-
L([i,
|
|
2270
|
-
async function
|
|
2271
|
-
if (!
|
|
2272
|
-
|
|
2288
|
+
L([i, s], v, { immediate: !0 }), P(() => g?.());
|
|
2289
|
+
async function y() {
|
|
2290
|
+
if (!f.value && p.value && h && i.value) {
|
|
2291
|
+
f.value = !0, m.value = "";
|
|
2273
2292
|
try {
|
|
2274
|
-
let e =
|
|
2275
|
-
|
|
2293
|
+
let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await B(G(l(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), J(h), H(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
|
|
2294
|
+
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;
|
|
2276
2295
|
} catch (e) {
|
|
2277
|
-
|
|
2296
|
+
m.value = e.message || String(e);
|
|
2278
2297
|
} finally {
|
|
2279
|
-
|
|
2298
|
+
f.value = !1;
|
|
2280
2299
|
}
|
|
2281
2300
|
}
|
|
2282
2301
|
}
|
|
2283
2302
|
async function b(e, r) {
|
|
2284
|
-
e.id && a(e.boardId) && await Y(
|
|
2303
|
+
e.id && a(e.boardId) && await Y(u(n, t(), e.postId, e.uid), {
|
|
2285
2304
|
status: r,
|
|
2286
2305
|
reviewedAt: K(),
|
|
2287
|
-
reviewedBy:
|
|
2306
|
+
reviewedBy: Ee().value?.uid || ""
|
|
2288
2307
|
});
|
|
2289
2308
|
}
|
|
2290
2309
|
return {
|
|
2291
|
-
reports:
|
|
2292
|
-
pending:
|
|
2293
|
-
loadingMore:
|
|
2294
|
-
hasMore:
|
|
2295
|
-
loadError:
|
|
2296
|
-
loadMore:
|
|
2310
|
+
reports: c,
|
|
2311
|
+
pending: d,
|
|
2312
|
+
loadingMore: f,
|
|
2313
|
+
hasMore: p,
|
|
2314
|
+
loadError: m,
|
|
2315
|
+
loadMore: y,
|
|
2297
2316
|
dismissReport: (e) => b(e, "dismissed"),
|
|
2298
2317
|
actionReport: (e) => b(e, "actioned")
|
|
2299
2318
|
};
|
|
2300
2319
|
}
|
|
2301
2320
|
//#endregion
|
|
2302
2321
|
//#region src/composables/useMemiBoardViews.ts
|
|
2303
|
-
function
|
|
2304
|
-
let e = () =>
|
|
2305
|
-
function
|
|
2322
|
+
function _r() {
|
|
2323
|
+
let e = () => j(), t = Z();
|
|
2324
|
+
function n(t, n) {
|
|
2306
2325
|
let { postsCollection: r } = e();
|
|
2307
2326
|
return `memi-board:viewed:${r}:${t}:${n}`;
|
|
2308
2327
|
}
|
|
2309
|
-
function
|
|
2328
|
+
function r(e, t) {
|
|
2310
2329
|
if (typeof sessionStorage > "u") return !1;
|
|
2311
2330
|
try {
|
|
2312
|
-
return sessionStorage.getItem(
|
|
2331
|
+
return sessionStorage.getItem(n(e, t)) === "1";
|
|
2313
2332
|
} catch {
|
|
2314
2333
|
return !1;
|
|
2315
2334
|
}
|
|
2316
2335
|
}
|
|
2317
|
-
function
|
|
2336
|
+
function i(e, t) {
|
|
2318
2337
|
if (typeof sessionStorage < "u") try {
|
|
2319
|
-
sessionStorage.setItem(
|
|
2338
|
+
sessionStorage.setItem(n(e, t), "1");
|
|
2320
2339
|
} catch {}
|
|
2321
2340
|
}
|
|
2322
|
-
async function
|
|
2323
|
-
let
|
|
2324
|
-
if (
|
|
2325
|
-
|
|
2341
|
+
async function a(a, o) {
|
|
2342
|
+
let s = a?.trim(), c = o?.trim();
|
|
2343
|
+
if (s && c && !r(s, c)) {
|
|
2344
|
+
i(s, c);
|
|
2326
2345
|
try {
|
|
2327
|
-
await Y(t
|
|
2346
|
+
await Y(y(t, e(), c), { viewCount: V(1) });
|
|
2328
2347
|
} catch (e) {
|
|
2329
2348
|
try {
|
|
2330
|
-
sessionStorage.removeItem(
|
|
2349
|
+
sessionStorage.removeItem(n(s, c));
|
|
2331
2350
|
} catch {}
|
|
2332
2351
|
import.meta.dev && console.warn("[memi-board] recordView failed", e);
|
|
2333
2352
|
}
|
|
2334
2353
|
}
|
|
2335
2354
|
}
|
|
2336
2355
|
return {
|
|
2337
|
-
recordView:
|
|
2338
|
-
hasRecordedView:
|
|
2356
|
+
recordView: a,
|
|
2357
|
+
hasRecordedView: r
|
|
2339
2358
|
};
|
|
2340
2359
|
}
|
|
2341
2360
|
//#endregion
|
|
2342
2361
|
//#region src/composables/useMemiBoardModeration.ts
|
|
2343
|
-
var
|
|
2362
|
+
var vr = {
|
|
2344
2363
|
flagged: !1,
|
|
2345
2364
|
category: "none",
|
|
2346
2365
|
reason: "",
|
|
2347
2366
|
via: "empty"
|
|
2348
2367
|
};
|
|
2349
|
-
function
|
|
2350
|
-
let t =
|
|
2368
|
+
function yr(e = {}) {
|
|
2369
|
+
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Oe(), a = (e) => k(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Pt(), l = Cn([...Sn, ...r.localBlocklist ?? []]);
|
|
2351
2370
|
function u(e) {
|
|
2352
2371
|
let t = e.match(l);
|
|
2353
2372
|
return t ? {
|
|
2354
2373
|
flagged: !0,
|
|
2355
2374
|
category: "abuse",
|
|
2356
|
-
reason:
|
|
2375
|
+
reason: xn({
|
|
2357
2376
|
via: "local",
|
|
2358
2377
|
localHit: t[0] || "",
|
|
2359
2378
|
category: "abuse"
|
|
@@ -2362,21 +2381,21 @@ function gr(e = {}) {
|
|
|
2362
2381
|
} : null;
|
|
2363
2382
|
}
|
|
2364
2383
|
function d() {
|
|
2365
|
-
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a =
|
|
2366
|
-
backend: new
|
|
2384
|
+
let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Ue(i, {
|
|
2385
|
+
backend: new Ve(),
|
|
2367
2386
|
useLimitedUseAppCheckTokens: t
|
|
2368
2387
|
});
|
|
2369
|
-
return
|
|
2388
|
+
return We(a, {
|
|
2370
2389
|
model: e,
|
|
2371
|
-
systemInstruction:
|
|
2390
|
+
systemInstruction: gn(n.value),
|
|
2372
2391
|
generationConfig: {
|
|
2373
2392
|
temperature: 0,
|
|
2374
2393
|
maxOutputTokens: 256,
|
|
2375
2394
|
responseMimeType: "application/json",
|
|
2376
|
-
responseSchema:
|
|
2377
|
-
flagged:
|
|
2378
|
-
category:
|
|
2379
|
-
reason:
|
|
2395
|
+
responseSchema: He.object({ properties: {
|
|
2396
|
+
flagged: He.boolean(),
|
|
2397
|
+
category: He.string(),
|
|
2398
|
+
reason: He.string()
|
|
2380
2399
|
} })
|
|
2381
2400
|
}
|
|
2382
2401
|
});
|
|
@@ -2397,7 +2416,7 @@ function gr(e = {}) {
|
|
|
2397
2416
|
};
|
|
2398
2417
|
}
|
|
2399
2418
|
function p(e, t = "") {
|
|
2400
|
-
let n =
|
|
2419
|
+
let n = xn({
|
|
2401
2420
|
reason: e.reason,
|
|
2402
2421
|
category: e.category,
|
|
2403
2422
|
via: e.via
|
|
@@ -2425,26 +2444,26 @@ function gr(e = {}) {
|
|
|
2425
2444
|
};
|
|
2426
2445
|
let i = e.trim();
|
|
2427
2446
|
if (!i) return {
|
|
2428
|
-
...
|
|
2447
|
+
...vr,
|
|
2429
2448
|
via: "empty"
|
|
2430
2449
|
};
|
|
2431
2450
|
if (t?.skipFilter) return {
|
|
2432
|
-
...
|
|
2451
|
+
...vr,
|
|
2433
2452
|
via: "admin-override"
|
|
2434
2453
|
};
|
|
2435
2454
|
let c = u(i);
|
|
2436
2455
|
if (c) return m(c);
|
|
2437
2456
|
if (r.enabled === !1) return {
|
|
2438
|
-
...
|
|
2457
|
+
...vr,
|
|
2439
2458
|
via: "disabled"
|
|
2440
2459
|
};
|
|
2441
2460
|
try {
|
|
2442
|
-
let e =
|
|
2461
|
+
let e = Tn((await d().generateContent(_n(i, n.value))).response.text());
|
|
2443
2462
|
if (!e) return f();
|
|
2444
2463
|
let t = {
|
|
2445
2464
|
flagged: e.flagged,
|
|
2446
2465
|
category: e.category,
|
|
2447
|
-
reason:
|
|
2466
|
+
reason: xn({
|
|
2448
2467
|
reason: e.reason,
|
|
2449
2468
|
category: e.category,
|
|
2450
2469
|
via: "ai"
|
|
@@ -2464,22 +2483,22 @@ function gr(e = {}) {
|
|
|
2464
2483
|
via: "restricted"
|
|
2465
2484
|
};
|
|
2466
2485
|
if (r.enabled === !1 || !r.moderateImages) return {
|
|
2467
|
-
...
|
|
2486
|
+
...vr,
|
|
2468
2487
|
via: "disabled"
|
|
2469
2488
|
};
|
|
2470
2489
|
try {
|
|
2471
2490
|
let t = await new Promise((t, n) => {
|
|
2472
2491
|
let r = new FileReader();
|
|
2473
2492
|
r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
|
|
2474
|
-
}), r =
|
|
2493
|
+
}), r = Tn((await d().generateContent([{ inlineData: {
|
|
2475
2494
|
mimeType: e.type || "image/jpeg",
|
|
2476
2495
|
data: t
|
|
2477
|
-
} },
|
|
2496
|
+
} }, vn(n.value)])).response.text());
|
|
2478
2497
|
if (!r) return f();
|
|
2479
2498
|
let i = {
|
|
2480
2499
|
flagged: r.flagged,
|
|
2481
2500
|
category: r.category,
|
|
2482
|
-
reason:
|
|
2501
|
+
reason: xn({
|
|
2483
2502
|
reason: r.reason,
|
|
2484
2503
|
category: r.category,
|
|
2485
2504
|
via: "ai"
|
|
@@ -2498,7 +2517,7 @@ function gr(e = {}) {
|
|
|
2498
2517
|
}
|
|
2499
2518
|
//#endregion
|
|
2500
2519
|
//#region src/composables/useMemiBoardUsers.ts
|
|
2501
|
-
var
|
|
2520
|
+
var br = [
|
|
2502
2521
|
"owner",
|
|
2503
2522
|
"admin",
|
|
2504
2523
|
"manager",
|
|
@@ -2506,31 +2525,31 @@ var _r = [
|
|
|
2506
2525
|
"user"
|
|
2507
2526
|
].map((e) => ({
|
|
2508
2527
|
value: e,
|
|
2509
|
-
label:
|
|
2510
|
-
description:
|
|
2528
|
+
label: k("ko", `common.role.${e}`),
|
|
2529
|
+
description: k("ko", `boardUsers.roleDescription.${e}`)
|
|
2511
2530
|
}));
|
|
2512
|
-
function
|
|
2513
|
-
let
|
|
2514
|
-
async function
|
|
2515
|
-
if (!
|
|
2516
|
-
await Y(
|
|
2517
|
-
role:
|
|
2531
|
+
function xr(e = {}) {
|
|
2532
|
+
let t = () => j(), n = Z(), { t: r } = A(), i = N(() => I(e.enabled) ?? !0 ? c(n, t()) : null), a = Te(i, { ssrKey: p(t(), "users-management") });
|
|
2533
|
+
async function o(e, i) {
|
|
2534
|
+
if (!br.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
|
|
2535
|
+
await Y(h(n, t(), e), {
|
|
2536
|
+
role: i,
|
|
2518
2537
|
updatedAt: K()
|
|
2519
2538
|
});
|
|
2520
2539
|
}
|
|
2521
2540
|
return {
|
|
2522
|
-
users:
|
|
2523
|
-
usersPending:
|
|
2524
|
-
updateUserRole:
|
|
2541
|
+
users: a,
|
|
2542
|
+
usersPending: a.pending,
|
|
2543
|
+
updateUserRole: o
|
|
2525
2544
|
};
|
|
2526
2545
|
}
|
|
2527
2546
|
//#endregion
|
|
2528
2547
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2529
|
-
function
|
|
2530
|
-
let t = Z(), n = () =>
|
|
2548
|
+
function Sr(e) {
|
|
2549
|
+
let t = Z(), n = () => j(), r = N(() => I(e)), i = N(() => {
|
|
2531
2550
|
let e = r.value;
|
|
2532
|
-
return e ?
|
|
2533
|
-
}), { data: a, pending: o } =
|
|
2551
|
+
return e ? h(t, n(), e) : null;
|
|
2552
|
+
}), { data: a, pending: o } = De(i);
|
|
2534
2553
|
return {
|
|
2535
2554
|
profile: a,
|
|
2536
2555
|
profilePending: o
|
|
@@ -2538,7 +2557,7 @@ function yr(e) {
|
|
|
2538
2557
|
}
|
|
2539
2558
|
//#endregion
|
|
2540
2559
|
//#region src/utils/section.ts
|
|
2541
|
-
var
|
|
2560
|
+
var Cr = [
|
|
2542
2561
|
3,
|
|
2543
2562
|
4,
|
|
2544
2563
|
5,
|
|
@@ -2548,43 +2567,43 @@ var br = [
|
|
|
2548
2567
|
9,
|
|
2549
2568
|
12
|
|
2550
2569
|
];
|
|
2551
|
-
function
|
|
2570
|
+
function wr(e) {
|
|
2552
2571
|
return [
|
|
2553
2572
|
{
|
|
2554
2573
|
value: "list",
|
|
2555
|
-
label:
|
|
2574
|
+
label: k(e, "sectionUtil.kind.list")
|
|
2556
2575
|
},
|
|
2557
2576
|
{
|
|
2558
2577
|
value: "post",
|
|
2559
|
-
label:
|
|
2578
|
+
label: k(e, "sectionUtil.kind.post")
|
|
2560
2579
|
},
|
|
2561
2580
|
{
|
|
2562
2581
|
value: "comments",
|
|
2563
|
-
label:
|
|
2582
|
+
label: k(e, "sectionUtil.kind.comments")
|
|
2564
2583
|
}
|
|
2565
2584
|
];
|
|
2566
2585
|
}
|
|
2567
|
-
function
|
|
2586
|
+
function Tr(e) {
|
|
2568
2587
|
return [{
|
|
2569
2588
|
value: "latest",
|
|
2570
|
-
label:
|
|
2589
|
+
label: k(e, "sectionUtil.sort.latest")
|
|
2571
2590
|
}, {
|
|
2572
2591
|
value: "likes",
|
|
2573
|
-
label:
|
|
2592
|
+
label: k(e, "sectionUtil.sort.likes")
|
|
2574
2593
|
}];
|
|
2575
2594
|
}
|
|
2576
|
-
var
|
|
2577
|
-
function
|
|
2595
|
+
var Er = wr("ko"), Dr = Tr("ko");
|
|
2596
|
+
function Or(e) {
|
|
2578
2597
|
return e === "list" ? 5 : 4;
|
|
2579
2598
|
}
|
|
2580
|
-
function
|
|
2599
|
+
function kr(e, t) {
|
|
2581
2600
|
let n = Math.floor(Number(t));
|
|
2582
|
-
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) :
|
|
2601
|
+
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Or(e);
|
|
2583
2602
|
}
|
|
2584
|
-
function
|
|
2603
|
+
function Ar(e) {
|
|
2585
2604
|
return e === 12 ? null : 12 - e;
|
|
2586
2605
|
}
|
|
2587
|
-
function
|
|
2606
|
+
function jr(e) {
|
|
2588
2607
|
return {
|
|
2589
2608
|
3: "col-span-12 md:col-span-3",
|
|
2590
2609
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2596,36 +2615,36 @@ function Or(e) {
|
|
|
2596
2615
|
12: "col-span-12"
|
|
2597
2616
|
}[e];
|
|
2598
2617
|
}
|
|
2599
|
-
function
|
|
2618
|
+
function Mr(e) {
|
|
2600
2619
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2601
2620
|
}
|
|
2602
|
-
function
|
|
2621
|
+
function Nr(e) {
|
|
2603
2622
|
let t = e.boardId || e.category || "free";
|
|
2604
2623
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2605
2624
|
}
|
|
2606
|
-
function
|
|
2625
|
+
function Pr(e) {
|
|
2607
2626
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2608
2627
|
}
|
|
2609
|
-
function
|
|
2628
|
+
function Fr(e) {
|
|
2610
2629
|
if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2611
2630
|
}
|
|
2612
|
-
function
|
|
2631
|
+
function Ir(e) {
|
|
2613
2632
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2614
2633
|
}
|
|
2615
|
-
function
|
|
2616
|
-
return
|
|
2634
|
+
function Lr(e, t) {
|
|
2635
|
+
return lt(e, t).trim() || ut(e, t).trim() || k(t, "sectionUtil.untitledPost");
|
|
2617
2636
|
}
|
|
2618
|
-
function
|
|
2637
|
+
function Rr(e) {
|
|
2619
2638
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2620
2639
|
}
|
|
2621
|
-
function
|
|
2622
|
-
return
|
|
2640
|
+
function zr(e, t) {
|
|
2641
|
+
return k(t, Rr(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
|
|
2623
2642
|
}
|
|
2624
|
-
function
|
|
2643
|
+
function Br(e, t) {
|
|
2625
2644
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2626
2645
|
}
|
|
2627
|
-
function
|
|
2628
|
-
let n = e.map((e) =>
|
|
2646
|
+
function Vr(e, t) {
|
|
2647
|
+
let n = e.map((e) => Br(e.boardId, t));
|
|
2629
2648
|
if (!n.length) return "default";
|
|
2630
2649
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2631
2650
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2633,27 +2652,27 @@ function Rr(e, t) {
|
|
|
2633
2652
|
}
|
|
2634
2653
|
//#endregion
|
|
2635
2654
|
//#region src/composables/useMemiBoardSections.ts
|
|
2636
|
-
var
|
|
2637
|
-
function
|
|
2655
|
+
var Hr = [];
|
|
2656
|
+
function Ur(e) {
|
|
2638
2657
|
let t = Number(e.cols);
|
|
2639
|
-
if (
|
|
2658
|
+
if (Cr.includes(t)) return t;
|
|
2640
2659
|
let n = e.width;
|
|
2641
2660
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2642
2661
|
}
|
|
2643
|
-
function
|
|
2662
|
+
function Wr(e) {
|
|
2644
2663
|
let t = e.height ?? e.size;
|
|
2645
2664
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2646
2665
|
}
|
|
2647
|
-
function
|
|
2666
|
+
function Gr(e) {
|
|
2648
2667
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2649
2668
|
}
|
|
2650
|
-
function
|
|
2669
|
+
function Kr(e) {
|
|
2651
2670
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2652
2671
|
}
|
|
2653
|
-
function
|
|
2672
|
+
function qr(e) {
|
|
2654
2673
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2655
2674
|
}
|
|
2656
|
-
function
|
|
2675
|
+
function Jr(e, t) {
|
|
2657
2676
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2658
2677
|
for (let e of n) {
|
|
2659
2678
|
let n = String(e || "").trim();
|
|
@@ -2661,131 +2680,145 @@ function Gr(e, t) {
|
|
|
2661
2680
|
}
|
|
2662
2681
|
return r;
|
|
2663
2682
|
}
|
|
2664
|
-
function
|
|
2665
|
-
return
|
|
2683
|
+
function Yr(e, t) {
|
|
2684
|
+
return k(t, "sections.defaultTitle", { n: e + 1 });
|
|
2666
2685
|
}
|
|
2667
|
-
function
|
|
2668
|
-
let r =
|
|
2686
|
+
function Xr(e, t, n) {
|
|
2687
|
+
let r = Gr(e), i = kr(r, e.count), a = Q(e.i18n), o = rt(e.i18nSource);
|
|
2669
2688
|
return {
|
|
2670
2689
|
id: String(e.id || `box-${t + 1}`),
|
|
2671
|
-
title: String(e.title ||
|
|
2690
|
+
title: String(e.title || Yr(t, n)),
|
|
2672
2691
|
showTitle: e.showTitle !== !1,
|
|
2673
2692
|
kind: r,
|
|
2674
|
-
boardId:
|
|
2693
|
+
boardId: qr(e),
|
|
2675
2694
|
count: i,
|
|
2676
|
-
postIds:
|
|
2677
|
-
sort:
|
|
2678
|
-
cols:
|
|
2679
|
-
height:
|
|
2695
|
+
postIds: Jr(e, i),
|
|
2696
|
+
sort: Kr(e),
|
|
2697
|
+
cols: Ur(e),
|
|
2698
|
+
height: Wr(e),
|
|
2680
2699
|
order: t,
|
|
2681
2700
|
...a ? { i18n: a } : {},
|
|
2682
2701
|
...o ? { i18nSource: o } : {}
|
|
2683
2702
|
};
|
|
2684
2703
|
}
|
|
2685
|
-
function
|
|
2686
|
-
let { locale: e } =
|
|
2687
|
-
async function
|
|
2688
|
-
if (!
|
|
2689
|
-
let
|
|
2704
|
+
function Zr() {
|
|
2705
|
+
let { locale: e } = A(), { translateEntries: t } = Nn(), r = Z(), i = ye(r, "settings", "homeLayout"), { data: a, pending: o } = De(i), s = N(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : Hr).map((t, n) => Xr(t, n, e.value)));
|
|
2706
|
+
async function c(r) {
|
|
2707
|
+
if (!n()) return r;
|
|
2708
|
+
let i = e.value, a = r.filter((e) => {
|
|
2690
2709
|
let t = e.title.trim();
|
|
2691
|
-
return t ? !(e.i18nSource?.title === t &&
|
|
2710
|
+
return t ? !(e.i18nSource?.title === t && ot(e.i18n, ["title"])) : !1;
|
|
2692
2711
|
});
|
|
2693
|
-
if (!
|
|
2694
|
-
let
|
|
2712
|
+
if (!a.length) return r;
|
|
2713
|
+
let o = await t(a.map((e) => ({
|
|
2695
2714
|
id: e.id,
|
|
2696
2715
|
fields: { title: e.title.trim() },
|
|
2697
2716
|
context: e.kind
|
|
2698
2717
|
})), {
|
|
2699
|
-
source:
|
|
2718
|
+
source: i,
|
|
2700
2719
|
purpose: "section heading on a website home page"
|
|
2701
2720
|
});
|
|
2702
|
-
return
|
|
2703
|
-
let t =
|
|
2721
|
+
return r.map((e) => {
|
|
2722
|
+
let t = o[e.id];
|
|
2704
2723
|
if (!t) return e;
|
|
2705
|
-
let n = e.title.trim(),
|
|
2706
|
-
return
|
|
2724
|
+
let n = e.title.trim(), r = at(i, { title: n }, t);
|
|
2725
|
+
return ot(r, ["title"]) ? {
|
|
2707
2726
|
...e,
|
|
2708
2727
|
title: n,
|
|
2709
|
-
i18n:
|
|
2728
|
+
i18n: r,
|
|
2710
2729
|
i18nSource: {
|
|
2711
|
-
locale:
|
|
2730
|
+
locale: i,
|
|
2712
2731
|
title: n
|
|
2713
2732
|
}
|
|
2714
2733
|
} : e;
|
|
2715
2734
|
});
|
|
2716
2735
|
}
|
|
2717
|
-
function
|
|
2718
|
-
let r = t, i =
|
|
2736
|
+
function l(t, n) {
|
|
2737
|
+
let r = t, i = Gr(r), a = kr(i, t.count), o = t.title.trim() || Yr(n, e.value);
|
|
2719
2738
|
return {
|
|
2720
2739
|
id: t.id,
|
|
2721
2740
|
title: o,
|
|
2722
2741
|
showTitle: t.showTitle !== !1,
|
|
2723
2742
|
kind: i,
|
|
2724
|
-
boardId:
|
|
2743
|
+
boardId: qr(r),
|
|
2725
2744
|
count: a,
|
|
2726
|
-
postIds:
|
|
2727
|
-
sort:
|
|
2728
|
-
cols:
|
|
2729
|
-
height:
|
|
2745
|
+
postIds: Jr(r, a),
|
|
2746
|
+
sort: Kr(r),
|
|
2747
|
+
cols: Ur(r),
|
|
2748
|
+
height: Wr(r),
|
|
2730
2749
|
order: n,
|
|
2731
2750
|
...t.i18n ? { i18n: t.i18n } : {},
|
|
2732
2751
|
...t.i18nSource?.title === o ? { i18nSource: t.i18nSource } : {}
|
|
2733
2752
|
};
|
|
2734
2753
|
}
|
|
2735
|
-
async function
|
|
2754
|
+
async function u(e) {
|
|
2736
2755
|
let t = e;
|
|
2737
2756
|
try {
|
|
2738
|
-
t = await
|
|
2757
|
+
t = await c(e);
|
|
2739
2758
|
} catch (e) {
|
|
2740
2759
|
console.warn("[memi-board] section translation skipped", e);
|
|
2741
2760
|
}
|
|
2742
|
-
await q(
|
|
2761
|
+
await q(i, {
|
|
2743
2762
|
columns: 12,
|
|
2744
|
-
blocks: t.map((e, t) =>
|
|
2763
|
+
blocks: t.map((e, t) => l(e, t)),
|
|
2745
2764
|
updatedAt: K()
|
|
2746
2765
|
});
|
|
2747
2766
|
}
|
|
2748
2767
|
return {
|
|
2749
|
-
settings:
|
|
2750
|
-
pending:
|
|
2751
|
-
sections:
|
|
2752
|
-
blocks:
|
|
2753
|
-
hasSavedLayout: N(() => Array.isArray(
|
|
2754
|
-
saveSections:
|
|
2755
|
-
saveBlocks:
|
|
2768
|
+
settings: a,
|
|
2769
|
+
pending: o,
|
|
2770
|
+
sections: s,
|
|
2771
|
+
blocks: s,
|
|
2772
|
+
hasSavedLayout: N(() => Array.isArray(a.value?.blocks)),
|
|
2773
|
+
saveSections: u,
|
|
2774
|
+
saveBlocks: u
|
|
2756
2775
|
};
|
|
2757
2776
|
}
|
|
2758
2777
|
//#endregion
|
|
2759
2778
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2760
|
-
function
|
|
2761
|
-
let
|
|
2762
|
-
let
|
|
2763
|
-
return a &&
|
|
2764
|
-
}), { data:
|
|
2779
|
+
function Qr(e, t) {
|
|
2780
|
+
let n = Z(), r = () => j(), i = I(e)?.trim() || "all", a = N(() => {
|
|
2781
|
+
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)), G(o(n, r()), ...s, W("createdAt", "desc"), H(i));
|
|
2783
|
+
}), { data: s, pending: c, error: l } = Te(a, { ssrKey: p(r(), `mini-list/${i}`) });
|
|
2765
2784
|
return {
|
|
2766
|
-
posts: N(() =>
|
|
2767
|
-
pending:
|
|
2768
|
-
error:
|
|
2785
|
+
posts: N(() => s.value ?? []),
|
|
2786
|
+
pending: c,
|
|
2787
|
+
error: l
|
|
2769
2788
|
};
|
|
2770
2789
|
}
|
|
2771
|
-
function
|
|
2772
|
-
let
|
|
2790
|
+
function $r(e) {
|
|
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 ? G(o(t, n()), X(R(), "in", r.value)) : null), { data: s, pending: c, error: l } = Te(a, { ssrKey: p(n(), `mini-picked/${i}`) });
|
|
2773
2792
|
return {
|
|
2774
2793
|
posts: N(() => {
|
|
2775
|
-
let e = new Map((
|
|
2776
|
-
for (let n of
|
|
2794
|
+
let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
|
|
2795
|
+
for (let n of r.value) {
|
|
2777
2796
|
let r = e.get(n);
|
|
2778
2797
|
r && r.isPublished !== !1 && t.push(r);
|
|
2779
2798
|
}
|
|
2780
2799
|
return t;
|
|
2781
2800
|
}),
|
|
2782
|
-
pending:
|
|
2783
|
-
error:
|
|
2801
|
+
pending: c,
|
|
2802
|
+
error: l
|
|
2784
2803
|
};
|
|
2785
2804
|
}
|
|
2786
2805
|
//#endregion
|
|
2787
2806
|
//#region src/data/versionHistory.ts
|
|
2788
|
-
var
|
|
2807
|
+
var ei = [
|
|
2808
|
+
{
|
|
2809
|
+
version: "0.48.0",
|
|
2810
|
+
date: "2026-10-29",
|
|
2811
|
+
highlights: [
|
|
2812
|
+
"댓글과 답글에 이미지를 한 장 첨부할 수 있어요",
|
|
2813
|
+
"큰 이미지는 올리기 전에 5MB 이하로 줄여요",
|
|
2814
|
+
"글을 지우면 댓글 이미지도 함께 지워요"
|
|
2815
|
+
]
|
|
2816
|
+
},
|
|
2817
|
+
{
|
|
2818
|
+
version: "0.47.1",
|
|
2819
|
+
date: "2026-10-29",
|
|
2820
|
+
highlights: ["AI 글쓰기 도우미가 실패하면 그 시도는 하루 횟수에서 빠져요", "App Check 연결 안내를 한 번용 토큰과 SPA에 맞게 고쳤어요"]
|
|
2821
|
+
},
|
|
2789
2822
|
{
|
|
2790
2823
|
version: "0.47.0",
|
|
2791
2824
|
date: "2026-10-23",
|
|
@@ -3111,7 +3144,7 @@ var Zr = [
|
|
|
3111
3144
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
3112
3145
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
3113
3146
|
}
|
|
3114
|
-
],
|
|
3147
|
+
], ti = 10, ni = 864e5, ri = 200, ii = {
|
|
3115
3148
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
3116
3149
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
3117
3150
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -3120,7 +3153,7 @@ var Zr = [
|
|
|
3120
3153
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
3121
3154
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
3122
3155
|
};
|
|
3123
|
-
function
|
|
3156
|
+
function ai(e) {
|
|
3124
3157
|
return {
|
|
3125
3158
|
proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
|
|
3126
3159
|
polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
|
|
@@ -3131,136 +3164,177 @@ function ni(e) {
|
|
|
3131
3164
|
title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
|
|
3132
3165
|
};
|
|
3133
3166
|
}
|
|
3134
|
-
function
|
|
3135
|
-
let r = t === "ko", i =
|
|
3167
|
+
function oi(e, t, n) {
|
|
3168
|
+
let r = t === "ko", i = mn(t);
|
|
3136
3169
|
if (e.action !== "custom") {
|
|
3137
|
-
if (r) return
|
|
3138
|
-
let t =
|
|
3170
|
+
if (r) return ii[e.action];
|
|
3171
|
+
let t = ai(i)[e.action];
|
|
3139
3172
|
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}.`;
|
|
3140
3173
|
}
|
|
3141
|
-
let a = e.customInstruction?.trim().slice(0,
|
|
3174
|
+
let a = e.customInstruction?.trim().slice(0, ri);
|
|
3142
3175
|
if (!a) throw Error(n("writingAssistant.customRequired"));
|
|
3143
3176
|
return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
|
|
3144
3177
|
}
|
|
3145
|
-
function
|
|
3178
|
+
function si(e) {
|
|
3146
3179
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
3147
3180
|
}
|
|
3148
|
-
function
|
|
3181
|
+
function ci(e) {
|
|
3149
3182
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
3150
3183
|
}
|
|
3151
|
-
function
|
|
3152
|
-
let t =
|
|
3153
|
-
|
|
3154
|
-
let e =
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3184
|
+
function li(e = {}) {
|
|
3185
|
+
let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = (e, t) => k(n.value, e, t), i = Oe(), a = Z(), o = Ee(), s = v(), { translateEntries: c, translateHtml: l } = Nn();
|
|
3186
|
+
function u(e, t) {
|
|
3187
|
+
let n = ci(e.aiWritingWindowAt), r = n != null && t - n < ni, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
|
|
3188
|
+
return {
|
|
3189
|
+
active: r,
|
|
3190
|
+
windowAt: n,
|
|
3191
|
+
count: r ? i : 0,
|
|
3192
|
+
pending: r ? a : 0
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
3195
|
+
function d(e, t) {
|
|
3196
|
+
let n = e + ni, i = Math.max(1, Math.ceil((n - t) / 36e5));
|
|
3197
|
+
return Error(r("writingAssistant.dailyLimit", {
|
|
3198
|
+
limit: 10,
|
|
3199
|
+
hours: i
|
|
3200
|
+
}));
|
|
3201
|
+
}
|
|
3202
|
+
async function f(e) {
|
|
3203
|
+
let t = o.value?.uid;
|
|
3204
|
+
if (!t) throw Error(r("writingAssistant.loginRequired"));
|
|
3205
|
+
await Ce(a, async (n) => {
|
|
3206
|
+
let i = h(a, j(), t), o = await n.get(i);
|
|
3207
|
+
if (!o.exists()) throw Error(r("writingAssistant.userMissing"));
|
|
3208
|
+
let s = o.data(), c = e(u(s, Date.now()), s);
|
|
3209
|
+
c && n.update(i, {
|
|
3210
|
+
...c,
|
|
3170
3211
|
updatedAt: K()
|
|
3171
3212
|
});
|
|
3172
3213
|
});
|
|
3173
3214
|
}
|
|
3174
|
-
async function
|
|
3175
|
-
(e
|
|
3176
|
-
|
|
3215
|
+
async function p() {
|
|
3216
|
+
await f((e) => {
|
|
3217
|
+
if (e.active && e.windowAt != null && e.count + e.pending >= 10) throw d(e.windowAt, Date.now());
|
|
3218
|
+
return e.active ? { aiWritingPending: e.pending + 1 } : {
|
|
3219
|
+
aiWritingCount: 0,
|
|
3220
|
+
aiWritingPending: 1,
|
|
3221
|
+
aiWritingWindowAt: K()
|
|
3222
|
+
};
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
async function m() {
|
|
3226
|
+
await f((e) => !e.active || e.pending < 1 || e.count >= 10 ? null : {
|
|
3227
|
+
aiWritingCount: e.count + 1,
|
|
3228
|
+
aiWritingPending: e.pending - 1
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
async function g() {
|
|
3232
|
+
await f((e) => !e.active || e.pending < 1 ? null : { aiWritingPending: e.pending - 1 });
|
|
3233
|
+
}
|
|
3234
|
+
async function _(e) {
|
|
3235
|
+
await p();
|
|
3236
|
+
try {
|
|
3237
|
+
let t = await e();
|
|
3238
|
+
try {
|
|
3239
|
+
await m();
|
|
3240
|
+
} catch {}
|
|
3241
|
+
return t;
|
|
3242
|
+
} catch (e) {
|
|
3243
|
+
throw await g().catch(() => {}), e;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
async function y(e) {
|
|
3247
|
+
(e.action === "translate-en" || e.action === "translate-ko") && D();
|
|
3248
|
+
let t = n.value, a = t === "ko", o = oi(e, t, r);
|
|
3177
3249
|
if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3250
|
+
return _(async () => {
|
|
3251
|
+
let t = s.moderation ?? {}, n = Ue(i, {
|
|
3252
|
+
backend: new Ve(),
|
|
3253
|
+
useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
|
|
3254
|
+
}), c = We(n, {
|
|
3255
|
+
model: t.model ?? "gemini-3.5-flash-lite",
|
|
3256
|
+
systemInstruction: (a ? [
|
|
3257
|
+
"너는 게시판 글쓰기 편집 도우미다.",
|
|
3258
|
+
"사용자가 요청한 편집 결과만 출력하고 설명, 인사, 마크다운 코드 펜스를 붙이지 마라.",
|
|
3259
|
+
e.action === "title" || e.selection ? "출력은 일반 텍스트로 작성하라." : "입력의 HTML 구조와 링크, 이미지 태그를 보존하고 결과도 HTML 조각으로 출력하라."
|
|
3260
|
+
] : [
|
|
3261
|
+
"You are an editing assistant for writing message board posts.",
|
|
3262
|
+
"Output only the requested edit result. Do not add explanations, greetings, or markdown code fences.",
|
|
3263
|
+
e.action === "title" || e.selection ? "Write the output as plain text." : "Preserve the HTML structure, links, and image tags of the input, and output the result as an HTML fragment as well."
|
|
3264
|
+
]).join("\n"),
|
|
3265
|
+
generationConfig: {
|
|
3266
|
+
temperature: e.action === "continue" || e.action === "title" || e.action === "custom" ? .6 : .2,
|
|
3267
|
+
maxOutputTokens: 2048
|
|
3268
|
+
}
|
|
3269
|
+
}), l = e.content.trim(), u = (a ? [
|
|
3270
|
+
`작업: ${o}`,
|
|
3271
|
+
e.title ? `현재 제목: ${e.title}` : "",
|
|
3272
|
+
`본문:\n${l.slice(0, 12e3)}`
|
|
3188
3273
|
] : [
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
]).join("\n"),
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
}
|
|
3197
|
-
}), f = e.content.trim(), p = (a ? [
|
|
3198
|
-
`작업: ${o}`,
|
|
3199
|
-
e.title ? `현재 제목: ${e.title}` : "",
|
|
3200
|
-
`본문:\n${f.slice(0, 12e3)}`
|
|
3201
|
-
] : [
|
|
3202
|
-
`Task: ${o}`,
|
|
3203
|
-
e.title ? `Current title: ${e.title}` : "",
|
|
3204
|
-
`Body:\n${f.slice(0, 12e3)}`
|
|
3205
|
-
]).filter(Boolean).join("\n\n"), m = ii((await d.generateContent(p)).response.text());
|
|
3206
|
-
if (!m) throw Error(r("writingAssistant.emptyResult"));
|
|
3207
|
-
return m;
|
|
3274
|
+
`Task: ${o}`,
|
|
3275
|
+
e.title ? `Current title: ${e.title}` : "",
|
|
3276
|
+
`Body:\n${l.slice(0, 12e3)}`
|
|
3277
|
+
]).filter(Boolean).join("\n\n"), d = si((await c.generateContent(u)).response.text());
|
|
3278
|
+
if (!d) throw Error(r("writingAssistant.emptyResult"));
|
|
3279
|
+
return d;
|
|
3280
|
+
});
|
|
3208
3281
|
}
|
|
3209
|
-
async function
|
|
3210
|
-
|
|
3282
|
+
async function b(e) {
|
|
3283
|
+
D();
|
|
3211
3284
|
let t = e.source ?? n.value, i = e.title?.trim() ?? "", a = e.summary?.trim() ?? "", o = e.content ?? "", s = {};
|
|
3212
3285
|
i && (s.title = i), a && (s.summary = a);
|
|
3213
|
-
let
|
|
3214
|
-
if (!Object.keys(s).length && !
|
|
3215
|
-
if (
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
};
|
|
3234
|
-
if (d) {
|
|
3235
|
-
let e = {};
|
|
3236
|
-
for (let [t, n] of Object.entries(p)) {
|
|
3237
|
-
let r = O(t);
|
|
3238
|
-
r && typeof n == "string" && n.trim() && (e[r] = { content: n.trim() });
|
|
3239
|
-
}
|
|
3240
|
-
let n = rt(t, { content: o }, e);
|
|
3241
|
-
if (!it(n, ["content"])) throw Error(r("writingAssistant.translateIncomplete"));
|
|
3242
|
-
h.contentI18n = n, h.contentI18nSource = {
|
|
3243
|
-
locale: t,
|
|
3244
|
-
hash: $e(o)
|
|
3286
|
+
let u = !!Qt(o);
|
|
3287
|
+
if (!Object.keys(s).length && !u) throw Error(r("writingAssistant.translateEmpty"));
|
|
3288
|
+
if (u && o.length > 12e3) throw Error(r("writingAssistant.translateTooLong", { limit: On }));
|
|
3289
|
+
return _(async () => {
|
|
3290
|
+
let [e, n] = await Promise.all([Object.keys(s).length ? c([{
|
|
3291
|
+
id: "post",
|
|
3292
|
+
fields: s,
|
|
3293
|
+
context: "message board post title and short summary"
|
|
3294
|
+
}], {
|
|
3295
|
+
source: t,
|
|
3296
|
+
purpose: "message board post title and short summary"
|
|
3297
|
+
}) : Promise.resolve({}), u ? l(o, { source: t }) : Promise.resolve({})]), d = at(t, s, e.post);
|
|
3298
|
+
if (Object.keys(s).length && !ot(d, Object.keys(s))) throw Error(r("writingAssistant.translateIncomplete"));
|
|
3299
|
+
let f = {
|
|
3300
|
+
i18n: d,
|
|
3301
|
+
i18nSource: {
|
|
3302
|
+
locale: t,
|
|
3303
|
+
title: i,
|
|
3304
|
+
summary: a
|
|
3305
|
+
}
|
|
3245
3306
|
};
|
|
3246
|
-
|
|
3247
|
-
|
|
3307
|
+
if (u) {
|
|
3308
|
+
let e = {};
|
|
3309
|
+
for (let [t, r] of Object.entries(n)) {
|
|
3310
|
+
let n = M(t);
|
|
3311
|
+
n && typeof r == "string" && r.trim() && (e[n] = { content: r.trim() });
|
|
3312
|
+
}
|
|
3313
|
+
let i = at(t, { content: o }, e);
|
|
3314
|
+
if (!ot(i, ["content"])) throw Error(r("writingAssistant.translateIncomplete"));
|
|
3315
|
+
f.contentI18n = i, f.contentI18nSource = {
|
|
3316
|
+
locale: t,
|
|
3317
|
+
hash: tt(o)
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
return f;
|
|
3321
|
+
});
|
|
3248
3322
|
}
|
|
3249
3323
|
return {
|
|
3250
|
-
assist:
|
|
3251
|
-
translatePost:
|
|
3324
|
+
assist: y,
|
|
3325
|
+
translatePost: b
|
|
3252
3326
|
};
|
|
3253
3327
|
}
|
|
3254
3328
|
//#endregion
|
|
3255
3329
|
//#region src/utils/renderMarkdown.ts
|
|
3256
|
-
function
|
|
3330
|
+
function ui(e) {
|
|
3257
3331
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3258
3332
|
}
|
|
3259
|
-
function
|
|
3333
|
+
function di(e) {
|
|
3260
3334
|
let t = e;
|
|
3261
3335
|
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;
|
|
3262
3336
|
}
|
|
3263
|
-
function
|
|
3337
|
+
function fi(e) {
|
|
3264
3338
|
if (!e?.trim()) return "";
|
|
3265
3339
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
3266
3340
|
function c() {
|
|
@@ -3269,7 +3343,7 @@ function li(e) {
|
|
|
3269
3343
|
return;
|
|
3270
3344
|
}
|
|
3271
3345
|
let e = o;
|
|
3272
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
3346
|
+
n.push(`<${e}>${s.map((e) => `<li>${di(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
3273
3347
|
}
|
|
3274
3348
|
for (; r < t.length;) {
|
|
3275
3349
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -3278,7 +3352,7 @@ function li(e) {
|
|
|
3278
3352
|
continue;
|
|
3279
3353
|
}
|
|
3280
3354
|
if (i) {
|
|
3281
|
-
a.push(
|
|
3355
|
+
a.push(ui(e)), r++;
|
|
3282
3356
|
continue;
|
|
3283
3357
|
}
|
|
3284
3358
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -3289,24 +3363,24 @@ function li(e) {
|
|
|
3289
3363
|
if (u) {
|
|
3290
3364
|
c();
|
|
3291
3365
|
let e = u[1].length;
|
|
3292
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${
|
|
3366
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${di(ui(u[2]))}</h${e}>`), r++;
|
|
3293
3367
|
continue;
|
|
3294
3368
|
}
|
|
3295
3369
|
if (e.startsWith("> ")) {
|
|
3296
3370
|
c();
|
|
3297
3371
|
let e = [];
|
|
3298
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
3299
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
3372
|
+
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(ui((t[r] ?? "").slice(2))), r++;
|
|
3373
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${di(e.join("<br />"))}</blockquote>`);
|
|
3300
3374
|
continue;
|
|
3301
3375
|
}
|
|
3302
3376
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
3303
3377
|
if (d) {
|
|
3304
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
3378
|
+
o && o !== "ul" && c(), o = "ul", s.push(ui(d[1])), r++;
|
|
3305
3379
|
continue;
|
|
3306
3380
|
}
|
|
3307
3381
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
3308
3382
|
if (f) {
|
|
3309
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
3383
|
+
o && o !== "ol" && c(), o = "ol", s.push(ui(f[1])), r++;
|
|
3310
3384
|
continue;
|
|
3311
3385
|
}
|
|
3312
3386
|
if (!e.trim()) {
|
|
@@ -3318,13 +3392,13 @@ function li(e) {
|
|
|
3318
3392
|
for (; r < t.length;) {
|
|
3319
3393
|
let e = t[r] ?? "";
|
|
3320
3394
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
3321
|
-
p.push(
|
|
3395
|
+
p.push(ui(e)), r++;
|
|
3322
3396
|
}
|
|
3323
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
3397
|
+
n.push(`<p class="my-2 leading-relaxed">${di(p.join("<br />"))}</p>`);
|
|
3324
3398
|
}
|
|
3325
3399
|
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");
|
|
3326
3400
|
}
|
|
3327
3401
|
//#endregion
|
|
3328
|
-
export {
|
|
3402
|
+
export { Qn as AUTHOR_MEMO_MAX_LENGTH, Cr as BOARD_SECTION_COLS, Er as BOARD_SECTION_KINDS, Dr as BOARD_SECTION_SORTS, br as BOARD_USER_ROLES, ir as COMMENT_BODY_MAX_LENGTH, Et as DEFAULT_BLOCK_BAN_DECAY_MS, Tt as DEFAULT_BLOCK_BAN_THRESHOLD, Pn as DEFAULT_BOARDS, Fn 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, On as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, dr as REPORT_DETAIL_MAX_LENGTH, fr as REPORT_REASONS, ti as WRITING_ASSISTANT_DAILY_LIMIT, Ke as asHttpUrl, ft as boardCopyIsTranslated, Qe as boardListOgDescription, Ze as boardListOgTitle, Xe as boardPostOgDescription, Ye as boardPostOgTitle, wr as boardSectionKinds, Tr as boardSectionSorts, p as boardSsrKey, h as boardUserDoc, c as boardUsersCol, sn as buildPostPreview, bt as canManageBoardByRole, xt as canWriteCommentByRole, kr as clampSectionCount, d as commentDoc, g as commentsCol, me as configureMemiBoard, ot as coversLocales, Or as defaultSectionCount, dt as displayPostContent, ut as displayPostSummary, lt as displayPostTitle, ct as displayStoredField, Dt as effectiveModerationBlockCount, Bt as extractEditorImageUrls, _t as fetchPublicListForSeo, gt as fetchPublicPostForSeo, ee as formatDate, oe as formatFullDate, fe as formatRelativeDate, At as formatRestrictedUntilLabel, te as formatTimestampDetails, tn as hasBodyImage, en as hasBodyText, tt as hashText, Rt as imeSafeSubmitClick, Lt as imeSafeSubmitPointerDown, yt as isAssignedStaff, nn as isContentEmpty, Bn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, ne as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, kt as isModerationWriteRestricted, he as likeDoc, t as likeDocId, b as likesCol, ce as loadMemiBoardLocale, st as localizedText, lr as memiBoardCommentLikesKey, se as memiBoardHostLocaleKey, pe as memiBoardLocaleInfo, at as mergeSourceFields, Br as miniBoardView, Fr as miniCommentLink, Pr as miniMoreLink, Ir as miniPostImage, Nr as miniPostLink, Lr as miniPostTitle, Vr as miniPostsView, Rr as miniViewGroup, zr as miniViewLabel, Ot as moderationWriteRestrictedUntilMs, $e as normalizeBasePath, Xr as normalizeBoardSection, M as normalizeMemiBoardLocale, Ar as pairSectionCols, Qt as plainTextFromHtml, s as postBodyDoc, y as postDoc, Ht as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, rt as readI18nSource, Q as readLocalizedFields, fi as renderMarkdownToHtml, u as reportDoc, le as reportDocId, pr as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, pt as resolvePublicSeoDb, jr as sectionColClass, Mr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Vt as storagePathFromDownloadUrl, $t as titleFromBody, Je as toAbsoluteUrl, qe as toBoardOgImageUrl, k as translate, j as useBoardPathConfig, Pt as useMemiBoardAuth, er as useMemiBoardAuthorMemo, Nn as useMemiBoardAutoTranslate, Zn as useMemiBoardCommentFeed, ur as useMemiBoardCommentLikes, or as useMemiBoardComments, v as useMemiBoardConfig, A as useMemiBoardI18n, cr as useMemiBoardLikes, Qr as useMemiBoardMiniLatest, $r as useMemiBoardMiniPicked, yr as useMemiBoardModeration, Gn as useMemiBoardPost, Wn as useMemiBoardPostList, Hn as useMemiBoardPosts, sr as useMemiBoardReplies, gr as useMemiBoardReportQueue, mr as useMemiBoardReports, Zr as useMemiBoardSections, Rn as useMemiBoardSettings, Yn as useMemiBoardUserComments, qn as useMemiBoardUserPosts, Sr as useMemiBoardUserProfile, xr as useMemiBoardUsers, _r as useMemiBoardViews, li as useMemiBoardWritingAssistant, ei as versionHistory, on as videoListCoverUrl, an as youtubeId };
|
|
3329
3403
|
|
|
3330
3404
|
//# sourceMappingURL=index.js.map
|