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