memi-board 0.42.1 → 0.44.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 +17 -1
- package/dist/composables/useMemiBoardComments.d.ts +6 -0
- package/dist/composables/useMemiBoardPosts.d.ts +15 -0
- package/dist/composables/useMemiBoardReports.d.ts +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +646 -608
- package/dist/index.js.map +1 -1
- package/dist/module.mjs.map +1 -1
- package/dist/runtime/components/Editor.vue +5 -1
- package/dist/runtime/composables/useMemiBoardSeo.ts +9 -3
- package/dist/{slugify-Cyq-Gg8X.js → slugify-Cm_pi2al.js} +44 -53
- package/dist/{slugify-Cyq-Gg8X.js.map → slugify-Cm_pi2al.js.map} +1 -1
- package/dist/storage.d.ts +2 -4
- package/dist/storage.js +46 -73
- package/dist/storage.js.map +1 -1
- package/package.json +20 -15
- package/dist/heic2any-D2QKy06h.js +0 -977
- package/dist/heic2any-D2QKy06h.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import { A as e, C as t, D as n, E as r,
|
|
2
|
-
import { computed 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, C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, j as g, k as _, l as v, m as y, n as b, o as x, p as S, r as C, s as w, u as T, v as E, w as D, x as O, y as k } from "./slugify-Cm_pi2al.js";
|
|
2
|
+
import { computed as A, onScopeDispose as j, reactive as ee, ref as M, toValue as N, watch as P } from "vue";
|
|
3
|
+
import { deleteDoc as F, deleteField as I, doc as te, documentId as L, endBefore as ne, getDoc as R, getDocs as z, increment as B, limit as V, limitToLast as re, onSnapshot as H, orderBy as U, query as W, runTransaction as ie, serverTimestamp as G, setDoc as K, startAfter as q, updateDoc as J, where as Y, writeBatch as X } from "firebase/firestore";
|
|
4
|
+
import { useCollection as ae, useCurrentUser as Z, useDocument as oe, useFirebaseApp as se, useFirebaseAuth as ce, useFirestore as Q } from "vuefire";
|
|
5
|
+
import { GoogleAuthProvider as le, OAuthProvider as ue, createUserWithEmailAndPassword as de, signInWithEmailAndPassword as fe, signInWithPopup as pe, signOut as me, updateProfile as he } from "firebase/auth";
|
|
6
|
+
import { deleteObject as ge, getStorage as _e, listAll as ve, ref as ye } from "firebase/storage";
|
|
7
|
+
import { GoogleAIBackend as be, Schema as xe, getAI as Se, getGenerativeModel as Ce } from "firebase/ai";
|
|
8
|
+
//#region \0rolldown/runtime.js
|
|
9
|
+
var we = Object.create, Te = Object.defineProperty, Ee = Object.getOwnPropertyDescriptor, De = Object.getOwnPropertyNames, Oe = Object.getPrototypeOf, ke = Object.prototype.hasOwnProperty, Ae = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), je = (e, t, n, r) => {
|
|
10
|
+
if (t && typeof t == "object" || typeof t == "function") for (var i = De(t), a = 0, o = i.length, s; a < o; a++) s = i[a], !ke.call(e, s) && s !== n && Te(e, s, {
|
|
11
|
+
get: ((e) => t[e]).bind(null, s),
|
|
12
|
+
enumerable: !(r = Ee(t, s)) || r.enumerable
|
|
13
|
+
});
|
|
14
|
+
return e;
|
|
15
|
+
}, Me = (e, t, n) => (n = e == null ? {} : we(Oe(e)), je(t || !e || !e.__esModule || !ke.call(e, "default") ? Te(n, "default", {
|
|
16
|
+
value: e,
|
|
17
|
+
enumerable: !0
|
|
18
|
+
}) : n, e));
|
|
19
|
+
//#endregion
|
|
8
20
|
//#region src/utils/boardSeo.ts
|
|
9
|
-
function
|
|
21
|
+
function Ne(e) {
|
|
10
22
|
if (typeof e != "string") return null;
|
|
11
23
|
let t = e.trim();
|
|
12
24
|
return /^https?:\/\//i.test(t) ? t : null;
|
|
13
25
|
}
|
|
14
|
-
function
|
|
15
|
-
let t =
|
|
26
|
+
function Pe(e) {
|
|
27
|
+
let t = Ne(e);
|
|
16
28
|
if (!t) return null;
|
|
17
29
|
if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
|
|
18
30
|
try {
|
|
@@ -29,42 +41,42 @@ function De(e) {
|
|
|
29
41
|
return t;
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
|
-
function
|
|
44
|
+
function Fe(e, t, n = "") {
|
|
33
45
|
let r = t.replace(/\/$/, ""), i = e.trim();
|
|
34
|
-
return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ?
|
|
46
|
+
return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? Fe(n, t) : r;
|
|
35
47
|
}
|
|
36
|
-
function
|
|
48
|
+
function Ie(e, t, n, r) {
|
|
37
49
|
let i = r?.trim().slice(0, 40), a = e.trim() || i || "사진", o = t?.trim(), s = n.trim() || "Board";
|
|
38
50
|
return o ? `${a} · ${o} | ${s}` : `${a} | ${s}`;
|
|
39
51
|
}
|
|
40
|
-
function
|
|
52
|
+
function Le(e) {
|
|
41
53
|
let t = [];
|
|
42
54
|
return e.summary?.trim() ? t.push(e.summary.trim().slice(0, 140)) : t.push("게시판 글"), e.categoryLabel?.trim() && t.push(e.categoryLabel.trim()), e.authorName?.trim() && t.push(e.authorName.trim()), t.join(" · ").slice(0, 200);
|
|
43
55
|
}
|
|
44
|
-
function
|
|
56
|
+
function Re(e, t) {
|
|
45
57
|
let n = t.trim() || "Board", r = e?.trim();
|
|
46
58
|
return r && r !== "전체" ? `${r} 게시판 | ${n}` : `게시판 | ${n}`;
|
|
47
59
|
}
|
|
48
|
-
function
|
|
60
|
+
function ze(e) {
|
|
49
61
|
if (e.description?.trim()) return e.description.trim().slice(0, 200);
|
|
50
62
|
let t = e.categoryLabel?.trim(), n = t && t !== "전체" ? `${t} 게시판 — 소식을 확인하세요.` : "게시판 — 공지와 이야기를 확인하세요.", r = (e.recentTitles || []).filter(Boolean).slice(0, 3);
|
|
51
63
|
return r.length ? `${n} ${r.map((e) => `「${e}」`).join(" ")}`.slice(0, 200) : n.slice(0, 200);
|
|
52
64
|
}
|
|
53
|
-
function
|
|
65
|
+
function Be(e) {
|
|
54
66
|
let t = (e || "/board").trim() || "/board";
|
|
55
67
|
return (t.startsWith("/") ? t : `/${t}`).replace(/\/$/, "") || "/board";
|
|
56
68
|
}
|
|
57
69
|
//#endregion
|
|
58
70
|
//#region src/composables/fetchPublicSeo.ts
|
|
59
|
-
function
|
|
71
|
+
function Ve() {
|
|
60
72
|
return {
|
|
61
73
|
db: Q(),
|
|
62
|
-
paths:
|
|
74
|
+
paths: h()
|
|
63
75
|
};
|
|
64
76
|
}
|
|
65
|
-
async function
|
|
77
|
+
async function He(e, t) {
|
|
66
78
|
try {
|
|
67
|
-
let n = await R(
|
|
79
|
+
let n = await R(g(e.db, e.paths, t));
|
|
68
80
|
if (!n.exists()) return {
|
|
69
81
|
label: t,
|
|
70
82
|
description: "",
|
|
@@ -84,7 +96,7 @@ async function Fe(e, t) {
|
|
|
84
96
|
};
|
|
85
97
|
}
|
|
86
98
|
}
|
|
87
|
-
function
|
|
99
|
+
function Ue(e) {
|
|
88
100
|
if (e && typeof e == "object" && "toDate" in e) try {
|
|
89
101
|
let t = e.toDate();
|
|
90
102
|
return t instanceof Date ? t.toISOString() : null;
|
|
@@ -93,36 +105,36 @@ function Ie(e) {
|
|
|
93
105
|
}
|
|
94
106
|
return null;
|
|
95
107
|
}
|
|
96
|
-
async function
|
|
108
|
+
async function We(e, t, n) {
|
|
97
109
|
let r = e.trim(), i = t.trim();
|
|
98
110
|
if (!r || !i) return null;
|
|
99
|
-
let a = n ??
|
|
100
|
-
a.paths =
|
|
101
|
-
let
|
|
102
|
-
if (
|
|
111
|
+
let a = n ?? Ve();
|
|
112
|
+
a.paths = _(a.paths);
|
|
113
|
+
let s = await He(a, r);
|
|
114
|
+
if (s.hidden) return null;
|
|
103
115
|
try {
|
|
104
|
-
let e = (await z(W(
|
|
116
|
+
let e = (await z(W(o(a.db, a.paths), Y("boardId", "==", r), Y("slug", "==", i), Y("listed", "==", !0), Y("isPublished", "==", !0), V(1)))).docs[0];
|
|
105
117
|
if (!e) return null;
|
|
106
118
|
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title.trim() : "";
|
|
107
119
|
return n ? {
|
|
108
120
|
id: e.id,
|
|
109
121
|
slug: typeof t.slug == "string" ? t.slug : i,
|
|
110
122
|
category: r,
|
|
111
|
-
categoryLabel:
|
|
123
|
+
categoryLabel: s.label,
|
|
112
124
|
title: n,
|
|
113
125
|
summary: typeof t.summary == "string" ? t.summary.trim().slice(0, 200) : "",
|
|
114
|
-
previewImage:
|
|
126
|
+
previewImage: Pe(t.previewImage),
|
|
115
127
|
authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
|
|
116
|
-
createdAt:
|
|
128
|
+
createdAt: Ue(t.createdAt)
|
|
117
129
|
} : null;
|
|
118
130
|
} catch (e) {
|
|
119
131
|
return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
|
-
async function
|
|
123
|
-
let n = e?.trim() || null, r = t ??
|
|
124
|
-
if (r.paths =
|
|
125
|
-
let e = await
|
|
134
|
+
async function Ge(e, t) {
|
|
135
|
+
let n = e?.trim() || null, r = t ?? Ve();
|
|
136
|
+
if (r.paths = _(r.paths), n) {
|
|
137
|
+
let e = await He(r, n);
|
|
126
138
|
if (e.hidden) return {
|
|
127
139
|
kind: "category",
|
|
128
140
|
category: n,
|
|
@@ -131,7 +143,7 @@ async function Re(e, t) {
|
|
|
131
143
|
ogImage: null,
|
|
132
144
|
recentTitles: []
|
|
133
145
|
};
|
|
134
|
-
let { recentTitles: t, ogImage: i } = await
|
|
146
|
+
let { recentTitles: t, ogImage: i } = await Ke(r, n);
|
|
135
147
|
return {
|
|
136
148
|
kind: "category",
|
|
137
149
|
category: n,
|
|
@@ -150,13 +162,13 @@ async function Re(e, t) {
|
|
|
150
162
|
recentTitles: []
|
|
151
163
|
};
|
|
152
164
|
}
|
|
153
|
-
async function
|
|
165
|
+
async function Ke(e, t) {
|
|
154
166
|
let n = [], r = null;
|
|
155
167
|
try {
|
|
156
|
-
let i = await z(W(
|
|
168
|
+
let i = await z(W(o(e.db, e.paths), Y("boardId", "==", t), Y("listed", "==", !0), Y("isPublished", "==", !0), U("createdAt", "desc"), V(12)));
|
|
157
169
|
for (let e of i.docs) {
|
|
158
170
|
let t = e.data() ?? {}, i = typeof t.title == "string" ? t.title.trim() : "";
|
|
159
|
-
i && n.length < 5 && n.push(i), r ||=
|
|
171
|
+
i && n.length < 5 && n.push(i), r ||= Pe(t.previewImage);
|
|
160
172
|
}
|
|
161
173
|
} catch (e) {
|
|
162
174
|
import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
|
|
@@ -168,31 +180,31 @@ async function ze(e, t) {
|
|
|
168
180
|
}
|
|
169
181
|
//#endregion
|
|
170
182
|
//#region src/utils/boardAccess.ts
|
|
171
|
-
function
|
|
183
|
+
function qe(e, t) {
|
|
172
184
|
return !e || !t ? !1 : (t.allowedStaffUids ?? []).includes(e);
|
|
173
185
|
}
|
|
174
|
-
function
|
|
175
|
-
return e ? t ? !0 : n ?
|
|
186
|
+
function Je(e, t, n, r) {
|
|
187
|
+
return e ? t ? !0 : n ? qe(e, r) : !1 : !1;
|
|
176
188
|
}
|
|
177
|
-
function
|
|
189
|
+
function Ye(e, t, n, r) {
|
|
178
190
|
if (!e) return !1;
|
|
179
|
-
if (r?.visibility === "hidden") return
|
|
191
|
+
if (r?.visibility === "hidden") return Je(e, t, n, r);
|
|
180
192
|
let i = r?.commentWriteRole ?? "user";
|
|
181
|
-
return i === "admin" ? t : i !== "staff" ||
|
|
193
|
+
return i === "admin" ? t : i !== "staff" || Je(e, t, n, r);
|
|
182
194
|
}
|
|
183
195
|
//#endregion
|
|
184
196
|
//#region src/composables/boardLookup.ts
|
|
185
|
-
var
|
|
186
|
-
function
|
|
187
|
-
|
|
197
|
+
var Xe;
|
|
198
|
+
function Ze(e) {
|
|
199
|
+
Xe = e;
|
|
188
200
|
}
|
|
189
|
-
function
|
|
190
|
-
return
|
|
201
|
+
function Qe(e) {
|
|
202
|
+
return Xe?.(e);
|
|
191
203
|
}
|
|
192
204
|
//#endregion
|
|
193
205
|
//#region src/utils/moderation-strike.ts
|
|
194
|
-
var
|
|
195
|
-
function
|
|
206
|
+
var $e = 10, et = 864e5;
|
|
207
|
+
function tt(e, t, n = Date.now(), r = et) {
|
|
196
208
|
let i = Math.max(0, Math.floor(Number(e) || 0));
|
|
197
209
|
if (i <= 0) return 0;
|
|
198
210
|
let a = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -200,7 +212,7 @@ function Je(e, t, n = Date.now(), r = qe) {
|
|
|
200
212
|
let o = Math.floor((n - a) / r);
|
|
201
213
|
return Math.max(0, i - o);
|
|
202
214
|
}
|
|
203
|
-
function
|
|
215
|
+
function nt(e, t, n = Date.now(), r = 10, i = et) {
|
|
204
216
|
let a = Math.max(0, Math.floor(Number(e) || 0));
|
|
205
217
|
if (a < r) return null;
|
|
206
218
|
let o = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -208,17 +220,17 @@ function Ye(e, t, n = Date.now(), r = 10, i = qe) {
|
|
|
208
220
|
let s = o + (a - (r - 1)) * i;
|
|
209
221
|
return s > n ? s : null;
|
|
210
222
|
}
|
|
211
|
-
function
|
|
212
|
-
return
|
|
223
|
+
function rt(e, t, n = Date.now(), r = 10, i = et) {
|
|
224
|
+
return nt(e, t, n, r, i) != null;
|
|
213
225
|
}
|
|
214
|
-
function
|
|
226
|
+
function it(e, t = Date.now()) {
|
|
215
227
|
let n = Math.max(0, e - t), r = Math.ceil(n / 36e5);
|
|
216
228
|
if (r <= 0) return "곧 해제";
|
|
217
229
|
if (r < 24) return `약 ${r}시간 후 해제`;
|
|
218
230
|
let i = Math.floor(r / 24), a = r % 24;
|
|
219
231
|
return a === 0 ? `약 ${i}일 후 해제` : `약 ${i}일 ${a}시간 후 해제`;
|
|
220
232
|
}
|
|
221
|
-
function
|
|
233
|
+
function at(e) {
|
|
222
234
|
if (e == null) return null;
|
|
223
235
|
if (typeof e == "number" && Number.isFinite(e)) return e;
|
|
224
236
|
if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
|
|
@@ -230,39 +242,39 @@ function Qe(e) {
|
|
|
230
242
|
}
|
|
231
243
|
//#endregion
|
|
232
244
|
//#region src/composables/useMemiBoardAuth.ts
|
|
233
|
-
var
|
|
234
|
-
function
|
|
245
|
+
var ot = "__MEMI_BOARD_AUTH_STATE__";
|
|
246
|
+
function st() {
|
|
235
247
|
let e = globalThis;
|
|
236
|
-
return e[
|
|
237
|
-
role:
|
|
238
|
-
rolePending:
|
|
239
|
-
rawBlockCount:
|
|
240
|
-
blockAtMs:
|
|
248
|
+
return e[ot] || (e[ot] = {
|
|
249
|
+
role: M("user"),
|
|
250
|
+
rolePending: M(!0),
|
|
251
|
+
rawBlockCount: M(0),
|
|
252
|
+
blockAtMs: M(null),
|
|
241
253
|
watchStarted: !1
|
|
242
|
-
}), e[
|
|
254
|
+
}), e[ot];
|
|
243
255
|
}
|
|
244
|
-
function
|
|
245
|
-
let e =
|
|
246
|
-
function
|
|
256
|
+
function ct() {
|
|
257
|
+
let e = c(), t = st(), n = Q(), r = ce(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: l } = t;
|
|
258
|
+
function u() {
|
|
247
259
|
let t = e.moderation?.blockBanThreshold;
|
|
248
260
|
return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
|
|
249
261
|
}
|
|
250
262
|
function d() {
|
|
251
263
|
let t = e.moderation?.blockBanDecayMs;
|
|
252
|
-
return typeof t == "number" && t > 0 ? t :
|
|
264
|
+
return typeof t == "number" && t > 0 ? t : et;
|
|
253
265
|
}
|
|
254
266
|
function f(e) {
|
|
255
|
-
return
|
|
267
|
+
return y(n, h(), e);
|
|
256
268
|
}
|
|
257
269
|
function p(e) {
|
|
258
270
|
if (!e) {
|
|
259
|
-
s.value = 0,
|
|
271
|
+
s.value = 0, l.value = null;
|
|
260
272
|
return;
|
|
261
273
|
}
|
|
262
|
-
s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)),
|
|
274
|
+
s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), l.value = at(e.moderationBlockAt);
|
|
263
275
|
}
|
|
264
|
-
async function
|
|
265
|
-
let t = f(e.uid), r =
|
|
276
|
+
async function g(e) {
|
|
277
|
+
let t = f(e.uid), r = m(n, h(), e.uid), i = await R(t);
|
|
266
278
|
if (!i.exists()) return await Promise.all([K(t, {
|
|
267
279
|
role: "user",
|
|
268
280
|
displayName: e.displayName,
|
|
@@ -283,7 +295,7 @@ function tt() {
|
|
|
283
295
|
"user"
|
|
284
296
|
].includes(String(a.role)) ? a.role : "user";
|
|
285
297
|
}
|
|
286
|
-
async function
|
|
298
|
+
async function _() {
|
|
287
299
|
let e = i.value?.uid;
|
|
288
300
|
if (!e) {
|
|
289
301
|
p(void 0);
|
|
@@ -292,34 +304,34 @@ function tt() {
|
|
|
292
304
|
let t = await R(f(e));
|
|
293
305
|
p(t.exists() ? t.data() : void 0);
|
|
294
306
|
}
|
|
295
|
-
t.watchStarted || (t.watchStarted = !0,
|
|
307
|
+
t.watchStarted || (t.watchStarted = !0, P(i, async (e) => {
|
|
296
308
|
if (o.value = !0, !e) {
|
|
297
309
|
a.value = "user", p(void 0), o.value = !1;
|
|
298
310
|
return;
|
|
299
311
|
}
|
|
300
312
|
try {
|
|
301
|
-
a.value = await
|
|
313
|
+
a.value = await g(e);
|
|
302
314
|
} catch (e) {
|
|
303
315
|
console.error("[memi-board] ensureUserDoc failed", e), a.value = "user", p(void 0);
|
|
304
316
|
} finally {
|
|
305
317
|
o.value = !1;
|
|
306
318
|
}
|
|
307
319
|
}, { immediate: !0 }));
|
|
308
|
-
let v =
|
|
309
|
-
let e =
|
|
310
|
-
return e <= 0 ? !1 :
|
|
311
|
-
}), T =
|
|
312
|
-
let e =
|
|
320
|
+
let v = A(() => !!i.value), b = A(() => a.value === "admin"), x = A(() => a.value === "staff"), S = A(() => b.value || x.value), C = A(() => tt(s.value, l.value, Date.now(), d())), w = A(() => {
|
|
321
|
+
let e = u();
|
|
322
|
+
return e <= 0 ? !1 : rt(s.value, l.value, Date.now(), e, d());
|
|
323
|
+
}), T = A(() => {
|
|
324
|
+
let e = u();
|
|
313
325
|
if (e <= 0) return null;
|
|
314
|
-
let t =
|
|
315
|
-
return t == null ? null :
|
|
316
|
-
}), E =
|
|
326
|
+
let t = nt(s.value, l.value, Date.now(), e, d());
|
|
327
|
+
return t == null ? null : it(t);
|
|
328
|
+
}), E = A(() => {
|
|
317
329
|
if (!w.value) return null;
|
|
318
330
|
let e = T.value;
|
|
319
331
|
return e ? `콘텐츠 경고가 누적되어 글·댓글 작성이 잠시 제한됐어요. (${e})` : "콘텐츠 경고가 누적되어 글·댓글 작성이 잠시 제한됐어요.";
|
|
320
332
|
});
|
|
321
333
|
async function D() {
|
|
322
|
-
let e = i.value?.uid, t =
|
|
334
|
+
let e = i.value?.uid, t = u();
|
|
323
335
|
if (!e || t <= 0) return {
|
|
324
336
|
effectiveCount: 0,
|
|
325
337
|
restricted: !1,
|
|
@@ -338,14 +350,14 @@ function tt() {
|
|
|
338
350
|
updatedAt: G()
|
|
339
351
|
});
|
|
340
352
|
let a = (r.exists() ? Math.max(0, Math.floor(Number(r.data().moderationBlockCount) || 0)) : 0) + 1, o = Date.now();
|
|
341
|
-
s.value = a,
|
|
342
|
-
let
|
|
353
|
+
s.value = a, l.value = o;
|
|
354
|
+
let c = tt(a, o, o, d()), p = rt(a, o, o, t, d()), m = ` (경고 ${c}/${t})`;
|
|
343
355
|
if (p) {
|
|
344
|
-
let e =
|
|
345
|
-
m = n ? ` 경고 ${
|
|
356
|
+
let e = nt(a, o, o, t, d()), n = e == null ? null : it(e, o);
|
|
357
|
+
m = n ? ` 경고 ${c}회가 되어 글·댓글 작성이 제한됩니다 (${n}).` : ` 경고 ${c}회가 되어 글·댓글 작성이 제한됩니다.`;
|
|
346
358
|
}
|
|
347
359
|
return {
|
|
348
|
-
effectiveCount:
|
|
360
|
+
effectiveCount: c,
|
|
349
361
|
restricted: p,
|
|
350
362
|
messageSuffix: m
|
|
351
363
|
};
|
|
@@ -355,62 +367,62 @@ function tt() {
|
|
|
355
367
|
return r;
|
|
356
368
|
}
|
|
357
369
|
async function k() {
|
|
358
|
-
return
|
|
359
|
-
}
|
|
360
|
-
async function A() {
|
|
361
|
-
let e = new fe("apple.com");
|
|
362
|
-
return e.addScope("email"), e.addScope("name"), he(O(), e);
|
|
370
|
+
return pe(O(), new le());
|
|
363
371
|
}
|
|
364
|
-
async function j(
|
|
365
|
-
|
|
372
|
+
async function j() {
|
|
373
|
+
let e = new ue("apple.com");
|
|
374
|
+
return e.addScope("email"), e.addScope("name"), pe(O(), e);
|
|
366
375
|
}
|
|
367
|
-
async function
|
|
368
|
-
|
|
369
|
-
return n && await _e(r.user, { displayName: n }), r;
|
|
376
|
+
async function ee(e, t) {
|
|
377
|
+
return fe(O(), e, t);
|
|
370
378
|
}
|
|
371
|
-
async function
|
|
372
|
-
await
|
|
379
|
+
async function M(e, t, n) {
|
|
380
|
+
let r = await de(O(), e, t);
|
|
381
|
+
return n && await he(r.user, { displayName: n }), r;
|
|
373
382
|
}
|
|
374
|
-
function
|
|
375
|
-
|
|
383
|
+
async function N() {
|
|
384
|
+
await me(O());
|
|
376
385
|
}
|
|
377
386
|
function F(e) {
|
|
378
|
-
return
|
|
387
|
+
return Je(i.value?.uid, b.value, x.value, e ? Qe(e) : void 0);
|
|
388
|
+
}
|
|
389
|
+
function I(e) {
|
|
390
|
+
return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
|
|
379
391
|
}
|
|
380
392
|
function te(e) {
|
|
381
393
|
return v.value && i.value?.uid === e.authorUid;
|
|
382
394
|
}
|
|
383
|
-
function
|
|
384
|
-
return v.value && (i.value?.uid === e.authorUid ||
|
|
395
|
+
function L(e) {
|
|
396
|
+
return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
|
|
385
397
|
}
|
|
386
398
|
return {
|
|
387
399
|
user: i,
|
|
388
400
|
isSignedIn: v,
|
|
389
|
-
isAdmin:
|
|
390
|
-
isStaff:
|
|
401
|
+
isAdmin: b,
|
|
402
|
+
isStaff: x,
|
|
391
403
|
canManageContent: S,
|
|
392
|
-
canManageBoard:
|
|
404
|
+
canManageBoard: F,
|
|
393
405
|
rolePending: o,
|
|
394
406
|
moderationBlockCount: C,
|
|
395
407
|
isWriteRestricted: w,
|
|
396
408
|
restrictedUntilLabel: T,
|
|
397
409
|
restrictedMessage: E,
|
|
398
410
|
signInWithGoogle: k,
|
|
399
|
-
signInWithApple:
|
|
400
|
-
signInWithEmail:
|
|
401
|
-
signUpWithEmail:
|
|
402
|
-
signOut:
|
|
403
|
-
canEdit:
|
|
404
|
-
canDelete:
|
|
411
|
+
signInWithApple: j,
|
|
412
|
+
signInWithEmail: ee,
|
|
413
|
+
signUpWithEmail: M,
|
|
414
|
+
signOut: N,
|
|
415
|
+
canEdit: I,
|
|
416
|
+
canDelete: I,
|
|
405
417
|
canEditComment: te,
|
|
406
|
-
canDeleteComment:
|
|
418
|
+
canDeleteComment: L,
|
|
407
419
|
recordContentModerationBlock: D,
|
|
408
|
-
refreshBoardUser:
|
|
420
|
+
refreshBoardUser: _
|
|
409
421
|
};
|
|
410
422
|
}
|
|
411
423
|
//#endregion
|
|
412
424
|
//#region src/utils/extractEditorImageUrls.ts
|
|
413
|
-
function
|
|
425
|
+
function lt(e) {
|
|
414
426
|
if (!e) return [];
|
|
415
427
|
let t = /* @__PURE__ */ new Set();
|
|
416
428
|
for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&", "&"));
|
|
@@ -419,7 +431,7 @@ function nt(e) {
|
|
|
419
431
|
}
|
|
420
432
|
//#endregion
|
|
421
433
|
//#region src/utils/storagePath.ts
|
|
422
|
-
function
|
|
434
|
+
function ut(e) {
|
|
423
435
|
try {
|
|
424
436
|
let t = new URL(e), n = t.hostname.toLowerCase();
|
|
425
437
|
if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
|
|
@@ -429,109 +441,109 @@ function rt(e) {
|
|
|
429
441
|
return null;
|
|
430
442
|
}
|
|
431
443
|
}
|
|
432
|
-
function
|
|
444
|
+
function dt(e) {
|
|
433
445
|
return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
|
|
434
446
|
}
|
|
435
447
|
//#endregion
|
|
436
448
|
//#region src/utils/deletePostCascade.ts
|
|
437
|
-
async function
|
|
449
|
+
async function ft(e) {
|
|
438
450
|
try {
|
|
439
|
-
let t = await
|
|
440
|
-
await Promise.all([...t.items.map((e) =>
|
|
451
|
+
let t = await ve(e);
|
|
452
|
+
await Promise.all([...t.items.map((e) => ge(e).catch(() => {})), ...t.prefixes.map((e) => ft(e))]);
|
|
441
453
|
} catch {}
|
|
442
454
|
}
|
|
443
|
-
async function
|
|
444
|
-
await
|
|
455
|
+
async function pt(e, t) {
|
|
456
|
+
await ft(ye(e, `${t}/images`)), await ft(ye(e, `${t}/attachments`));
|
|
445
457
|
}
|
|
446
|
-
async function
|
|
458
|
+
async function mt(e, t) {
|
|
447
459
|
let n = [...new Set([...t].filter(Boolean))];
|
|
448
|
-
await Promise.all(n.map((t) =>
|
|
460
|
+
await Promise.all(n.map((t) => ge(ye(e, t)).catch(() => {})));
|
|
449
461
|
}
|
|
450
|
-
var
|
|
451
|
-
async function
|
|
462
|
+
var ht = 450;
|
|
463
|
+
async function gt(e, t) {
|
|
452
464
|
let n = await z(t);
|
|
453
|
-
for (let t = 0; t < n.docs.length; t +=
|
|
465
|
+
for (let t = 0; t < n.docs.length; t += ht) {
|
|
454
466
|
let r = X(e);
|
|
455
|
-
n.docs.slice(t, t +
|
|
467
|
+
n.docs.slice(t, t + ht).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
456
468
|
}
|
|
457
469
|
}
|
|
458
|
-
async function
|
|
459
|
-
await
|
|
470
|
+
async function _t(e, t, n) {
|
|
471
|
+
await gt(e, W(O(e, t), Y("postId", "==", n)));
|
|
460
472
|
}
|
|
461
|
-
async function
|
|
462
|
-
await
|
|
473
|
+
async function vt(e, t, n) {
|
|
474
|
+
await gt(e, W(i(e, t), Y("postId", "==", n)));
|
|
463
475
|
}
|
|
464
|
-
async function
|
|
465
|
-
await
|
|
476
|
+
async function yt(e, t, n) {
|
|
477
|
+
await gt(e, W(O(e, t), Y("commentId", "==", n)));
|
|
466
478
|
}
|
|
467
|
-
async function
|
|
468
|
-
let
|
|
479
|
+
async function bt(e, n, r, i) {
|
|
480
|
+
let o = t(e, r, i), s = a(e, r, i), [c, l] = await Promise.all([R(o), R(s)]);
|
|
469
481
|
if (!c.exists()) return;
|
|
470
482
|
let u = c.data(), d = l.exists() ? String(l.data().content ?? "") : "";
|
|
471
|
-
await
|
|
483
|
+
await gt(e, W(E(e, r), Y("postId", "==", i))), await _t(e, r, i), await vt(e, r, i);
|
|
472
484
|
let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
473
485
|
for (let e of u.attachments ?? []) {
|
|
474
486
|
if (!e?.path) continue;
|
|
475
487
|
f.add(e.path);
|
|
476
|
-
let t =
|
|
488
|
+
let t = dt(e.path);
|
|
477
489
|
t && t !== i && p.add(t);
|
|
478
490
|
}
|
|
479
|
-
for (let e of
|
|
480
|
-
let t =
|
|
491
|
+
for (let e of lt(d)) {
|
|
492
|
+
let t = ut(e);
|
|
481
493
|
if (!t) continue;
|
|
482
494
|
f.add(t);
|
|
483
495
|
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
484
496
|
n !== t && n.includes("/images/thumbnails/") && f.add(n);
|
|
485
497
|
let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
486
498
|
r && !t.includes("/thumbnails/") && f.add(`${r[1]}thumbnails/${r[2]}.jpg`);
|
|
487
|
-
let a =
|
|
499
|
+
let a = dt(t);
|
|
488
500
|
a && a !== i && p.add(a);
|
|
489
501
|
}
|
|
490
|
-
await
|
|
491
|
-
for (let e of p) await
|
|
492
|
-
await
|
|
502
|
+
await F(s), await pt(n, D(r, i));
|
|
503
|
+
for (let e of p) await pt(n, D(r, e));
|
|
504
|
+
await mt(n, f), await F(o);
|
|
493
505
|
}
|
|
494
506
|
//#endregion
|
|
495
507
|
//#region src/utils/postBody.ts
|
|
496
|
-
function
|
|
508
|
+
function xt(e) {
|
|
497
509
|
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();
|
|
498
510
|
}
|
|
499
|
-
function
|
|
500
|
-
let n =
|
|
511
|
+
function St(e, t = 40) {
|
|
512
|
+
let n = xt(e);
|
|
501
513
|
if (!n) return "";
|
|
502
514
|
if (n.length <= t) return n;
|
|
503
515
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
504
516
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
505
517
|
}
|
|
506
|
-
function
|
|
507
|
-
return
|
|
518
|
+
function Ct(e) {
|
|
519
|
+
return xt(e).length > 0;
|
|
508
520
|
}
|
|
509
|
-
function
|
|
521
|
+
function wt(e, t) {
|
|
510
522
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
511
523
|
}
|
|
512
|
-
function
|
|
513
|
-
return !
|
|
524
|
+
function Tt(e) {
|
|
525
|
+
return !Ct(e);
|
|
514
526
|
}
|
|
515
527
|
//#endregion
|
|
516
528
|
//#region src/utils/postPreview.ts
|
|
517
|
-
function
|
|
529
|
+
function Et(e) {
|
|
518
530
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
519
531
|
}
|
|
520
|
-
function
|
|
532
|
+
function Dt(e) {
|
|
521
533
|
if (e) try {
|
|
522
|
-
let t = new URL(
|
|
534
|
+
let t = new URL(Et(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
523
535
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
524
536
|
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;
|
|
525
537
|
} catch {
|
|
526
538
|
return;
|
|
527
539
|
}
|
|
528
540
|
}
|
|
529
|
-
function
|
|
530
|
-
let t =
|
|
541
|
+
function Ot(e) {
|
|
542
|
+
let t = Dt(e.videoUrl);
|
|
531
543
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
532
544
|
}
|
|
533
|
-
function
|
|
534
|
-
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 ?
|
|
545
|
+
function kt(e, t = []) {
|
|
546
|
+
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 ? Et(r[1]) : a, s = o ? Dt(o) : void 0, c = n ? Et(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
535
547
|
return {
|
|
536
548
|
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),
|
|
537
549
|
...c ? { previewImage: c } : {},
|
|
@@ -540,7 +552,7 @@ function St(e, t = []) {
|
|
|
540
552
|
}
|
|
541
553
|
//#endregion
|
|
542
554
|
//#region src/composables/useMemiBoardSettings.ts
|
|
543
|
-
var
|
|
555
|
+
var At = [
|
|
544
556
|
{
|
|
545
557
|
id: "free",
|
|
546
558
|
label: "자유",
|
|
@@ -568,17 +580,17 @@ var Ct = [
|
|
|
568
580
|
visibility: "public",
|
|
569
581
|
order: 2
|
|
570
582
|
}
|
|
571
|
-
],
|
|
572
|
-
function
|
|
583
|
+
], jt = At;
|
|
584
|
+
function Mt(e) {
|
|
573
585
|
return e === "hidden" ? "hidden" : "public";
|
|
574
586
|
}
|
|
575
|
-
function
|
|
587
|
+
function Nt(e, t, n) {
|
|
576
588
|
return {
|
|
577
589
|
id: e,
|
|
578
590
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
579
591
|
order: typeof t.order == "number" ? t.order : n,
|
|
580
592
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
581
|
-
visibility:
|
|
593
|
+
visibility: Mt(t.visibility),
|
|
582
594
|
listView: t.listView ?? "default",
|
|
583
595
|
editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
|
|
584
596
|
writeRole: t.writeRole ?? "user",
|
|
@@ -586,33 +598,33 @@ function Et(e, t, n) {
|
|
|
586
598
|
allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
|
|
587
599
|
};
|
|
588
600
|
}
|
|
589
|
-
function
|
|
590
|
-
let t = () =>
|
|
591
|
-
function
|
|
601
|
+
function Pt() {
|
|
602
|
+
let t = () => h(), n = Q(), r = se(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = ct(), l = A(() => W(e(n, t()), U("order", "asc"))), { data: u, pending: d } = ae(l, { ssrKey: S(t(), "boards") }), f = A(() => u.value.map((e, t) => Nt(e.id, e, t))), p = f, m = A(() => f.value.filter((e) => e.visibility !== "hidden")), _ = m;
|
|
603
|
+
function v(e) {
|
|
592
604
|
if (e) return f.value.find((t) => t.id === e)?.label ?? e;
|
|
593
605
|
}
|
|
594
|
-
let y =
|
|
595
|
-
function
|
|
606
|
+
let y = v;
|
|
607
|
+
function x(e) {
|
|
596
608
|
if (e) return f.value.find((t) => t.id === e)?.description?.trim() || void 0;
|
|
597
609
|
}
|
|
598
|
-
let
|
|
599
|
-
function
|
|
610
|
+
let C = x;
|
|
611
|
+
function w(e) {
|
|
600
612
|
return e ? f.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
|
|
601
613
|
}
|
|
602
|
-
let T =
|
|
603
|
-
function
|
|
604
|
-
return
|
|
614
|
+
let T = w;
|
|
615
|
+
function D(e) {
|
|
616
|
+
return w(e) === "hidden";
|
|
605
617
|
}
|
|
606
|
-
let
|
|
618
|
+
let O = D;
|
|
607
619
|
function k(e) {
|
|
608
620
|
if (e) return f.value.find((t) => t.id === e);
|
|
609
621
|
}
|
|
610
|
-
|
|
611
|
-
function
|
|
612
|
-
return
|
|
622
|
+
Ze((e) => k(e));
|
|
623
|
+
function j(e) {
|
|
624
|
+
return Je(c.value?.uid, a.value, s.value, e ? k(e) : void 0);
|
|
613
625
|
}
|
|
614
|
-
let
|
|
615
|
-
async function
|
|
626
|
+
let ee = A(() => f.value.filter((e) => j(e.id)));
|
|
627
|
+
async function M(e, t, r) {
|
|
616
628
|
let i;
|
|
617
629
|
for (;;) {
|
|
618
630
|
let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
|
|
@@ -622,14 +634,14 @@ function Dt() {
|
|
|
622
634
|
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
623
635
|
}
|
|
624
636
|
}
|
|
625
|
-
async function
|
|
626
|
-
await
|
|
637
|
+
async function N(e, r) {
|
|
638
|
+
await M(o(n, t()), e, r), await M(E(n, t()), e, r);
|
|
627
639
|
}
|
|
628
640
|
function P(e, t) {
|
|
629
641
|
return {
|
|
630
642
|
label: e.label.trim(),
|
|
631
643
|
description: (e.description ?? "").trim(),
|
|
632
|
-
visibility:
|
|
644
|
+
visibility: Mt(e.visibility),
|
|
633
645
|
listView: e.listView ?? "default",
|
|
634
646
|
editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
|
|
635
647
|
writeRole: e.writeRole ?? "user",
|
|
@@ -639,61 +651,61 @@ function Dt() {
|
|
|
639
651
|
updatedAt: G()
|
|
640
652
|
};
|
|
641
653
|
}
|
|
642
|
-
async function
|
|
654
|
+
async function I(e, r = e.order ?? 0) {
|
|
643
655
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
644
656
|
let a = e.id.trim(), o = e.label.trim();
|
|
645
657
|
if (!a || !o) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
646
|
-
let s =
|
|
658
|
+
let s = Mt(e.visibility), c = P({
|
|
647
659
|
...e,
|
|
648
660
|
label: o,
|
|
649
661
|
visibility: s
|
|
650
662
|
}, r);
|
|
651
|
-
await K(
|
|
663
|
+
await K(g(n, t(), a), c, { merge: !0 }), await N(a, s !== "hidden");
|
|
652
664
|
}
|
|
653
|
-
let
|
|
654
|
-
async function
|
|
665
|
+
let te = I;
|
|
666
|
+
async function L(e) {
|
|
655
667
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
656
668
|
let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
|
|
657
669
|
e.forEach((e, i) => {
|
|
658
670
|
let s = e.id.trim(), c = e.label.trim();
|
|
659
671
|
if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
660
|
-
let l =
|
|
672
|
+
let l = Mt(e.visibility), u = P({
|
|
661
673
|
...e,
|
|
662
674
|
label: c,
|
|
663
675
|
visibility: l
|
|
664
676
|
}, i);
|
|
665
|
-
a.set(
|
|
677
|
+
a.set(g(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
|
|
666
678
|
id: s,
|
|
667
679
|
listed: l !== "hidden"
|
|
668
680
|
});
|
|
669
681
|
}), await a.commit();
|
|
670
|
-
for (let e of o) await
|
|
682
|
+
for (let e of o) await N(e.id, e.listed);
|
|
671
683
|
}
|
|
672
|
-
let
|
|
673
|
-
async function
|
|
684
|
+
let ne = L;
|
|
685
|
+
async function R(e) {
|
|
674
686
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
675
687
|
let a = e.trim();
|
|
676
688
|
if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
|
|
677
689
|
for (;;) {
|
|
678
|
-
let e = await z(W(
|
|
690
|
+
let e = await z(W(o(n, t()), Y("boardId", "==", a), V(25)));
|
|
679
691
|
if (e.empty) break;
|
|
680
|
-
for (let i of e.docs) await
|
|
692
|
+
for (let i of e.docs) await bt(n, _e(r), t(), i.id);
|
|
681
693
|
}
|
|
682
|
-
await
|
|
694
|
+
await F(g(n, t(), e));
|
|
683
695
|
}
|
|
684
|
-
let
|
|
685
|
-
async function
|
|
696
|
+
let B = R;
|
|
697
|
+
async function re() {
|
|
686
698
|
return Promise.resolve();
|
|
687
699
|
}
|
|
688
|
-
async function
|
|
700
|
+
async function H(e) {
|
|
689
701
|
let t = e.trim();
|
|
690
702
|
if (!t) throw Error("보드 이름을 입력해 주세요.");
|
|
691
703
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
692
704
|
let n = f.value.find((e) => e.label === t);
|
|
693
705
|
if (n) return n.id;
|
|
694
|
-
let r =
|
|
706
|
+
let r = b(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
|
|
695
707
|
for (; s.has(a);) a = `${r}-${o++}`;
|
|
696
|
-
return await
|
|
708
|
+
return await I({
|
|
697
709
|
id: a,
|
|
698
710
|
label: t,
|
|
699
711
|
visibility: "public",
|
|
@@ -707,55 +719,56 @@ function Dt() {
|
|
|
707
719
|
return {
|
|
708
720
|
boards: f,
|
|
709
721
|
publicBoards: m,
|
|
710
|
-
boardLabel:
|
|
711
|
-
boardDescription:
|
|
712
|
-
boardVisibility:
|
|
713
|
-
isBoardHidden:
|
|
722
|
+
boardLabel: v,
|
|
723
|
+
boardDescription: x,
|
|
724
|
+
boardVisibility: w,
|
|
725
|
+
isBoardHidden: D,
|
|
714
726
|
getBoard: k,
|
|
715
|
-
canManageBoard:
|
|
716
|
-
managedBoards:
|
|
727
|
+
canManageBoard: j,
|
|
728
|
+
managedBoards: ee,
|
|
717
729
|
categories: p,
|
|
718
|
-
publicCategories:
|
|
730
|
+
publicCategories: _,
|
|
719
731
|
categoryLabel: y,
|
|
720
|
-
categoryDescription:
|
|
732
|
+
categoryDescription: C,
|
|
721
733
|
categoryVisibility: T,
|
|
722
|
-
isCategoryHidden:
|
|
734
|
+
isCategoryHidden: O,
|
|
723
735
|
settingsPending: d,
|
|
724
|
-
ensureSettings:
|
|
725
|
-
addBoard:
|
|
726
|
-
addCategory:
|
|
727
|
-
saveBoard:
|
|
728
|
-
saveCategory:
|
|
729
|
-
saveBoards:
|
|
730
|
-
saveCategories:
|
|
731
|
-
deleteBoard:
|
|
732
|
-
deleteCategory:
|
|
733
|
-
DEFAULT_BOARDS:
|
|
734
|
-
DEFAULT_CATEGORIES:
|
|
736
|
+
ensureSettings: re,
|
|
737
|
+
addBoard: H,
|
|
738
|
+
addCategory: H,
|
|
739
|
+
saveBoard: I,
|
|
740
|
+
saveCategory: te,
|
|
741
|
+
saveBoards: L,
|
|
742
|
+
saveCategories: ne,
|
|
743
|
+
deleteBoard: R,
|
|
744
|
+
deleteCategory: B,
|
|
745
|
+
DEFAULT_BOARDS: At,
|
|
746
|
+
DEFAULT_CATEGORIES: jt
|
|
735
747
|
};
|
|
736
748
|
}
|
|
737
749
|
//#endregion
|
|
738
750
|
//#region src/composables/useMemiBoardPosts.ts
|
|
739
|
-
|
|
751
|
+
var Ft = /* @__PURE__ */ new Map();
|
|
752
|
+
function It(e) {
|
|
740
753
|
if (!e || typeof e != "object") return !1;
|
|
741
754
|
let t = "code" in e ? String(e.code) : "";
|
|
742
755
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
743
756
|
}
|
|
744
|
-
function
|
|
745
|
-
let t =
|
|
757
|
+
function Lt(e) {
|
|
758
|
+
let t = N(e)?.trim();
|
|
746
759
|
if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
|
|
747
760
|
return t;
|
|
748
761
|
}
|
|
749
|
-
function
|
|
750
|
-
let n = Q(), r =
|
|
762
|
+
function Rt(e) {
|
|
763
|
+
let n = Q(), r = se(), { isBoardHidden: i, getBoard: s } = Pt(), c = () => h(), l = () => Lt(e), u = () => o(n, c()), d = (e) => t(n, c(), e), f = (e) => a(n, c(), e);
|
|
751
764
|
function p() {
|
|
752
765
|
return !i(l());
|
|
753
766
|
}
|
|
754
767
|
function m() {
|
|
755
|
-
return
|
|
768
|
+
return s(l())?.editorType === "image";
|
|
756
769
|
}
|
|
757
|
-
function
|
|
758
|
-
let t = m(), n =
|
|
770
|
+
function g(e) {
|
|
771
|
+
let t = m(), n = Ct(e.content), r = wt(e.content, e.attachments);
|
|
759
772
|
if (t) {
|
|
760
773
|
if (!r) throw Error("사진을 올려 주세요.");
|
|
761
774
|
return;
|
|
@@ -763,8 +776,8 @@ function At(e) {
|
|
|
763
776
|
if (!n && !r) throw Error("본문에 글자를 입력하거나 이미지를 첨부해 주세요.");
|
|
764
777
|
if (!e.title?.trim()) throw Error("제목을 입력해 주세요.");
|
|
765
778
|
}
|
|
766
|
-
function
|
|
767
|
-
return
|
|
779
|
+
function _() {
|
|
780
|
+
return te(u()).id;
|
|
768
781
|
}
|
|
769
782
|
async function v(e = {}) {
|
|
770
783
|
let t = e.pageSize ?? 20, n = [Y("boardId", "==", l()), Y("isPublished", "==", !0)];
|
|
@@ -787,7 +800,7 @@ function At(e) {
|
|
|
787
800
|
content: n.exists() ? n.data().content ?? "" : ""
|
|
788
801
|
} : null;
|
|
789
802
|
}
|
|
790
|
-
async function
|
|
803
|
+
async function x(e) {
|
|
791
804
|
try {
|
|
792
805
|
let t = (await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", e.trim()), V(1)))).docs[0];
|
|
793
806
|
if (!t) return { status: "not-found" };
|
|
@@ -798,19 +811,43 @@ function At(e) {
|
|
|
798
811
|
post: e
|
|
799
812
|
} : { status: "not-found" };
|
|
800
813
|
} catch (e) {
|
|
801
|
-
return
|
|
814
|
+
return It(e) ? { status: "permission-denied" } : {
|
|
802
815
|
status: "error",
|
|
803
816
|
message: e instanceof Error ? e.message : String(e)
|
|
804
817
|
};
|
|
805
818
|
}
|
|
806
819
|
} catch (e) {
|
|
807
|
-
return
|
|
820
|
+
return It(e) ? { status: "permission-denied" } : {
|
|
808
821
|
status: "error",
|
|
809
822
|
message: e instanceof Error ? e.message : String(e)
|
|
810
823
|
};
|
|
811
824
|
}
|
|
812
825
|
}
|
|
813
|
-
async function
|
|
826
|
+
async function S(e) {
|
|
827
|
+
let t = e.trim(), n = Ft.get(`${l()}/${t}`);
|
|
828
|
+
if (n) return {
|
|
829
|
+
status: "ok",
|
|
830
|
+
id: n
|
|
831
|
+
};
|
|
832
|
+
try {
|
|
833
|
+
let e = (await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", t), V(1)))).docs[0];
|
|
834
|
+
return e ? {
|
|
835
|
+
status: "ok",
|
|
836
|
+
id: e.id
|
|
837
|
+
} : { status: "not-found" };
|
|
838
|
+
} catch (e) {
|
|
839
|
+
return It(e) ? { status: "permission-denied" } : {
|
|
840
|
+
status: "error",
|
|
841
|
+
message: e instanceof Error ? e.message : String(e)
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
async function C(e) {
|
|
846
|
+
let t = b(e.trim()) || "post", n = t, r = 1;
|
|
847
|
+
for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", n), V(1)))).empty;) r++, n = `${t}-${r}`;
|
|
848
|
+
return n;
|
|
849
|
+
}
|
|
850
|
+
async function w(e) {
|
|
814
851
|
if (!e.createdAt) return {
|
|
815
852
|
previous: null,
|
|
816
853
|
next: null
|
|
@@ -821,7 +858,7 @@ function At(e) {
|
|
|
821
858
|
Y("isPublished", "==", !0),
|
|
822
859
|
...e.listed === !1 ? [] : [Y("listed", "==", !0)],
|
|
823
860
|
U("createdAt", "desc")
|
|
824
|
-
], [n, r] = await Promise.all([z(W(u(), ...t, q(e.createdAt), V(1))), z(W(u(), ...t,
|
|
861
|
+
], [n, r] = await Promise.all([z(W(u(), ...t, q(e.createdAt), V(1))), z(W(u(), ...t, ne(e.createdAt), re(1)))]), i = (e) => {
|
|
825
862
|
let t = e.docs[0];
|
|
826
863
|
return t ? {
|
|
827
864
|
id: t.id,
|
|
@@ -839,29 +876,22 @@ function At(e) {
|
|
|
839
876
|
};
|
|
840
877
|
}
|
|
841
878
|
}
|
|
842
|
-
async function
|
|
843
|
-
|
|
844
|
-
let t = e.postId ||
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
let t = S(i) || "post";
|
|
849
|
-
r = t;
|
|
850
|
-
let n = 1;
|
|
851
|
-
for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
|
|
852
|
-
}
|
|
853
|
-
let a = St(e.content, e.attachments), o = p();
|
|
854
|
-
return await K(d(t), {
|
|
879
|
+
async function T(e) {
|
|
880
|
+
g(e);
|
|
881
|
+
let t = e.postId || _(), r = m(), i, a;
|
|
882
|
+
r ? (i = t, a = St(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await C(a));
|
|
883
|
+
let o = kt(e.content, e.attachments), s = p(), c = X(n);
|
|
884
|
+
return c.set(d(t), {
|
|
855
885
|
boardId: l(),
|
|
856
|
-
slug:
|
|
857
|
-
title:
|
|
858
|
-
...
|
|
886
|
+
slug: i,
|
|
887
|
+
title: a,
|
|
888
|
+
...o,
|
|
859
889
|
tags: e.tags ?? [],
|
|
860
890
|
attachments: e.attachments ?? [],
|
|
861
891
|
commentCount: 0,
|
|
862
892
|
likeCount: 0,
|
|
863
893
|
viewCount: 0,
|
|
864
|
-
listed:
|
|
894
|
+
listed: s,
|
|
865
895
|
isPublished: !1,
|
|
866
896
|
authorUid: e.authorUid,
|
|
867
897
|
authorName: e.authorName,
|
|
@@ -870,56 +900,59 @@ function At(e) {
|
|
|
870
900
|
moderationModel: e.moderationModel ?? null,
|
|
871
901
|
createdAt: G(),
|
|
872
902
|
updatedAt: G()
|
|
873
|
-
}),
|
|
903
|
+
}), c.set(f(t), { content: e.content }), await c.commit(), Ft.set(`${l()}/${i}`, t), i;
|
|
874
904
|
}
|
|
875
|
-
async function
|
|
876
|
-
|
|
877
|
-
let n = m() ?
|
|
905
|
+
async function E(e, t) {
|
|
906
|
+
g(t);
|
|
907
|
+
let n = m() ? St(t.content) : t.title.trim(), r = kt(t.content, t.attachments), i = p();
|
|
878
908
|
await Promise.all([J(d(e), {
|
|
879
909
|
title: n,
|
|
880
910
|
...r,
|
|
881
|
-
previewImage: r.previewImage ? r.previewImage :
|
|
882
|
-
videoUrl: r.videoUrl ? r.videoUrl :
|
|
911
|
+
previewImage: r.previewImage ? r.previewImage : I(),
|
|
912
|
+
videoUrl: r.videoUrl ? r.videoUrl : I(),
|
|
883
913
|
tags: t.tags ?? [],
|
|
884
914
|
listed: i,
|
|
885
915
|
attachments: t.attachments ?? [],
|
|
886
916
|
updatedAt: G()
|
|
887
917
|
}), K(f(e), { content: t.content }, { merge: !0 })]);
|
|
888
918
|
}
|
|
889
|
-
async function
|
|
919
|
+
async function D(e) {
|
|
890
920
|
await J(d(e), {
|
|
891
921
|
isPublished: !0,
|
|
892
922
|
publishedAt: G(),
|
|
893
923
|
updatedAt: G()
|
|
894
924
|
});
|
|
895
925
|
}
|
|
896
|
-
async function
|
|
897
|
-
await
|
|
926
|
+
async function O(e) {
|
|
927
|
+
await bt(n, _e(r), c(), e);
|
|
928
|
+
for (let [t, n] of Ft) n === e && Ft.delete(t);
|
|
898
929
|
}
|
|
899
930
|
return {
|
|
900
|
-
createPostId:
|
|
931
|
+
createPostId: _,
|
|
901
932
|
getPosts: v,
|
|
902
933
|
getPost: y,
|
|
903
|
-
getPostBySlug:
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
934
|
+
getPostBySlug: x,
|
|
935
|
+
resolvePostIdBySlug: S,
|
|
936
|
+
resolveUniqueSlug: C,
|
|
937
|
+
getAdjacentPosts: w,
|
|
938
|
+
createPost: T,
|
|
939
|
+
updatePost: E,
|
|
940
|
+
publishPost: D,
|
|
941
|
+
deletePost: O
|
|
909
942
|
};
|
|
910
943
|
}
|
|
911
|
-
var
|
|
912
|
-
function
|
|
913
|
-
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } =
|
|
944
|
+
var zt = 10;
|
|
945
|
+
function Bt(e, t = {}) {
|
|
946
|
+
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = ct(), s = () => h(), c = t.pageSize ?? zt, l = A(() => i.value?.uid ?? ""), u = A(() => Lt(e)), d = () => o(n, s()), f = A(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = M([]), m = M([]), g = M([]), _ = M(!0), v = M(!0), y = M(!1), b = M(""), x = null, S = null, C, w;
|
|
914
947
|
function T(e) {
|
|
915
948
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
916
949
|
}
|
|
917
|
-
let E =
|
|
950
|
+
let E = A(() => {
|
|
918
951
|
let e = /* @__PURE__ */ new Map();
|
|
919
952
|
for (let t of [
|
|
920
953
|
...m.value,
|
|
921
954
|
...p.value,
|
|
922
|
-
...
|
|
955
|
+
...g.value
|
|
923
956
|
]) t.id && e.set(t.id, t);
|
|
924
957
|
return [...e.values()].sort((e, t) => T(t) - T(e) || String(t.id).localeCompare(String(e.id)));
|
|
925
958
|
});
|
|
@@ -928,24 +961,24 @@ function Mt(e, t = {}) {
|
|
|
928
961
|
return f.value && e.push(Y("listed", "==", !0)), e;
|
|
929
962
|
}
|
|
930
963
|
function O() {
|
|
931
|
-
C?.(), C = H(W(d(), ...D(), U("createdAt", "desc"), U(L(), "desc"), V(
|
|
964
|
+
C?.(), C = H(W(d(), ...D(), U("createdAt", "desc"), U(L(), "desc"), V(c)), (e) => {
|
|
932
965
|
p.value = e.docs.map((e) => ({
|
|
933
966
|
id: e.id,
|
|
934
967
|
...e.data()
|
|
935
|
-
})), x = e.docs.at(-1) ?? null, S || (v.value = e.docs.length >=
|
|
968
|
+
})), x = e.docs.at(-1) ?? null, S || (v.value = e.docs.length >= c), _.value = !1;
|
|
936
969
|
}, (e) => {
|
|
937
|
-
console.error("[memi-board:postList] onSnapshot failed", e), b.value = e.message || String(e),
|
|
970
|
+
console.error("[memi-board:postList] onSnapshot failed", e), b.value = e.message || String(e), _.value = !1;
|
|
938
971
|
});
|
|
939
972
|
}
|
|
940
973
|
function k() {
|
|
941
974
|
w?.();
|
|
942
975
|
let e = l.value;
|
|
943
976
|
if (!e) {
|
|
944
|
-
|
|
977
|
+
g.value = [];
|
|
945
978
|
return;
|
|
946
979
|
}
|
|
947
980
|
w = H(W(d(), Y("boardId", "==", u.value), Y("authorUid", "==", e), Y("isPublished", "==", !1), U("createdAt", "desc"), V(20)), (e) => {
|
|
948
|
-
|
|
981
|
+
g.value = e.docs.map((e) => ({
|
|
949
982
|
id: e.id,
|
|
950
983
|
...e.data()
|
|
951
984
|
}));
|
|
@@ -953,8 +986,8 @@ function Mt(e, t = {}) {
|
|
|
953
986
|
console.error("[memi-board:postList] own draft onSnapshot failed", e);
|
|
954
987
|
});
|
|
955
988
|
}
|
|
956
|
-
async function
|
|
957
|
-
if (!(y.value || !v.value ||
|
|
989
|
+
async function ee() {
|
|
990
|
+
if (!(y.value || !v.value || _.value)) {
|
|
958
991
|
y.value = !0, b.value = "";
|
|
959
992
|
try {
|
|
960
993
|
let e = S ?? x, t = [
|
|
@@ -962,12 +995,12 @@ function Mt(e, t = {}) {
|
|
|
962
995
|
U("createdAt", "desc"),
|
|
963
996
|
U(L(), "desc"),
|
|
964
997
|
...e ? [q(e)] : [],
|
|
965
|
-
V(
|
|
966
|
-
], n = await z(W(d(), ...t)), r = n.docs.slice(0,
|
|
998
|
+
V(c + 1)
|
|
999
|
+
], n = await z(W(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
|
|
967
1000
|
id: e.id,
|
|
968
1001
|
...e.data()
|
|
969
1002
|
})), a = new Set([...m.value, ...p.value].map((e) => e.id));
|
|
970
|
-
m.value.push(...i.filter((e) => !a.has(e.id))), S = r.at(-1) ?? S, v.value = n.docs.length >
|
|
1003
|
+
m.value.push(...i.filter((e) => !a.has(e.id))), S = r.at(-1) ?? S, v.value = n.docs.length > c;
|
|
971
1004
|
} catch (e) {
|
|
972
1005
|
b.value = e.message || String(e), console.error("[memi-board:postList] loadMore failed", e);
|
|
973
1006
|
} finally {
|
|
@@ -975,40 +1008,40 @@ function Mt(e, t = {}) {
|
|
|
975
1008
|
}
|
|
976
1009
|
}
|
|
977
1010
|
}
|
|
978
|
-
function
|
|
979
|
-
C?.(), p.value = [], m.value = [], x = null, S = null, v.value = !0, b.value = "",
|
|
1011
|
+
function N() {
|
|
1012
|
+
C?.(), p.value = [], m.value = [], x = null, S = null, v.value = !0, b.value = "", _.value = !0, O(), k();
|
|
980
1013
|
}
|
|
981
|
-
return
|
|
1014
|
+
return P([
|
|
982
1015
|
u,
|
|
983
1016
|
f,
|
|
984
1017
|
l
|
|
985
|
-
],
|
|
1018
|
+
], N, { immediate: !0 }), j(() => {
|
|
986
1019
|
C?.(), w?.();
|
|
987
1020
|
}), {
|
|
988
1021
|
posts: E,
|
|
989
|
-
postsPending:
|
|
1022
|
+
postsPending: _,
|
|
990
1023
|
hasMore: v,
|
|
991
1024
|
loadingMore: y,
|
|
992
1025
|
loadError: b,
|
|
993
|
-
loadMore:
|
|
1026
|
+
loadMore: ee
|
|
994
1027
|
};
|
|
995
1028
|
}
|
|
996
|
-
function
|
|
997
|
-
let r = Q(), i = () =>
|
|
1029
|
+
function Vt(e, n) {
|
|
1030
|
+
let r = Q(), i = () => h(), o = A(() => Lt(e)), s = A(() => typeof n == "string" ? n : n.value), c = A(() => t(r, i(), s.value)), l = A(() => a(r, i(), s.value)), { data: u, pending: d } = oe(c), { data: f, pending: p } = oe(l);
|
|
998
1031
|
return {
|
|
999
|
-
post:
|
|
1032
|
+
post: A(() => !u.value || u.value.boardId !== o.value ? null : {
|
|
1000
1033
|
...u.value,
|
|
1001
|
-
id:
|
|
1034
|
+
id: s.value,
|
|
1002
1035
|
content: f.value?.content ?? ""
|
|
1003
1036
|
}),
|
|
1004
|
-
pending:
|
|
1037
|
+
pending: A(() => d.value || p.value)
|
|
1005
1038
|
};
|
|
1006
1039
|
}
|
|
1007
1040
|
//#endregion
|
|
1008
1041
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
1009
|
-
var
|
|
1010
|
-
function
|
|
1011
|
-
let n = Q(), r = () =>
|
|
1042
|
+
var Ht = 10;
|
|
1043
|
+
function Ut(e, t = {}) {
|
|
1044
|
+
let n = Q(), r = () => h(), i = () => o(n, r()), a = t.pageSize ?? Ht, s = A(() => N(e)?.trim() || ""), c = M([]), l = M([]), u = M(!0), d = M(!0), f = M(!1), p = M(""), m = null, g = null, _;
|
|
1012
1045
|
function v(e) {
|
|
1013
1046
|
return {
|
|
1014
1047
|
id: e.id,
|
|
@@ -1018,41 +1051,41 @@ function Ft(e, t = {}) {
|
|
|
1018
1051
|
function y(e) {
|
|
1019
1052
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1020
1053
|
}
|
|
1021
|
-
let b =
|
|
1054
|
+
let b = A(() => {
|
|
1022
1055
|
let e = /* @__PURE__ */ new Map();
|
|
1023
|
-
for (let t of [...l.value, ...
|
|
1056
|
+
for (let t of [...l.value, ...c.value]) t.id && e.set(t.id, t);
|
|
1024
1057
|
return [...e.values()].sort((e, t) => y(t) - y(e) || String(t.id).localeCompare(String(e.id)));
|
|
1025
1058
|
});
|
|
1026
1059
|
function x() {
|
|
1027
1060
|
return [
|
|
1028
|
-
Y("authorUid", "==",
|
|
1061
|
+
Y("authorUid", "==", s.value),
|
|
1029
1062
|
Y("isPublished", "==", !0),
|
|
1030
1063
|
Y("listed", "==", !0)
|
|
1031
1064
|
];
|
|
1032
1065
|
}
|
|
1033
1066
|
function S() {
|
|
1034
|
-
if (
|
|
1067
|
+
if (_?.(), !s.value) {
|
|
1035
1068
|
u.value = !1;
|
|
1036
1069
|
return;
|
|
1037
1070
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1071
|
+
_ = H(W(i(), ...x(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
|
|
1072
|
+
c.value = e.docs.map(v), m = e.docs.at(-1) ?? null, g || (d.value = e.docs.length >= a), u.value = !1;
|
|
1040
1073
|
}, (e) => {
|
|
1041
1074
|
console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
|
|
1042
1075
|
});
|
|
1043
1076
|
}
|
|
1044
1077
|
async function C() {
|
|
1045
|
-
if (!
|
|
1078
|
+
if (!f.value && d.value && !u.value && s.value) {
|
|
1046
1079
|
f.value = !0, p.value = "";
|
|
1047
1080
|
try {
|
|
1048
|
-
let e =
|
|
1081
|
+
let e = g ?? m, t = [
|
|
1049
1082
|
...x(),
|
|
1050
1083
|
U("createdAt", "desc"),
|
|
1051
1084
|
U(L(), "desc"),
|
|
1052
1085
|
...e ? [q(e)] : [],
|
|
1053
1086
|
V(a + 1)
|
|
1054
|
-
], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(v),
|
|
1055
|
-
l.value.push(...o.filter((e) => !
|
|
1087
|
+
], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(v), s = new Set([...l.value, ...c.value].map((e) => e.id));
|
|
1088
|
+
l.value.push(...o.filter((e) => !s.has(e.id))), g = r.at(-1) ?? g, d.value = n.docs.length > a;
|
|
1056
1089
|
} catch (e) {
|
|
1057
1090
|
p.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
|
|
1058
1091
|
} finally {
|
|
@@ -1061,9 +1094,9 @@ function Ft(e, t = {}) {
|
|
|
1061
1094
|
}
|
|
1062
1095
|
}
|
|
1063
1096
|
function w() {
|
|
1064
|
-
|
|
1097
|
+
_?.(), c.value = [], l.value = [], m = null, g = null, d.value = !0, p.value = "", u.value = !0, S();
|
|
1065
1098
|
}
|
|
1066
|
-
return
|
|
1099
|
+
return P(s, w, { immediate: !0 }), j(() => _?.()), {
|
|
1067
1100
|
posts: b,
|
|
1068
1101
|
postsPending: u,
|
|
1069
1102
|
hasMore: d,
|
|
@@ -1074,10 +1107,10 @@ function Ft(e, t = {}) {
|
|
|
1074
1107
|
}
|
|
1075
1108
|
//#endregion
|
|
1076
1109
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1077
|
-
var
|
|
1078
|
-
function
|
|
1079
|
-
let n = Q(), r = () =>
|
|
1080
|
-
function
|
|
1110
|
+
var Wt = 10;
|
|
1111
|
+
function Gt(e, t = {}) {
|
|
1112
|
+
let n = Q(), r = () => h(), i = () => E(n, r()), a = t.pageSize ?? Wt, o = A(() => N(e)?.trim() || ""), s = M([]), c = M([]), l = M(!0), u = M(!0), d = M(!1), f = M(""), p = null, m = null, g;
|
|
1113
|
+
function _(e) {
|
|
1081
1114
|
return {
|
|
1082
1115
|
id: e.id,
|
|
1083
1116
|
...e.data()
|
|
@@ -1086,7 +1119,7 @@ function Lt(e, t = {}) {
|
|
|
1086
1119
|
function v(e) {
|
|
1087
1120
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1088
1121
|
}
|
|
1089
|
-
let y =
|
|
1122
|
+
let y = A(() => {
|
|
1090
1123
|
let e = /* @__PURE__ */ new Map();
|
|
1091
1124
|
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1092
1125
|
return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
|
|
@@ -1095,18 +1128,18 @@ function Lt(e, t = {}) {
|
|
|
1095
1128
|
return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
|
|
1096
1129
|
}
|
|
1097
1130
|
function x() {
|
|
1098
|
-
if (
|
|
1131
|
+
if (g?.(), !o.value) {
|
|
1099
1132
|
l.value = !1;
|
|
1100
1133
|
return;
|
|
1101
1134
|
}
|
|
1102
|
-
|
|
1103
|
-
s.value = e.docs.map(
|
|
1135
|
+
g = H(W(i(), ...b(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
|
|
1136
|
+
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1104
1137
|
}, (e) => {
|
|
1105
1138
|
console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
1106
1139
|
});
|
|
1107
1140
|
}
|
|
1108
1141
|
async function S() {
|
|
1109
|
-
if (!
|
|
1142
|
+
if (!d.value && u.value && !l.value && o.value) {
|
|
1110
1143
|
d.value = !0, f.value = "";
|
|
1111
1144
|
try {
|
|
1112
1145
|
let e = m ?? p, t = [
|
|
@@ -1115,7 +1148,7 @@ function Lt(e, t = {}) {
|
|
|
1115
1148
|
U(L(), "desc"),
|
|
1116
1149
|
...e ? [q(e)] : [],
|
|
1117
1150
|
V(a + 1)
|
|
1118
|
-
], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(
|
|
1151
|
+
], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1119
1152
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1120
1153
|
} catch (e) {
|
|
1121
1154
|
f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
@@ -1125,9 +1158,9 @@ function Lt(e, t = {}) {
|
|
|
1125
1158
|
}
|
|
1126
1159
|
}
|
|
1127
1160
|
function C() {
|
|
1128
|
-
|
|
1161
|
+
g?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, x();
|
|
1129
1162
|
}
|
|
1130
|
-
return
|
|
1163
|
+
return P(o, C, { immediate: !0 }), j(() => g?.()), {
|
|
1131
1164
|
comments: y,
|
|
1132
1165
|
commentsPending: l,
|
|
1133
1166
|
hasMore: u,
|
|
@@ -1138,13 +1171,13 @@ function Lt(e, t = {}) {
|
|
|
1138
1171
|
}
|
|
1139
1172
|
//#endregion
|
|
1140
1173
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1141
|
-
var
|
|
1142
|
-
function
|
|
1143
|
-
let n = Q(), r = () =>
|
|
1144
|
-
let e = Math.floor(Number(
|
|
1145
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1146
|
-
}), o =
|
|
1147
|
-
function
|
|
1174
|
+
var Kt = 10;
|
|
1175
|
+
function qt(e = "latest", t = {}) {
|
|
1176
|
+
let n = Q(), r = () => h(), i = () => E(n, r()), a = A(() => {
|
|
1177
|
+
let e = Math.floor(Number(N(t.pageSize)));
|
|
1178
|
+
return Number.isFinite(e) && e > 0 ? e : Kt;
|
|
1179
|
+
}), o = A(() => N(e) === "likes" ? "likes" : "latest"), s = M([]), c = M([]), l = M(!0), u = M(!0), d = M(!1), f = M(""), p = null, m = null, g;
|
|
1180
|
+
function _(e) {
|
|
1148
1181
|
return {
|
|
1149
1182
|
id: e.id,
|
|
1150
1183
|
...e.data()
|
|
@@ -1160,7 +1193,7 @@ function zt(e = "latest", t = {}) {
|
|
|
1160
1193
|
}
|
|
1161
1194
|
return v(t) - v(e) || String(t.id).localeCompare(String(e.id));
|
|
1162
1195
|
}
|
|
1163
|
-
let b =
|
|
1196
|
+
let b = A(() => {
|
|
1164
1197
|
let e = /* @__PURE__ */ new Map();
|
|
1165
1198
|
for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
|
|
1166
1199
|
return [...e.values()].sort(y);
|
|
@@ -1178,8 +1211,8 @@ function zt(e = "latest", t = {}) {
|
|
|
1178
1211
|
];
|
|
1179
1212
|
}
|
|
1180
1213
|
function S() {
|
|
1181
|
-
|
|
1182
|
-
s.value = e.docs.map(
|
|
1214
|
+
g?.(), g = H(W(i(), ...x(), V(a.value)), (e) => {
|
|
1215
|
+
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
|
|
1183
1216
|
}, (e) => {
|
|
1184
1217
|
console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
1185
1218
|
});
|
|
@@ -1188,7 +1221,7 @@ function zt(e = "latest", t = {}) {
|
|
|
1188
1221
|
if (!(d.value || !u.value || l.value)) {
|
|
1189
1222
|
d.value = !0, f.value = "";
|
|
1190
1223
|
try {
|
|
1191
|
-
let e = m ?? p, t = await z(W(i(), ...x(), ...e ? [q(e)] : [], V(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(
|
|
1224
|
+
let e = m ?? p, t = await z(W(i(), ...x(), ...e ? [q(e)] : [], V(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1192
1225
|
c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
|
|
1193
1226
|
} catch (e) {
|
|
1194
1227
|
f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
@@ -1198,9 +1231,9 @@ function zt(e = "latest", t = {}) {
|
|
|
1198
1231
|
}
|
|
1199
1232
|
}
|
|
1200
1233
|
function w() {
|
|
1201
|
-
|
|
1234
|
+
g?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, S();
|
|
1202
1235
|
}
|
|
1203
|
-
return
|
|
1236
|
+
return P([o, a], w, { immediate: !0 }), j(() => g?.()), {
|
|
1204
1237
|
comments: b,
|
|
1205
1238
|
commentsPending: l,
|
|
1206
1239
|
hasMore: u,
|
|
@@ -1212,9 +1245,9 @@ function zt(e = "latest", t = {}) {
|
|
|
1212
1245
|
}
|
|
1213
1246
|
//#endregion
|
|
1214
1247
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1215
|
-
var
|
|
1248
|
+
var Jt = 100, Yt = /* @__PURE__ */ new Map();
|
|
1216
1249
|
function $(e) {
|
|
1217
|
-
let t =
|
|
1250
|
+
let t = Yt.get(e);
|
|
1218
1251
|
return t || (t = ee({
|
|
1219
1252
|
text: "",
|
|
1220
1253
|
sentiment: null,
|
|
@@ -1222,19 +1255,19 @@ function $(e) {
|
|
|
1222
1255
|
pending: !1,
|
|
1223
1256
|
saving: !1,
|
|
1224
1257
|
error: ""
|
|
1225
|
-
}),
|
|
1258
|
+
}), Yt.set(e, t)), t;
|
|
1226
1259
|
}
|
|
1227
|
-
function
|
|
1228
|
-
let t = Q(), n = () =>
|
|
1260
|
+
function Xt(e) {
|
|
1261
|
+
let t = Q(), n = () => h(), { user: r } = ct(), i = A(() => N(e)), a = A(() => `${r.value?.uid ?? ""}:${i.value}`), o = A(() => {
|
|
1229
1262
|
let e = r.value?.uid, a = i.value;
|
|
1230
|
-
return !e || !a ? null :
|
|
1231
|
-
}), s =
|
|
1263
|
+
return !e || !a ? null : f(t, n(), e, a);
|
|
1264
|
+
}), s = A({
|
|
1232
1265
|
get: () => $(a.value).text,
|
|
1233
1266
|
set: (e) => {
|
|
1234
1267
|
$(a.value).text = e;
|
|
1235
1268
|
}
|
|
1236
|
-
}), c =
|
|
1237
|
-
async function
|
|
1269
|
+
}), c = A(() => $(a.value).sentiment), l = A(() => $(a.value).loaded), u = A(() => $(a.value).pending), d = A(() => $(a.value).saving), p = A(() => $(a.value).error);
|
|
1270
|
+
async function m() {
|
|
1238
1271
|
let e = o.value;
|
|
1239
1272
|
if (!e) return;
|
|
1240
1273
|
let t = $(a.value);
|
|
@@ -1248,7 +1281,7 @@ function Ht(e) {
|
|
|
1248
1281
|
t.pending = !1;
|
|
1249
1282
|
}
|
|
1250
1283
|
}
|
|
1251
|
-
async function
|
|
1284
|
+
async function g(e, t) {
|
|
1252
1285
|
let n = o.value;
|
|
1253
1286
|
if (!n) return;
|
|
1254
1287
|
let r = e.trim();
|
|
@@ -1267,13 +1300,13 @@ function Ht(e) {
|
|
|
1267
1300
|
i.saving = !1;
|
|
1268
1301
|
}
|
|
1269
1302
|
}
|
|
1270
|
-
async function
|
|
1303
|
+
async function _() {
|
|
1271
1304
|
let e = o.value;
|
|
1272
1305
|
if (!e) return;
|
|
1273
1306
|
let t = $(a.value);
|
|
1274
1307
|
t.saving = !0, t.error = "";
|
|
1275
1308
|
try {
|
|
1276
|
-
await
|
|
1309
|
+
await F(e), t.text = "", t.sentiment = null, t.loaded = !0;
|
|
1277
1310
|
} catch (e) {
|
|
1278
1311
|
throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
|
|
1279
1312
|
} finally {
|
|
@@ -1286,40 +1319,40 @@ function Ht(e) {
|
|
|
1286
1319
|
loaded: l,
|
|
1287
1320
|
pending: u,
|
|
1288
1321
|
saving: d,
|
|
1289
|
-
error:
|
|
1290
|
-
load:
|
|
1291
|
-
save:
|
|
1292
|
-
remove:
|
|
1322
|
+
error: p,
|
|
1323
|
+
load: m,
|
|
1324
|
+
save: g,
|
|
1325
|
+
remove: _
|
|
1293
1326
|
};
|
|
1294
1327
|
}
|
|
1295
1328
|
//#endregion
|
|
1296
1329
|
//#region src/composables/useMemiBoardComments.ts
|
|
1297
|
-
var
|
|
1298
|
-
function
|
|
1330
|
+
var Zt = 10, Qt = 5, $t = 1e3;
|
|
1331
|
+
function en(e) {
|
|
1299
1332
|
let t = e.trim();
|
|
1300
1333
|
if (!t) throw Error("댓글 내용을 입력해 주세요.");
|
|
1301
|
-
if (t.length > 1e3) throw Error(`댓글은 ${
|
|
1334
|
+
if (t.length > 1e3) throw Error(`댓글은 ${$t.toLocaleString()}자까지 작성할 수 있습니다.`);
|
|
1302
1335
|
return t;
|
|
1303
1336
|
}
|
|
1304
|
-
function
|
|
1305
|
-
let i = Q(), a = () =>
|
|
1337
|
+
function tn(e, n, r = {}) {
|
|
1338
|
+
let i = Q(), a = () => h(), { isBoardHidden: o } = Pt(), c = () => E(i, a()), l = A(() => N(e)), u = A(() => N(n)), d = () => !o(l.value), f = M([]), p = M([]), m = M(r.subscribe !== !1), g = M([]), _ = M(!1), v = M(r.subscribe !== !1), y = M(r.subscribe !== !1), b = null, x, S;
|
|
1306
1339
|
function C(e) {
|
|
1307
1340
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1308
1341
|
}
|
|
1309
|
-
let w =
|
|
1342
|
+
let w = A(() => {
|
|
1310
1343
|
let e = /* @__PURE__ */ new Map();
|
|
1311
1344
|
for (let t of [
|
|
1312
1345
|
...f.value,
|
|
1313
1346
|
...p.value,
|
|
1314
|
-
...
|
|
1347
|
+
...g.value
|
|
1315
1348
|
]) t.id && e.set(t.id, t);
|
|
1316
1349
|
return [...e.values()].sort((e, t) => C(e) - C(t) || String(e.id).localeCompare(String(t.id)));
|
|
1317
1350
|
});
|
|
1318
|
-
|
|
1319
|
-
if (
|
|
1351
|
+
P([u, l], async ([e]) => {
|
|
1352
|
+
if (g.value = [], f.value = [], p.value = [], b = null, clearTimeout(x), x = void 0, S?.(), S = void 0, v.value = r.subscribe !== !1, r.subscribe !== !1) {
|
|
1320
1353
|
m.value = !0, y.value = !0;
|
|
1321
1354
|
try {
|
|
1322
|
-
let t = z(W(
|
|
1355
|
+
let t = z(W(c(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
|
|
1323
1356
|
if (await Promise.all([t.then((t) => {
|
|
1324
1357
|
if (u.value !== e) return;
|
|
1325
1358
|
let n = t.docs.map((e) => ({
|
|
@@ -1327,14 +1360,14 @@ function qt(e, n, r = {}) {
|
|
|
1327
1360
|
...e.data()
|
|
1328
1361
|
}));
|
|
1329
1362
|
p.value = n.filter((e) => e.parentId === void 0);
|
|
1330
|
-
}),
|
|
1363
|
+
}), D()]), u.value !== e) return;
|
|
1331
1364
|
} finally {
|
|
1332
1365
|
u.value === e && (m.value = !1, y.value = !1);
|
|
1333
1366
|
}
|
|
1334
1367
|
}
|
|
1335
1368
|
}, { immediate: !0 });
|
|
1336
1369
|
function T() {
|
|
1337
|
-
S?.(), S = H(W(
|
|
1370
|
+
S?.(), S = H(W(c(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
|
|
1338
1371
|
let t = new Map(f.value.map((e) => [e.id, e]));
|
|
1339
1372
|
for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
|
|
1340
1373
|
id: n.doc.id,
|
|
@@ -1345,9 +1378,9 @@ function qt(e, n, r = {}) {
|
|
|
1345
1378
|
console.error("[memi-board:comments] onSnapshot failed", e);
|
|
1346
1379
|
});
|
|
1347
1380
|
}
|
|
1348
|
-
async function
|
|
1349
|
-
if (!
|
|
1350
|
-
|
|
1381
|
+
async function D() {
|
|
1382
|
+
if (!_.value && v.value) {
|
|
1383
|
+
_.value = !0;
|
|
1351
1384
|
try {
|
|
1352
1385
|
let e = [
|
|
1353
1386
|
Y("postId", "==", u.value),
|
|
@@ -1356,26 +1389,26 @@ function qt(e, n, r = {}) {
|
|
|
1356
1389
|
U(L(), "asc"),
|
|
1357
1390
|
...b ? [q(b)] : [],
|
|
1358
1391
|
V(11)
|
|
1359
|
-
], t = await z(W(
|
|
1392
|
+
], t = await z(W(c(), ...e)), n = t.docs.slice(0, Zt), r = n.map((e) => ({
|
|
1360
1393
|
id: e.id,
|
|
1361
1394
|
...e.data()
|
|
1362
|
-
})), i = new Set(
|
|
1363
|
-
|
|
1395
|
+
})), i = new Set(g.value.map((e) => e.id));
|
|
1396
|
+
g.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Zt && (v.value = !1, x = setTimeout(() => {
|
|
1364
1397
|
x = void 0, T();
|
|
1365
1398
|
}, 1e3));
|
|
1366
1399
|
} finally {
|
|
1367
|
-
|
|
1400
|
+
_.value = !1;
|
|
1368
1401
|
}
|
|
1369
1402
|
}
|
|
1370
1403
|
}
|
|
1371
|
-
|
|
1404
|
+
j(() => {
|
|
1372
1405
|
clearTimeout(x), S?.();
|
|
1373
1406
|
});
|
|
1374
|
-
async function
|
|
1375
|
-
let n =
|
|
1407
|
+
async function O(e) {
|
|
1408
|
+
let n = en(e.body), r = X(i), o = te(c());
|
|
1376
1409
|
r.set(o, {
|
|
1377
1410
|
postId: u.value,
|
|
1378
|
-
boardId:
|
|
1411
|
+
boardId: l.value,
|
|
1379
1412
|
listed: d(),
|
|
1380
1413
|
body: n,
|
|
1381
1414
|
authorUid: e.authorUid,
|
|
@@ -1395,14 +1428,14 @@ function qt(e, n, r = {}) {
|
|
|
1395
1428
|
}), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1396
1429
|
}
|
|
1397
1430
|
async function k(e) {
|
|
1398
|
-
let n =
|
|
1431
|
+
let n = en(e.body);
|
|
1399
1432
|
if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
|
|
1400
|
-
let r = X(i), o =
|
|
1433
|
+
let r = X(i), o = te(c()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1401
1434
|
if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
|
|
1402
1435
|
let p = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1403
1436
|
r.set(o, {
|
|
1404
1437
|
postId: u.value,
|
|
1405
|
-
boardId:
|
|
1438
|
+
boardId: l.value,
|
|
1406
1439
|
listed: d(),
|
|
1407
1440
|
body: n,
|
|
1408
1441
|
authorUid: e.authorUid,
|
|
@@ -1419,24 +1452,24 @@ function qt(e, n, r = {}) {
|
|
|
1419
1452
|
isReply: !0,
|
|
1420
1453
|
createdAt: G(),
|
|
1421
1454
|
updatedAt: G()
|
|
1422
|
-
}), r.update(
|
|
1455
|
+
}), r.update(s(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1423
1456
|
}
|
|
1424
|
-
async function
|
|
1457
|
+
async function ee(e) {
|
|
1425
1458
|
if (!e.id) return;
|
|
1426
1459
|
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
|
|
1427
|
-
await
|
|
1460
|
+
await yt(i, a(), e.id);
|
|
1428
1461
|
let n = X(i);
|
|
1429
|
-
n.delete(
|
|
1462
|
+
n.delete(s(i, a(), e.id)), e.parentId && e.rootId && n.update(s(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(), u.value), { commentCount: B(-1) }), await n.commit(), g.value = g.value.filter((t) => t.id !== e.id), p.value = p.value.filter((t) => t.id !== e.id), f.value = f.value.filter((t) => t.id !== e.id);
|
|
1430
1463
|
}
|
|
1431
|
-
async function
|
|
1432
|
-
let n =
|
|
1433
|
-
await J(
|
|
1464
|
+
async function F(e, t) {
|
|
1465
|
+
let n = en(t);
|
|
1466
|
+
await J(s(i, a(), e), {
|
|
1434
1467
|
body: n,
|
|
1435
1468
|
updatedAt: G()
|
|
1436
1469
|
});
|
|
1437
1470
|
}
|
|
1438
|
-
async function
|
|
1439
|
-
await J(
|
|
1471
|
+
async function I(e, t, n) {
|
|
1472
|
+
await J(s(i, a(), e), {
|
|
1440
1473
|
isBlinded: t,
|
|
1441
1474
|
moderatedAt: G(),
|
|
1442
1475
|
moderatedBy: n
|
|
@@ -1444,19 +1477,19 @@ function qt(e, n, r = {}) {
|
|
|
1444
1477
|
}
|
|
1445
1478
|
return {
|
|
1446
1479
|
comments: w,
|
|
1447
|
-
commentsPending:
|
|
1480
|
+
commentsPending: A(() => y.value || m.value),
|
|
1448
1481
|
hasMore: v,
|
|
1449
|
-
loadingMore:
|
|
1450
|
-
loadMore:
|
|
1451
|
-
addComment:
|
|
1482
|
+
loadingMore: _,
|
|
1483
|
+
loadMore: D,
|
|
1484
|
+
addComment: O,
|
|
1452
1485
|
addReply: k,
|
|
1453
|
-
updateComment:
|
|
1454
|
-
setCommentBlinded:
|
|
1455
|
-
deleteComment:
|
|
1486
|
+
updateComment: F,
|
|
1487
|
+
setCommentBlinded: I,
|
|
1488
|
+
deleteComment: ee
|
|
1456
1489
|
};
|
|
1457
1490
|
}
|
|
1458
|
-
function
|
|
1459
|
-
let r = Q(), i = () =>
|
|
1491
|
+
function nn(e, t, n) {
|
|
1492
|
+
let r = Q(), i = () => h(), a = () => E(r, i()), o = t.trim(), s = M([]), c = M(!1), l = M(!1), u = M(!0), d = null, f;
|
|
1460
1493
|
function p(e) {
|
|
1461
1494
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
1462
1495
|
}
|
|
@@ -1472,8 +1505,8 @@ function Jt(e, t, n) {
|
|
|
1472
1505
|
console.error("[memi-board:replies] onSnapshot failed", e);
|
|
1473
1506
|
});
|
|
1474
1507
|
}
|
|
1475
|
-
async function
|
|
1476
|
-
if (!
|
|
1508
|
+
async function g() {
|
|
1509
|
+
if (!c.value && u.value) {
|
|
1477
1510
|
c.value = !0;
|
|
1478
1511
|
try {
|
|
1479
1512
|
let e = [
|
|
@@ -1484,39 +1517,39 @@ function Jt(e, t, n) {
|
|
|
1484
1517
|
U(L(), "asc"),
|
|
1485
1518
|
...d ? [q(d)] : [],
|
|
1486
1519
|
V(6)
|
|
1487
|
-
], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0,
|
|
1520
|
+
], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Qt), c = i.map((e) => ({
|
|
1488
1521
|
id: e.id,
|
|
1489
1522
|
...e.data()
|
|
1490
1523
|
})).filter((e) => !r.has(e.id));
|
|
1491
|
-
s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <=
|
|
1524
|
+
s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Qt && (u.value = !1, m());
|
|
1492
1525
|
} finally {
|
|
1493
1526
|
c.value = !1;
|
|
1494
1527
|
}
|
|
1495
1528
|
}
|
|
1496
1529
|
}
|
|
1497
|
-
function
|
|
1530
|
+
function _() {
|
|
1498
1531
|
f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
|
|
1499
1532
|
}
|
|
1500
1533
|
async function v() {
|
|
1501
|
-
|
|
1534
|
+
_(), await g();
|
|
1502
1535
|
}
|
|
1503
|
-
return
|
|
1536
|
+
return j(() => f?.()), {
|
|
1504
1537
|
replies: s,
|
|
1505
1538
|
loading: c,
|
|
1506
1539
|
loaded: l,
|
|
1507
1540
|
hasMore: u,
|
|
1508
|
-
loadMore:
|
|
1541
|
+
loadMore: g,
|
|
1509
1542
|
refresh: v
|
|
1510
1543
|
};
|
|
1511
1544
|
}
|
|
1512
1545
|
//#endregion
|
|
1513
1546
|
//#region src/composables/useMemiBoardLikes.ts
|
|
1514
|
-
function
|
|
1515
|
-
let r = () =>
|
|
1547
|
+
function rn(e, n) {
|
|
1548
|
+
let r = () => h(), i = Q(), a = Z(), o = M(!1), s = M(!1), c;
|
|
1516
1549
|
function l(e) {
|
|
1517
|
-
return
|
|
1550
|
+
return k(i, r(), n, e);
|
|
1518
1551
|
}
|
|
1519
|
-
|
|
1552
|
+
P(a, (e) => {
|
|
1520
1553
|
if (c?.(), c = void 0, !e) {
|
|
1521
1554
|
o.value = !1;
|
|
1522
1555
|
return;
|
|
@@ -1526,7 +1559,7 @@ function Yt(e, n) {
|
|
|
1526
1559
|
}, (e) => {
|
|
1527
1560
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
1528
1561
|
});
|
|
1529
|
-
}, { immediate: !0 }),
|
|
1562
|
+
}, { immediate: !0 }), j(() => c?.());
|
|
1530
1563
|
async function u() {
|
|
1531
1564
|
let c = a.value?.uid;
|
|
1532
1565
|
if (!c) throw Error("로그인이 필요합니다.");
|
|
@@ -1534,7 +1567,7 @@ function Yt(e, n) {
|
|
|
1534
1567
|
s.value = !0;
|
|
1535
1568
|
try {
|
|
1536
1569
|
let a = l(c), o = t(i, r(), n);
|
|
1537
|
-
return await
|
|
1570
|
+
return await ie(i, async (t) => (await t.get(a)).exists() ? (t.delete(a), t.update(o, { likeCount: B(-1) }), !1) : (t.set(a, {
|
|
1538
1571
|
uid: c,
|
|
1539
1572
|
postId: n,
|
|
1540
1573
|
boardId: e,
|
|
@@ -1550,50 +1583,50 @@ function Yt(e, n) {
|
|
|
1550
1583
|
toggleLike: u
|
|
1551
1584
|
};
|
|
1552
1585
|
}
|
|
1553
|
-
var
|
|
1554
|
-
function
|
|
1555
|
-
let n = () =>
|
|
1556
|
-
|
|
1557
|
-
u?.(), u = void 0,
|
|
1586
|
+
var an = Symbol.for("memi-board:comment-likes");
|
|
1587
|
+
function on(e, t) {
|
|
1588
|
+
let n = () => h(), r = Q(), i = Z(), a = A(() => N(e)), o = A(() => N(t)), c = M(/* @__PURE__ */ new Set()), l = M(null), u;
|
|
1589
|
+
P([i, o], ([e, t]) => {
|
|
1590
|
+
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = H(W(O(r, n()), Y("uid", "==", e.uid), Y("postId", "==", t), Y("target", "==", "comment")), (e) => {
|
|
1558
1591
|
let t = /* @__PURE__ */ new Set();
|
|
1559
1592
|
for (let n of e.docs) {
|
|
1560
1593
|
let e = n.data().commentId;
|
|
1561
1594
|
typeof e == "string" && e && t.add(e);
|
|
1562
1595
|
}
|
|
1563
|
-
|
|
1596
|
+
c.value = t;
|
|
1564
1597
|
}, (e) => {
|
|
1565
1598
|
console.error("[memi-board:comment-likes] onSnapshot failed", e);
|
|
1566
1599
|
}));
|
|
1567
|
-
}, { immediate: !0 }),
|
|
1600
|
+
}, { immediate: !0 }), j(() => u?.());
|
|
1568
1601
|
function d(e) {
|
|
1569
|
-
return !!e &&
|
|
1602
|
+
return !!e && c.value.has(e);
|
|
1570
1603
|
}
|
|
1571
1604
|
function f(e) {
|
|
1572
|
-
return !!e &&
|
|
1605
|
+
return !!e && l.value === e;
|
|
1573
1606
|
}
|
|
1574
1607
|
async function p(e) {
|
|
1575
1608
|
let t = i.value?.uid;
|
|
1576
1609
|
if (!t) throw Error("로그인이 필요합니다.");
|
|
1577
1610
|
if (!e) return !1;
|
|
1578
|
-
if (
|
|
1579
|
-
|
|
1611
|
+
if (l.value) return d(e);
|
|
1612
|
+
l.value = e;
|
|
1580
1613
|
try {
|
|
1581
|
-
let i =
|
|
1582
|
-
let r = await n.get(i), s = await n.get(
|
|
1614
|
+
let i = k(r, n(), e, t), l = s(r, n(), e), u = await ie(r, async (n) => {
|
|
1615
|
+
let r = await n.get(i), s = await n.get(l);
|
|
1583
1616
|
if (!s.exists()) throw Error("댓글을 찾을 수 없습니다.");
|
|
1584
|
-
let
|
|
1585
|
-
return r.exists() ? (n.delete(i), n.update(
|
|
1617
|
+
let c = Number(s.data()?.likeCount ?? 0);
|
|
1618
|
+
return r.exists() ? (n.delete(i), n.update(l, { likeCount: Math.max(0, c - 1) }), !1) : (n.set(i, {
|
|
1586
1619
|
uid: t,
|
|
1587
1620
|
postId: o.value,
|
|
1588
1621
|
boardId: a.value,
|
|
1589
1622
|
commentId: e,
|
|
1590
1623
|
target: "comment",
|
|
1591
1624
|
createdAt: G()
|
|
1592
|
-
}), n.update(
|
|
1593
|
-
}), d = new Set(
|
|
1594
|
-
return u ? d.add(e) : d.delete(e),
|
|
1625
|
+
}), n.update(l, { likeCount: c + 1 }), !0);
|
|
1626
|
+
}), d = new Set(c.value);
|
|
1627
|
+
return u ? d.add(e) : d.delete(e), c.value = d, u;
|
|
1595
1628
|
} finally {
|
|
1596
|
-
|
|
1629
|
+
l.value = null;
|
|
1597
1630
|
}
|
|
1598
1631
|
}
|
|
1599
1632
|
return {
|
|
@@ -1604,7 +1637,7 @@ function Zt(e, t) {
|
|
|
1604
1637
|
}
|
|
1605
1638
|
//#endregion
|
|
1606
1639
|
//#region src/composables/useMemiBoardReports.ts
|
|
1607
|
-
var
|
|
1640
|
+
var sn = 200, cn = [
|
|
1608
1641
|
{
|
|
1609
1642
|
value: "spam",
|
|
1610
1643
|
label: "스팸·광고"
|
|
@@ -1626,12 +1659,12 @@ var Qt = 200, $t = [
|
|
|
1626
1659
|
label: "기타"
|
|
1627
1660
|
}
|
|
1628
1661
|
];
|
|
1629
|
-
function
|
|
1630
|
-
return
|
|
1662
|
+
function ln(e) {
|
|
1663
|
+
return cn.find((t) => t.value === e)?.label || e || "신고";
|
|
1631
1664
|
}
|
|
1632
|
-
function
|
|
1633
|
-
let n = () =>
|
|
1634
|
-
|
|
1665
|
+
function un(e, t) {
|
|
1666
|
+
let n = () => h(), i = Q(), a = Z(), o = M(!1), s = M(!1), c;
|
|
1667
|
+
P(a, (e) => {
|
|
1635
1668
|
if (c?.(), c = void 0, !e) {
|
|
1636
1669
|
o.value = !1;
|
|
1637
1670
|
return;
|
|
@@ -1641,13 +1674,13 @@ function tn(e, t) {
|
|
|
1641
1674
|
}, (e) => {
|
|
1642
1675
|
console.error("[memi-board:reports] onSnapshot failed", e);
|
|
1643
1676
|
});
|
|
1644
|
-
}, { immediate: !0 }),
|
|
1677
|
+
}, { immediate: !0 }), j(() => c?.());
|
|
1645
1678
|
async function l(c) {
|
|
1646
1679
|
let l = a.value?.uid;
|
|
1647
1680
|
if (!l) throw Error("로그인이 필요합니다.");
|
|
1648
1681
|
if (s.value) return;
|
|
1649
1682
|
if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
|
|
1650
|
-
let u =
|
|
1683
|
+
let u = cn.some((e) => e.value === c.reason) ? c.reason : null;
|
|
1651
1684
|
if (!u) throw Error("신고 사유를 선택해 주세요.");
|
|
1652
1685
|
let d = (c.detail || "").trim();
|
|
1653
1686
|
if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
|
|
@@ -1676,35 +1709,35 @@ function tn(e, t) {
|
|
|
1676
1709
|
submitReport: l
|
|
1677
1710
|
};
|
|
1678
1711
|
}
|
|
1679
|
-
var
|
|
1680
|
-
function
|
|
1681
|
-
let t = () =>
|
|
1682
|
-
function
|
|
1712
|
+
var dn = 20;
|
|
1713
|
+
function fn(e = {}) {
|
|
1714
|
+
let t = () => h(), n = Q(), { canManageContent: a, canManageBoard: o } = ct(), s = e.pageSize ?? dn, c = A(() => N(e.boardId)?.trim() || ""), l = M([]), u = M(!0), d = M(!1), f = M(!1), p = M(""), m = null, g;
|
|
1715
|
+
function _(e) {
|
|
1683
1716
|
return {
|
|
1684
1717
|
id: e.id,
|
|
1685
1718
|
...e.data()
|
|
1686
1719
|
};
|
|
1687
1720
|
}
|
|
1688
1721
|
function v() {
|
|
1689
|
-
if (
|
|
1722
|
+
if (g?.(), !a.value) {
|
|
1690
1723
|
l.value = [], u.value = !1;
|
|
1691
1724
|
return;
|
|
1692
1725
|
}
|
|
1693
1726
|
u.value = !0;
|
|
1694
1727
|
let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")];
|
|
1695
|
-
|
|
1696
|
-
l.value = e.docs.map(
|
|
1728
|
+
g = H(W(i(n, t()), ...e, U("createdAt", "desc"), U(L(), "desc"), V(s)), (e) => {
|
|
1729
|
+
l.value = e.docs.map(_).filter((e) => o(e.boardId)), m = e.docs.at(-1) ?? null, f.value = e.docs.length >= s, u.value = !1;
|
|
1697
1730
|
}, (e) => {
|
|
1698
1731
|
console.error("[memi-board:report-queue] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
|
|
1699
1732
|
});
|
|
1700
1733
|
}
|
|
1701
|
-
|
|
1734
|
+
P([a, c], v, { immediate: !0 }), j(() => g?.());
|
|
1702
1735
|
async function y() {
|
|
1703
|
-
if (!
|
|
1736
|
+
if (!d.value && f.value && m && a.value) {
|
|
1704
1737
|
d.value = !0, p.value = "";
|
|
1705
1738
|
try {
|
|
1706
|
-
let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")], r = await z(W(
|
|
1707
|
-
l.value.push(...
|
|
1739
|
+
let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")], r = await z(W(i(n, t()), ...e, U("createdAt", "desc"), U(L(), "desc"), q(m), V(s + 1))), a = r.docs.slice(0, s), u = new Set(l.value.map((e) => e.id));
|
|
1740
|
+
l.value.push(...a.map(_).filter((e) => !u.has(e.id) && o(e.boardId))), m = a.at(-1) ?? m, f.value = r.docs.length > s;
|
|
1708
1741
|
} catch (e) {
|
|
1709
1742
|
p.value = e.message || String(e);
|
|
1710
1743
|
} finally {
|
|
@@ -1713,7 +1746,7 @@ function rn(e = {}) {
|
|
|
1713
1746
|
}
|
|
1714
1747
|
}
|
|
1715
1748
|
async function b(e, i) {
|
|
1716
|
-
|
|
1749
|
+
e.id && o(e.boardId) && await J(r(n, t(), e.postId, e.uid), {
|
|
1717
1750
|
status: i,
|
|
1718
1751
|
reviewedAt: G(),
|
|
1719
1752
|
reviewedBy: Z().value?.uid || ""
|
|
@@ -1732,8 +1765,8 @@ function rn(e = {}) {
|
|
|
1732
1765
|
}
|
|
1733
1766
|
//#endregion
|
|
1734
1767
|
//#region src/composables/useMemiBoardViews.ts
|
|
1735
|
-
function
|
|
1736
|
-
let e = () =>
|
|
1768
|
+
function pn() {
|
|
1769
|
+
let e = () => h(), n = Q();
|
|
1737
1770
|
function r(t, n) {
|
|
1738
1771
|
let { postsCollection: r } = e();
|
|
1739
1772
|
return `memi-board:viewed:${r}:${t}:${n}`;
|
|
@@ -1747,13 +1780,13 @@ function an() {
|
|
|
1747
1780
|
}
|
|
1748
1781
|
}
|
|
1749
1782
|
function a(e, t) {
|
|
1750
|
-
if (
|
|
1783
|
+
if (typeof sessionStorage < "u") try {
|
|
1751
1784
|
sessionStorage.setItem(r(e, t), "1");
|
|
1752
1785
|
} catch {}
|
|
1753
1786
|
}
|
|
1754
1787
|
async function o(o, s) {
|
|
1755
1788
|
let c = o?.trim(), l = s?.trim();
|
|
1756
|
-
if (
|
|
1789
|
+
if (c && l && !i(c, l)) {
|
|
1757
1790
|
a(c, l);
|
|
1758
1791
|
try {
|
|
1759
1792
|
await J(t(n, e(), l), { viewCount: B(1) });
|
|
@@ -1772,7 +1805,7 @@ function an() {
|
|
|
1772
1805
|
}
|
|
1773
1806
|
//#endregion
|
|
1774
1807
|
//#region src/utils/moderation-prompt.ts
|
|
1775
|
-
var
|
|
1808
|
+
var mn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", hn = {
|
|
1776
1809
|
abuse: "욕설·비속어·모욕",
|
|
1777
1810
|
spam: "스팸·광고",
|
|
1778
1811
|
adult: "선정적·성인 내용",
|
|
@@ -1780,17 +1813,17 @@ var on = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
1780
1813
|
other: "부적절한 내용",
|
|
1781
1814
|
none: ""
|
|
1782
1815
|
};
|
|
1783
|
-
function
|
|
1816
|
+
function gn(e) {
|
|
1784
1817
|
if (e.via === "local" && e.localHit) {
|
|
1785
1818
|
let t = e.localHit.trim();
|
|
1786
1819
|
return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
|
|
1787
1820
|
}
|
|
1788
1821
|
let t = (e.reason || "").trim();
|
|
1789
1822
|
if (t) return t;
|
|
1790
|
-
let n = e.category && e.category !== "none" ?
|
|
1823
|
+
let n = e.category && e.category !== "none" ? hn[e.category] || e.category : "";
|
|
1791
1824
|
return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
|
|
1792
1825
|
}
|
|
1793
|
-
var
|
|
1826
|
+
var _n = [
|
|
1794
1827
|
"씨발",
|
|
1795
1828
|
"시발",
|
|
1796
1829
|
"시이발",
|
|
@@ -1816,10 +1849,10 @@ var ln = [
|
|
|
1816
1849
|
"bitch",
|
|
1817
1850
|
"asshole"
|
|
1818
1851
|
];
|
|
1819
|
-
function
|
|
1852
|
+
function vn(e) {
|
|
1820
1853
|
return new RegExp(e.join("|"), "i");
|
|
1821
1854
|
}
|
|
1822
|
-
var
|
|
1855
|
+
var yn = /* @__PURE__ */ new Set([
|
|
1823
1856
|
"none",
|
|
1824
1857
|
"abuse",
|
|
1825
1858
|
"spam",
|
|
@@ -1827,7 +1860,7 @@ var dn = /* @__PURE__ */ new Set([
|
|
|
1827
1860
|
"violence",
|
|
1828
1861
|
"other"
|
|
1829
1862
|
]);
|
|
1830
|
-
function
|
|
1863
|
+
function bn(e) {
|
|
1831
1864
|
let t = e.trim();
|
|
1832
1865
|
if (!t) return null;
|
|
1833
1866
|
let n;
|
|
@@ -1845,7 +1878,7 @@ function fn(e) {
|
|
|
1845
1878
|
if (typeof n != "object" || !n) return null;
|
|
1846
1879
|
let r = n;
|
|
1847
1880
|
if (typeof r.flagged != "boolean") return null;
|
|
1848
|
-
let i = typeof r.category == "string" &&
|
|
1881
|
+
let i = typeof r.category == "string" && yn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
1849
1882
|
return {
|
|
1850
1883
|
flagged: r.flagged,
|
|
1851
1884
|
category: i,
|
|
@@ -1854,20 +1887,20 @@ function fn(e) {
|
|
|
1854
1887
|
}
|
|
1855
1888
|
//#endregion
|
|
1856
1889
|
//#region src/composables/useMemiBoardModeration.ts
|
|
1857
|
-
var
|
|
1890
|
+
var xn = {
|
|
1858
1891
|
flagged: !1,
|
|
1859
1892
|
category: "none",
|
|
1860
1893
|
reason: "",
|
|
1861
1894
|
via: "empty"
|
|
1862
1895
|
};
|
|
1863
|
-
function
|
|
1864
|
-
let e =
|
|
1896
|
+
function Sn() {
|
|
1897
|
+
let e = c().moderation ?? {}, t = se(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = ct(), a = vn([..._n, ...e.localBlocklist ?? []]);
|
|
1865
1898
|
function o(e) {
|
|
1866
1899
|
let t = e.match(a);
|
|
1867
1900
|
return t ? {
|
|
1868
1901
|
flagged: !0,
|
|
1869
1902
|
category: "abuse",
|
|
1870
|
-
reason:
|
|
1903
|
+
reason: gn({
|
|
1871
1904
|
via: "local",
|
|
1872
1905
|
localHit: t[0] || "",
|
|
1873
1906
|
category: "abuse"
|
|
@@ -1876,26 +1909,26 @@ function mn() {
|
|
|
1876
1909
|
} : null;
|
|
1877
1910
|
}
|
|
1878
1911
|
function s() {
|
|
1879
|
-
let n = e.model ?? "gemini-3.5-flash-lite", r = e.useLimitedUseAppCheckTokens !== !1, i =
|
|
1880
|
-
backend: new
|
|
1912
|
+
let n = e.model ?? "gemini-3.5-flash-lite", r = e.useLimitedUseAppCheckTokens !== !1, i = Se(t, {
|
|
1913
|
+
backend: new be(),
|
|
1881
1914
|
useLimitedUseAppCheckTokens: r
|
|
1882
1915
|
});
|
|
1883
|
-
return
|
|
1916
|
+
return Ce(i, {
|
|
1884
1917
|
model: n,
|
|
1885
|
-
systemInstruction:
|
|
1918
|
+
systemInstruction: mn,
|
|
1886
1919
|
generationConfig: {
|
|
1887
1920
|
temperature: 0,
|
|
1888
1921
|
maxOutputTokens: 256,
|
|
1889
1922
|
responseMimeType: "application/json",
|
|
1890
|
-
responseSchema:
|
|
1891
|
-
flagged:
|
|
1892
|
-
category:
|
|
1893
|
-
reason:
|
|
1923
|
+
responseSchema: xe.object({ properties: {
|
|
1924
|
+
flagged: xe.boolean(),
|
|
1925
|
+
category: xe.string(),
|
|
1926
|
+
reason: xe.string()
|
|
1894
1927
|
} })
|
|
1895
1928
|
}
|
|
1896
1929
|
});
|
|
1897
1930
|
}
|
|
1898
|
-
function
|
|
1931
|
+
function l() {
|
|
1899
1932
|
return e.onError === "block" ? {
|
|
1900
1933
|
flagged: !0,
|
|
1901
1934
|
category: "other",
|
|
@@ -1910,8 +1943,8 @@ function mn() {
|
|
|
1910
1943
|
via: "ai-error-allow"
|
|
1911
1944
|
};
|
|
1912
1945
|
}
|
|
1913
|
-
function
|
|
1914
|
-
let n =
|
|
1946
|
+
function u(e, t = "") {
|
|
1947
|
+
let n = gn({
|
|
1915
1948
|
reason: e.reason,
|
|
1916
1949
|
category: e.category,
|
|
1917
1950
|
via: e.via
|
|
@@ -1925,9 +1958,9 @@ function mn() {
|
|
|
1925
1958
|
if (!e.flagged || e.error || e.via !== "local" && e.via !== "ai") return e;
|
|
1926
1959
|
try {
|
|
1927
1960
|
let { messageSuffix: t } = await i();
|
|
1928
|
-
return
|
|
1961
|
+
return u(e, t);
|
|
1929
1962
|
} catch (t) {
|
|
1930
|
-
return console.warn("[memi-board] record moderation strike failed", t instanceof Error ? t.message : t),
|
|
1963
|
+
return console.warn("[memi-board] record moderation strike failed", t instanceof Error ? t.message : t), u(e);
|
|
1931
1964
|
}
|
|
1932
1965
|
}
|
|
1933
1966
|
async function f(t, i) {
|
|
@@ -1939,26 +1972,26 @@ function mn() {
|
|
|
1939
1972
|
};
|
|
1940
1973
|
let a = t.trim();
|
|
1941
1974
|
if (!a) return {
|
|
1942
|
-
...
|
|
1975
|
+
...xn,
|
|
1943
1976
|
via: "empty"
|
|
1944
1977
|
};
|
|
1945
1978
|
if (i?.skipFilter) return {
|
|
1946
|
-
...
|
|
1979
|
+
...xn,
|
|
1947
1980
|
via: "admin-override"
|
|
1948
1981
|
};
|
|
1949
|
-
let
|
|
1950
|
-
if (
|
|
1982
|
+
let c = o(a);
|
|
1983
|
+
if (c) return d(c);
|
|
1951
1984
|
if (e.enabled === !1) return {
|
|
1952
|
-
...
|
|
1985
|
+
...xn,
|
|
1953
1986
|
via: "disabled"
|
|
1954
1987
|
};
|
|
1955
1988
|
try {
|
|
1956
|
-
let e =
|
|
1957
|
-
if (!e) return
|
|
1989
|
+
let e = bn((await s().generateContent(`심사할 텍스트:\n"""${a.slice(0, 4e3)}"""`)).response.text());
|
|
1990
|
+
if (!e) return l();
|
|
1958
1991
|
let t = {
|
|
1959
1992
|
flagged: e.flagged,
|
|
1960
1993
|
category: e.category,
|
|
1961
|
-
reason:
|
|
1994
|
+
reason: gn({
|
|
1962
1995
|
reason: e.reason,
|
|
1963
1996
|
category: e.category,
|
|
1964
1997
|
via: "ai"
|
|
@@ -1967,7 +2000,7 @@ function mn() {
|
|
|
1967
2000
|
};
|
|
1968
2001
|
return t.flagged ? d(t) : t;
|
|
1969
2002
|
} catch (e) {
|
|
1970
|
-
return console.warn("[memi-board] moderation AI failed", e instanceof Error ? e.message : e),
|
|
2003
|
+
return console.warn("[memi-board] moderation AI failed", e instanceof Error ? e.message : e), l();
|
|
1971
2004
|
}
|
|
1972
2005
|
}
|
|
1973
2006
|
async function p(t) {
|
|
@@ -1978,22 +2011,22 @@ function mn() {
|
|
|
1978
2011
|
via: "restricted"
|
|
1979
2012
|
};
|
|
1980
2013
|
if (e.enabled === !1 || !e.moderateImages) return {
|
|
1981
|
-
...
|
|
2014
|
+
...xn,
|
|
1982
2015
|
via: "disabled"
|
|
1983
2016
|
};
|
|
1984
2017
|
try {
|
|
1985
2018
|
let e = await new Promise((e, n) => {
|
|
1986
2019
|
let r = new FileReader();
|
|
1987
2020
|
r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
|
|
1988
|
-
}), n =
|
|
2021
|
+
}), n = bn((await s().generateContent([{ inlineData: {
|
|
1989
2022
|
mimeType: t.type || "image/jpeg",
|
|
1990
2023
|
data: e
|
|
1991
2024
|
} }, "이 이미지를 심사해 주세요."])).response.text());
|
|
1992
|
-
if (!n) return
|
|
2025
|
+
if (!n) return l();
|
|
1993
2026
|
let r = {
|
|
1994
2027
|
flagged: n.flagged,
|
|
1995
2028
|
category: n.category,
|
|
1996
|
-
reason:
|
|
2029
|
+
reason: gn({
|
|
1997
2030
|
reason: n.reason,
|
|
1998
2031
|
category: n.category,
|
|
1999
2032
|
via: "ai"
|
|
@@ -2002,7 +2035,7 @@ function mn() {
|
|
|
2002
2035
|
};
|
|
2003
2036
|
return r.flagged ? d(r) : r;
|
|
2004
2037
|
} catch (e) {
|
|
2005
|
-
return console.warn("[memi-board] moderation AI image failed", e instanceof Error ? e.message : e),
|
|
2038
|
+
return console.warn("[memi-board] moderation AI image failed", e instanceof Error ? e.message : e), l();
|
|
2006
2039
|
}
|
|
2007
2040
|
}
|
|
2008
2041
|
return {
|
|
@@ -2012,7 +2045,7 @@ function mn() {
|
|
|
2012
2045
|
}
|
|
2013
2046
|
//#endregion
|
|
2014
2047
|
//#region src/composables/useMemiBoardUsers.ts
|
|
2015
|
-
var
|
|
2048
|
+
var Cn = [
|
|
2016
2049
|
{
|
|
2017
2050
|
value: "admin",
|
|
2018
2051
|
label: "관리자",
|
|
@@ -2029,11 +2062,11 @@ var hn = [
|
|
|
2029
2062
|
description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
|
|
2030
2063
|
}
|
|
2031
2064
|
];
|
|
2032
|
-
function
|
|
2033
|
-
let t = () =>
|
|
2065
|
+
function wn(e = {}) {
|
|
2066
|
+
let t = () => h(), n = Q(), r = A(() => N(e.enabled) ?? !0 ? p(n, t()) : null), i = ae(r, { ssrKey: S(t(), "users-management") });
|
|
2034
2067
|
async function a(e, r) {
|
|
2035
|
-
if (!
|
|
2036
|
-
await J(
|
|
2068
|
+
if (!Cn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
|
|
2069
|
+
await J(y(n, t(), e), {
|
|
2037
2070
|
role: r,
|
|
2038
2071
|
updatedAt: G()
|
|
2039
2072
|
});
|
|
@@ -2046,11 +2079,11 @@ function gn(e = {}) {
|
|
|
2046
2079
|
}
|
|
2047
2080
|
//#endregion
|
|
2048
2081
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2049
|
-
function
|
|
2050
|
-
let t = Q(), n = () =>
|
|
2082
|
+
function Tn(e) {
|
|
2083
|
+
let t = Q(), n = () => h(), r = A(() => N(e)), i = A(() => {
|
|
2051
2084
|
let e = r.value;
|
|
2052
|
-
return e ?
|
|
2053
|
-
}), { data: a, pending: o } =
|
|
2085
|
+
return e ? y(t, n(), e) : null;
|
|
2086
|
+
}), { data: a, pending: o } = oe(i);
|
|
2054
2087
|
return {
|
|
2055
2088
|
profile: a,
|
|
2056
2089
|
profilePending: o
|
|
@@ -2058,7 +2091,7 @@ function _n(e) {
|
|
|
2058
2091
|
}
|
|
2059
2092
|
//#endregion
|
|
2060
2093
|
//#region src/utils/section.ts
|
|
2061
|
-
var
|
|
2094
|
+
var En = [
|
|
2062
2095
|
3,
|
|
2063
2096
|
4,
|
|
2064
2097
|
5,
|
|
@@ -2067,7 +2100,7 @@ var vn = [
|
|
|
2067
2100
|
8,
|
|
2068
2101
|
9,
|
|
2069
2102
|
12
|
|
2070
|
-
],
|
|
2103
|
+
], Dn = [
|
|
2071
2104
|
{
|
|
2072
2105
|
value: "list",
|
|
2073
2106
|
label: "게시목록"
|
|
@@ -2080,24 +2113,24 @@ var vn = [
|
|
|
2080
2113
|
value: "comments",
|
|
2081
2114
|
label: "댓글모음"
|
|
2082
2115
|
}
|
|
2083
|
-
],
|
|
2116
|
+
], On = [{
|
|
2084
2117
|
value: "latest",
|
|
2085
2118
|
label: "최신순"
|
|
2086
2119
|
}, {
|
|
2087
2120
|
value: "likes",
|
|
2088
2121
|
label: "좋아요순"
|
|
2089
2122
|
}];
|
|
2090
|
-
function
|
|
2123
|
+
function kn(e) {
|
|
2091
2124
|
return e === "list" ? 5 : 4;
|
|
2092
2125
|
}
|
|
2093
|
-
function
|
|
2126
|
+
function An(e, t) {
|
|
2094
2127
|
let n = Math.floor(Number(t));
|
|
2095
|
-
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) :
|
|
2128
|
+
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : kn(e);
|
|
2096
2129
|
}
|
|
2097
|
-
function
|
|
2130
|
+
function jn(e) {
|
|
2098
2131
|
return e === 12 ? null : 12 - e;
|
|
2099
2132
|
}
|
|
2100
|
-
function
|
|
2133
|
+
function Mn(e) {
|
|
2101
2134
|
return {
|
|
2102
2135
|
3: "col-span-12 md:col-span-3",
|
|
2103
2136
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2109,36 +2142,36 @@ function wn(e) {
|
|
|
2109
2142
|
12: "col-span-12"
|
|
2110
2143
|
}[e];
|
|
2111
2144
|
}
|
|
2112
|
-
function
|
|
2145
|
+
function Nn(e) {
|
|
2113
2146
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2114
2147
|
}
|
|
2115
|
-
function
|
|
2148
|
+
function Pn(e) {
|
|
2116
2149
|
let t = e.boardId || e.category || "free";
|
|
2117
2150
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2118
2151
|
}
|
|
2119
|
-
function
|
|
2152
|
+
function Fn(e) {
|
|
2120
2153
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2121
2154
|
}
|
|
2122
|
-
function
|
|
2123
|
-
if (
|
|
2155
|
+
function In(e) {
|
|
2156
|
+
if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2124
2157
|
}
|
|
2125
|
-
function
|
|
2158
|
+
function Ln(e) {
|
|
2126
2159
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2127
2160
|
}
|
|
2128
|
-
function
|
|
2161
|
+
function Rn(e) {
|
|
2129
2162
|
return e.title?.trim() || e.summary || "게시물";
|
|
2130
2163
|
}
|
|
2131
|
-
function
|
|
2164
|
+
function zn(e) {
|
|
2132
2165
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2133
2166
|
}
|
|
2134
|
-
function
|
|
2135
|
-
return
|
|
2167
|
+
function Bn(e) {
|
|
2168
|
+
return zn(e) === "media" ? "미디어" : "목록";
|
|
2136
2169
|
}
|
|
2137
|
-
function
|
|
2170
|
+
function Vn(e, t) {
|
|
2138
2171
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2139
2172
|
}
|
|
2140
|
-
function
|
|
2141
|
-
let n = e.map((e) =>
|
|
2173
|
+
function Hn(e, t) {
|
|
2174
|
+
let n = e.map((e) => Vn(e.boardId, t));
|
|
2142
2175
|
if (!n.length) return "default";
|
|
2143
2176
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2144
2177
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2146,68 +2179,68 @@ function Pn(e, t) {
|
|
|
2146
2179
|
}
|
|
2147
2180
|
//#endregion
|
|
2148
2181
|
//#region src/composables/useMemiBoardSections.ts
|
|
2149
|
-
var
|
|
2150
|
-
function
|
|
2182
|
+
var Un = [];
|
|
2183
|
+
function Wn(e) {
|
|
2151
2184
|
let t = Number(e.cols);
|
|
2152
|
-
if (
|
|
2185
|
+
if (En.includes(t)) return t;
|
|
2153
2186
|
let n = e.width;
|
|
2154
2187
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2155
2188
|
}
|
|
2156
|
-
function
|
|
2189
|
+
function Gn(e) {
|
|
2157
2190
|
let t = e.height ?? e.size;
|
|
2158
2191
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2159
2192
|
}
|
|
2160
|
-
function
|
|
2193
|
+
function Kn(e) {
|
|
2161
2194
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2162
2195
|
}
|
|
2163
|
-
function
|
|
2196
|
+
function qn(e) {
|
|
2164
2197
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2165
2198
|
}
|
|
2166
|
-
function
|
|
2199
|
+
function Jn(e) {
|
|
2167
2200
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2168
2201
|
}
|
|
2169
|
-
function
|
|
2202
|
+
function Yn(e, t) {
|
|
2170
2203
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2171
2204
|
for (let e of n) {
|
|
2172
2205
|
let n = String(e || "").trim();
|
|
2173
|
-
if (
|
|
2206
|
+
if (n && !r.includes(n) && (r.push(n), r.length >= t)) break;
|
|
2174
2207
|
}
|
|
2175
2208
|
return r;
|
|
2176
2209
|
}
|
|
2177
|
-
function
|
|
2178
|
-
let n =
|
|
2210
|
+
function Xn(e, t) {
|
|
2211
|
+
let n = Kn(e), r = An(n, e.count);
|
|
2179
2212
|
return {
|
|
2180
2213
|
id: String(e.id || `box-${t + 1}`),
|
|
2181
2214
|
title: String(e.title || `섹션 ${t + 1}`),
|
|
2182
2215
|
showTitle: e.showTitle !== !1,
|
|
2183
2216
|
kind: n,
|
|
2184
|
-
boardId:
|
|
2217
|
+
boardId: Jn(e),
|
|
2185
2218
|
count: r,
|
|
2186
|
-
postIds:
|
|
2187
|
-
sort:
|
|
2188
|
-
cols:
|
|
2189
|
-
height:
|
|
2219
|
+
postIds: Yn(e, r),
|
|
2220
|
+
sort: qn(e),
|
|
2221
|
+
cols: Wn(e),
|
|
2222
|
+
height: Gn(e),
|
|
2190
2223
|
order: t
|
|
2191
2224
|
};
|
|
2192
2225
|
}
|
|
2193
|
-
function
|
|
2194
|
-
let e = Q(), t =
|
|
2226
|
+
function Zn() {
|
|
2227
|
+
let e = Q(), t = te(e, "settings", "homeLayout"), { data: n, pending: r } = oe(t), i = A(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Un).map((e, t) => Xn(e, t)));
|
|
2195
2228
|
async function a(e) {
|
|
2196
2229
|
await K(t, {
|
|
2197
2230
|
columns: 12,
|
|
2198
2231
|
blocks: e.map((e, t) => {
|
|
2199
|
-
let n = e, r =
|
|
2232
|
+
let n = e, r = Kn(n), i = An(r, e.count);
|
|
2200
2233
|
return {
|
|
2201
2234
|
id: e.id,
|
|
2202
2235
|
title: e.title.trim() || `섹션 ${t + 1}`,
|
|
2203
2236
|
showTitle: e.showTitle !== !1,
|
|
2204
2237
|
kind: r,
|
|
2205
|
-
boardId:
|
|
2238
|
+
boardId: Jn(n),
|
|
2206
2239
|
count: i,
|
|
2207
|
-
postIds:
|
|
2208
|
-
sort:
|
|
2209
|
-
cols:
|
|
2210
|
-
height:
|
|
2240
|
+
postIds: Yn(n, i),
|
|
2241
|
+
sort: qn(n),
|
|
2242
|
+
cols: Wn(n),
|
|
2243
|
+
height: Gn(n),
|
|
2211
2244
|
order: t
|
|
2212
2245
|
};
|
|
2213
2246
|
}),
|
|
@@ -2219,42 +2252,47 @@ function Un() {
|
|
|
2219
2252
|
pending: r,
|
|
2220
2253
|
sections: i,
|
|
2221
2254
|
blocks: i,
|
|
2222
|
-
hasSavedLayout:
|
|
2255
|
+
hasSavedLayout: A(() => Array.isArray(n.value?.blocks)),
|
|
2223
2256
|
saveSections: a,
|
|
2224
2257
|
saveBlocks: a
|
|
2225
2258
|
};
|
|
2226
2259
|
}
|
|
2227
2260
|
//#endregion
|
|
2228
2261
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2229
|
-
function
|
|
2230
|
-
let n = Q(), r = () =>
|
|
2231
|
-
let i = Math.max(1, Math.floor(Number(
|
|
2232
|
-
return a &&
|
|
2233
|
-
}), { data:
|
|
2262
|
+
function Qn(e, t) {
|
|
2263
|
+
let n = Q(), r = () => h(), i = N(e)?.trim() || "all", a = A(() => {
|
|
2264
|
+
let i = Math.max(1, Math.floor(Number(N(t)) || 5)), a = N(e)?.trim() || "", s = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
|
|
2265
|
+
return a && s.unshift(Y("boardId", "==", a)), W(o(n, r()), ...s, U("createdAt", "desc"), V(i));
|
|
2266
|
+
}), { data: s, pending: c, error: l } = ae(a, { ssrKey: S(r(), `mini-list/${i}`) });
|
|
2234
2267
|
return {
|
|
2235
|
-
posts:
|
|
2236
|
-
pending:
|
|
2268
|
+
posts: A(() => s.value ?? []),
|
|
2269
|
+
pending: c,
|
|
2237
2270
|
error: l
|
|
2238
2271
|
};
|
|
2239
2272
|
}
|
|
2240
|
-
function
|
|
2241
|
-
let t = Q(), n = () =>
|
|
2273
|
+
function $n(e) {
|
|
2274
|
+
let t = Q(), n = () => h(), r = A(() => [...new Set(N(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = A(() => r.value.length ? W(o(t, n()), Y(L(), "in", r.value)) : null), { data: s, pending: c, error: l } = ae(a, { ssrKey: S(n(), `mini-picked/${i}`) });
|
|
2242
2275
|
return {
|
|
2243
|
-
posts:
|
|
2244
|
-
let e = new Map((
|
|
2276
|
+
posts: A(() => {
|
|
2277
|
+
let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
|
|
2245
2278
|
for (let n of r.value) {
|
|
2246
2279
|
let r = e.get(n);
|
|
2247
2280
|
r && r.isPublished !== !1 && t.push(r);
|
|
2248
2281
|
}
|
|
2249
2282
|
return t;
|
|
2250
2283
|
}),
|
|
2251
|
-
pending:
|
|
2284
|
+
pending: c,
|
|
2252
2285
|
error: l
|
|
2253
2286
|
};
|
|
2254
2287
|
}
|
|
2255
2288
|
//#endregion
|
|
2256
2289
|
//#region src/data/versionHistory.ts
|
|
2257
|
-
var
|
|
2290
|
+
var er = [
|
|
2291
|
+
{
|
|
2292
|
+
version: "0.44.0",
|
|
2293
|
+
date: "2026-10-01",
|
|
2294
|
+
highlights: ["글을 저장하고 미리보기가 뜨기까지 기다리는 시간이 줄었어요 (특히 iPhone·Mac Safari)"]
|
|
2295
|
+
},
|
|
2258
2296
|
{
|
|
2259
2297
|
version: "0.42.0",
|
|
2260
2298
|
date: "2026-08-27",
|
|
@@ -2524,7 +2562,7 @@ var Kn = [
|
|
|
2524
2562
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
2525
2563
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
2526
2564
|
}
|
|
2527
|
-
],
|
|
2565
|
+
], tr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2528
2566
|
(function(n, r) {
|
|
2529
2567
|
typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs = r();
|
|
2530
2568
|
})(e, (function() {
|
|
@@ -2818,7 +2856,7 @@ var Kn = [
|
|
|
2818
2856
|
return w(1e3 * e);
|
|
2819
2857
|
}, w.en = b[y], w.Ls = b, w.p = {}, w;
|
|
2820
2858
|
}));
|
|
2821
|
-
})),
|
|
2859
|
+
})), nr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2822
2860
|
(function(n, r) {
|
|
2823
2861
|
typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_plugin_relativeTime = r();
|
|
2824
2862
|
})(e, (function() {
|
|
@@ -2922,9 +2960,9 @@ var Kn = [
|
|
|
2922
2960
|
};
|
|
2923
2961
|
};
|
|
2924
2962
|
}));
|
|
2925
|
-
})),
|
|
2963
|
+
})), rr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2926
2964
|
(function(n, r) {
|
|
2927
|
-
typeof e == "object" && t !== void 0 ? t.exports = r(
|
|
2965
|
+
typeof e == "object" && t !== void 0 ? t.exports = r(tr()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
|
|
2928
2966
|
})(e, (function(e) {
|
|
2929
2967
|
function t(e) {
|
|
2930
2968
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
@@ -2972,24 +3010,24 @@ var Kn = [
|
|
|
2972
3010
|
};
|
|
2973
3011
|
return n.default.locale(r, null, !0), r;
|
|
2974
3012
|
}));
|
|
2975
|
-
})),
|
|
2976
|
-
|
|
2977
|
-
function
|
|
3013
|
+
})), ir = /* @__PURE__ */ Me(tr(), 1), ar = /* @__PURE__ */ Me(nr(), 1);
|
|
3014
|
+
rr(), ir.default.extend(ar.default), ir.default.locale("ko");
|
|
3015
|
+
function or(e) {
|
|
2978
3016
|
return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
|
|
2979
3017
|
}
|
|
2980
|
-
function
|
|
2981
|
-
return e?.toDate ? (0,
|
|
3018
|
+
function sr(e, t = Date.now()) {
|
|
3019
|
+
return e?.toDate ? (0, ir.default)(e.toDate()).from((0, ir.default)(t)) : "방금 전";
|
|
2982
3020
|
}
|
|
2983
|
-
function
|
|
2984
|
-
return e?.toDate ? (0,
|
|
3021
|
+
function cr(e) {
|
|
3022
|
+
return e?.toDate ? (0, ir.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
|
|
2985
3023
|
}
|
|
2986
|
-
function
|
|
2987
|
-
let n =
|
|
2988
|
-
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${
|
|
3024
|
+
function lr(e, t) {
|
|
3025
|
+
let n = cr(e), r = e?.toMillis?.(), i = t?.toMillis?.();
|
|
3026
|
+
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${cr(t)}`];
|
|
2989
3027
|
}
|
|
2990
3028
|
//#endregion
|
|
2991
3029
|
//#region src/composables/useMemiBoardWritingAssistant.ts
|
|
2992
|
-
var
|
|
3030
|
+
var ur = 10, dr = 864e5, fr = 200, pr = {
|
|
2993
3031
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
2994
3032
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
2995
3033
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -2998,29 +3036,29 @@ var nr = 10, rr = 864e5, ir = 200, ar = {
|
|
|
2998
3036
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
2999
3037
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
3000
3038
|
};
|
|
3001
|
-
function
|
|
3002
|
-
if (e.action !== "custom") return
|
|
3003
|
-
let t = e.customInstruction?.trim().slice(0,
|
|
3039
|
+
function mr(e) {
|
|
3040
|
+
if (e.action !== "custom") return pr[e.action];
|
|
3041
|
+
let t = e.customInstruction?.trim().slice(0, fr);
|
|
3004
3042
|
if (!t) throw Error("어떻게 바꿀지 지시문을 입력해 주세요.");
|
|
3005
3043
|
return `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${t}`;
|
|
3006
3044
|
}
|
|
3007
|
-
function
|
|
3045
|
+
function hr(e) {
|
|
3008
3046
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
3009
3047
|
}
|
|
3010
|
-
function
|
|
3048
|
+
function gr(e) {
|
|
3011
3049
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
3012
3050
|
}
|
|
3013
|
-
function
|
|
3014
|
-
let e =
|
|
3051
|
+
function _r() {
|
|
3052
|
+
let e = se(), t = Q(), n = Z(), r = c();
|
|
3015
3053
|
async function i() {
|
|
3016
3054
|
let e = n.value?.uid;
|
|
3017
3055
|
if (!e) throw Error("AI 글쓰기 도우미는 로그인 후 사용할 수 있습니다.");
|
|
3018
|
-
await
|
|
3019
|
-
let r =
|
|
3056
|
+
await ie(t, async (n) => {
|
|
3057
|
+
let r = y(t, h(), e), i = await n.get(r);
|
|
3020
3058
|
if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
|
|
3021
|
-
let a = i.data(), o =
|
|
3059
|
+
let a = i.data(), o = gr(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < dr;
|
|
3022
3060
|
if (l && s >= 10) {
|
|
3023
|
-
let e = o +
|
|
3061
|
+
let e = o + dr, t = Math.max(1, Math.ceil((e - c) / 36e5));
|
|
3024
3062
|
throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
|
|
3025
3063
|
}
|
|
3026
3064
|
n.update(r, {
|
|
@@ -3031,13 +3069,13 @@ function lr() {
|
|
|
3031
3069
|
});
|
|
3032
3070
|
}
|
|
3033
3071
|
async function a(t) {
|
|
3034
|
-
let n =
|
|
3072
|
+
let n = mr(t);
|
|
3035
3073
|
if (!t.content.trim()) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
|
|
3036
3074
|
await i();
|
|
3037
|
-
let a = r.moderation ?? {}, o =
|
|
3038
|
-
backend: new
|
|
3075
|
+
let a = r.moderation ?? {}, o = Se(e, {
|
|
3076
|
+
backend: new be(),
|
|
3039
3077
|
useLimitedUseAppCheckTokens: a.useLimitedUseAppCheckTokens !== !1
|
|
3040
|
-
}), s =
|
|
3078
|
+
}), s = Ce(o, {
|
|
3041
3079
|
model: a.model ?? "gemini-3.5-flash-lite",
|
|
3042
3080
|
systemInstruction: [
|
|
3043
3081
|
"너는 게시판 글쓰기 편집 도우미다.",
|
|
@@ -3052,7 +3090,7 @@ function lr() {
|
|
|
3052
3090
|
`작업: ${n}`,
|
|
3053
3091
|
t.title ? `현재 제목: ${t.title}` : "",
|
|
3054
3092
|
`본문:\n${c.slice(0, 12e3)}`
|
|
3055
|
-
].filter(Boolean).join("\n\n"), u =
|
|
3093
|
+
].filter(Boolean).join("\n\n"), u = hr((await s.generateContent(l)).response.text());
|
|
3056
3094
|
if (!u) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
|
|
3057
3095
|
return u;
|
|
3058
3096
|
}
|
|
@@ -3060,14 +3098,14 @@ function lr() {
|
|
|
3060
3098
|
}
|
|
3061
3099
|
//#endregion
|
|
3062
3100
|
//#region src/utils/renderMarkdown.ts
|
|
3063
|
-
function
|
|
3101
|
+
function vr(e) {
|
|
3064
3102
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3065
3103
|
}
|
|
3066
|
-
function
|
|
3104
|
+
function yr(e) {
|
|
3067
3105
|
let t = e;
|
|
3068
3106
|
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;
|
|
3069
3107
|
}
|
|
3070
|
-
function
|
|
3108
|
+
function br(e) {
|
|
3071
3109
|
if (!e?.trim()) return "";
|
|
3072
3110
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
3073
3111
|
function c() {
|
|
@@ -3076,7 +3114,7 @@ function fr(e) {
|
|
|
3076
3114
|
return;
|
|
3077
3115
|
}
|
|
3078
3116
|
let e = o;
|
|
3079
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
3117
|
+
n.push(`<${e}>${s.map((e) => `<li>${yr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
3080
3118
|
}
|
|
3081
3119
|
for (; r < t.length;) {
|
|
3082
3120
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -3085,7 +3123,7 @@ function fr(e) {
|
|
|
3085
3123
|
continue;
|
|
3086
3124
|
}
|
|
3087
3125
|
if (i) {
|
|
3088
|
-
a.push(
|
|
3126
|
+
a.push(vr(e)), r++;
|
|
3089
3127
|
continue;
|
|
3090
3128
|
}
|
|
3091
3129
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -3096,24 +3134,24 @@ function fr(e) {
|
|
|
3096
3134
|
if (u) {
|
|
3097
3135
|
c();
|
|
3098
3136
|
let e = u[1].length;
|
|
3099
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${
|
|
3137
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${yr(vr(u[2]))}</h${e}>`), r++;
|
|
3100
3138
|
continue;
|
|
3101
3139
|
}
|
|
3102
3140
|
if (e.startsWith("> ")) {
|
|
3103
3141
|
c();
|
|
3104
3142
|
let e = [];
|
|
3105
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
3106
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
3143
|
+
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(vr((t[r] ?? "").slice(2))), r++;
|
|
3144
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${yr(e.join("<br />"))}</blockquote>`);
|
|
3107
3145
|
continue;
|
|
3108
3146
|
}
|
|
3109
3147
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
3110
3148
|
if (d) {
|
|
3111
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
3149
|
+
o && o !== "ul" && c(), o = "ul", s.push(vr(d[1])), r++;
|
|
3112
3150
|
continue;
|
|
3113
3151
|
}
|
|
3114
3152
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
3115
3153
|
if (f) {
|
|
3116
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
3154
|
+
o && o !== "ol" && c(), o = "ol", s.push(vr(f[1])), r++;
|
|
3117
3155
|
continue;
|
|
3118
3156
|
}
|
|
3119
3157
|
if (!e.trim()) {
|
|
@@ -3125,13 +3163,13 @@ function fr(e) {
|
|
|
3125
3163
|
for (; r < t.length;) {
|
|
3126
3164
|
let e = t[r] ?? "";
|
|
3127
3165
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
3128
|
-
p.push(
|
|
3166
|
+
p.push(vr(e)), r++;
|
|
3129
3167
|
}
|
|
3130
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
3168
|
+
n.push(`<p class="my-2 leading-relaxed">${yr(p.join("<br />"))}</p>`);
|
|
3131
3169
|
}
|
|
3132
3170
|
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");
|
|
3133
3171
|
}
|
|
3134
3172
|
//#endregion
|
|
3135
|
-
export {
|
|
3173
|
+
export { Jt as AUTHOR_MEMO_MAX_LENGTH, En as BOARD_SECTION_COLS, Dn as BOARD_SECTION_KINDS, On as BOARD_SECTION_SORTS, Cn as BOARD_USER_ROLES, $t as COMMENT_BODY_MAX_LENGTH, et as DEFAULT_BLOCK_BAN_DECAY_MS, $e as DEFAULT_BLOCK_BAN_THRESHOLD, At as DEFAULT_BOARDS, jt as DEFAULT_CATEGORIES, x as DEFAULT_COMMENTS_COLLECTION, w as DEFAULT_LIKES_COLLECTION, u as DEFAULT_POSTS_COLLECTION, v as DEFAULT_REPORTS_COLLECTION, T as DEFAULT_SETTINGS_COLLECTION, d as DEFAULT_USERS_COLLECTION, sn as REPORT_DETAIL_MAX_LENGTH, cn as REPORT_REASONS, ur as WRITING_ASSISTANT_DAILY_LIMIT, Ne as asHttpUrl, ze as boardListOgDescription, Re as boardListOgTitle, Le as boardPostOgDescription, Ie as boardPostOgTitle, S as boardSsrKey, y as boardUserDoc, p as boardUsersCol, kt as buildPostPreview, Je as canManageBoardByRole, Ye as canWriteCommentByRole, An as clampSectionCount, s as commentDoc, E as commentsCol, C as configureMemiBoard, kn as defaultSectionCount, tt as effectiveModerationBlockCount, lt as extractEditorImageUrls, Ge as fetchPublicListForSeo, We as fetchPublicPostForSeo, or as formatDate, cr as formatFullDate, sr as formatRelativeDate, it as formatRestrictedUntilLabel, lr as formatTimestampDetails, wt as hasBodyImage, Ct as hasBodyText, qe as isAssignedStaff, Tt as isContentEmpty, It as isFirestorePermissionDenied, rt as isModerationWriteRestricted, k as likeDoc, l as likeDocId, O as likesCol, an as memiBoardCommentLikesKey, Vn as miniBoardView, In as miniCommentLink, Fn as miniMoreLink, Ln as miniPostImage, Pn as miniPostLink, Rn as miniPostTitle, Hn as miniPostsView, zn as miniViewGroup, Bn as miniViewLabel, nt as moderationWriteRestrictedUntilMs, Be as normalizeBasePath, Xn as normalizeBoardSection, jn as pairSectionCols, xt as plainTextFromHtml, a as postBodyDoc, t as postDoc, dt as postNamespaceFromStoragePath, D as postStorageFolder, o as postsCol, br as renderMarkdownToHtml, r as reportDoc, n as reportDocId, ln as reportReasonLabel, i as reportsCol, _ as resolveBoardPathConfig, Ve as resolvePublicSeoDb, Mn as sectionColClass, Nn as sectionMinHeight, e as settingsCol, g as settingsDoc, b as slugify, ut as storagePathFromDownloadUrl, St as titleFromBody, Fe as toAbsoluteUrl, Pe as toBoardOgImageUrl, h as useBoardPathConfig, ct as useMemiBoardAuth, Xt as useMemiBoardAuthorMemo, qt as useMemiBoardCommentFeed, on as useMemiBoardCommentLikes, tn as useMemiBoardComments, c as useMemiBoardConfig, rn as useMemiBoardLikes, Qn as useMemiBoardMiniLatest, $n as useMemiBoardMiniPicked, Sn as useMemiBoardModeration, Vt as useMemiBoardPost, Bt as useMemiBoardPostList, Rt as useMemiBoardPosts, nn as useMemiBoardReplies, fn as useMemiBoardReportQueue, un as useMemiBoardReports, Zn as useMemiBoardSections, Pt as useMemiBoardSettings, Gt as useMemiBoardUserComments, Ut as useMemiBoardUserPosts, Tn as useMemiBoardUserProfile, wn as useMemiBoardUsers, pn as useMemiBoardViews, _r as useMemiBoardWritingAssistant, er as versionHistory, Ot as videoListCoverUrl, Dt as youtubeId };
|
|
3136
3174
|
|
|
3137
3175
|
//# sourceMappingURL=index.js.map
|