memi-board 0.43.0 → 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/useMemiBoardPosts.d.ts +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +384 -358
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/Editor.vue +5 -1
- package/dist/runtime/composables/useMemiBoardSeo.ts +9 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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
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
|
|
4
|
-
import { useCollection as
|
|
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
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
6
|
import { deleteObject as ge, getStorage as _e, listAll as ve, ref as ye } from "firebase/storage";
|
|
7
7
|
import { GoogleAIBackend as be, Schema as xe, getAI as Se, getGenerativeModel as Ce } from "firebase/ai";
|
|
@@ -76,7 +76,7 @@ function Ve() {
|
|
|
76
76
|
}
|
|
77
77
|
async function He(e, t) {
|
|
78
78
|
try {
|
|
79
|
-
let n = await
|
|
79
|
+
let n = await R(g(e.db, e.paths, t));
|
|
80
80
|
if (!n.exists()) return {
|
|
81
81
|
label: t,
|
|
82
82
|
description: "",
|
|
@@ -113,7 +113,7 @@ async function We(e, t, n) {
|
|
|
113
113
|
let s = await He(a, r);
|
|
114
114
|
if (s.hidden) return null;
|
|
115
115
|
try {
|
|
116
|
-
let e = (await
|
|
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];
|
|
117
117
|
if (!e) return null;
|
|
118
118
|
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title.trim() : "";
|
|
119
119
|
return n ? {
|
|
@@ -165,7 +165,7 @@ async function Ge(e, t) {
|
|
|
165
165
|
async function Ke(e, t) {
|
|
166
166
|
let n = [], r = null;
|
|
167
167
|
try {
|
|
168
|
-
let i = await
|
|
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)));
|
|
169
169
|
for (let e of i.docs) {
|
|
170
170
|
let t = e.data() ?? {}, i = typeof t.title == "string" ? t.title.trim() : "";
|
|
171
171
|
i && n.length < 5 && n.push(i), r ||= Pe(t.previewImage);
|
|
@@ -254,7 +254,7 @@ function st() {
|
|
|
254
254
|
}), e[ot];
|
|
255
255
|
}
|
|
256
256
|
function ct() {
|
|
257
|
-
let e = c(), t = st(), n = Q(), r = ce(), i =
|
|
257
|
+
let e = c(), t = st(), n = Q(), r = ce(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: l } = t;
|
|
258
258
|
function u() {
|
|
259
259
|
let t = e.moderation?.blockBanThreshold;
|
|
260
260
|
return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
|
|
@@ -274,22 +274,22 @@ function ct() {
|
|
|
274
274
|
s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), l.value = at(e.moderationBlockAt);
|
|
275
275
|
}
|
|
276
276
|
async function g(e) {
|
|
277
|
-
let t = f(e.uid), r = m(n, h(), e.uid), i = await
|
|
278
|
-
if (!i.exists()) return await Promise.all([
|
|
277
|
+
let t = f(e.uid), r = m(n, h(), e.uid), i = await R(t);
|
|
278
|
+
if (!i.exists()) return await Promise.all([K(t, {
|
|
279
279
|
role: "user",
|
|
280
280
|
displayName: e.displayName,
|
|
281
281
|
photoURL: e.photoURL,
|
|
282
|
-
joinedAt:
|
|
283
|
-
lastVisitAt:
|
|
284
|
-
updatedAt:
|
|
285
|
-
}),
|
|
282
|
+
joinedAt: G(),
|
|
283
|
+
lastVisitAt: G(),
|
|
284
|
+
updatedAt: G()
|
|
285
|
+
}), K(r, { email: e.email })]), p({ moderationBlockCount: 0 }), "user";
|
|
286
286
|
let a = i.data();
|
|
287
287
|
p(a);
|
|
288
288
|
let o = typeof a.displayName == "string" && a.displayName.trim(), s = typeof a.photoURL == "string" && a.photoURL.trim(), c = {
|
|
289
|
-
lastVisitAt:
|
|
290
|
-
updatedAt:
|
|
289
|
+
lastVisitAt: G(),
|
|
290
|
+
updatedAt: G()
|
|
291
291
|
};
|
|
292
|
-
return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([
|
|
292
|
+
return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([J(t, c), K(r, { email: e.email }, { merge: !0 })]), [
|
|
293
293
|
"admin",
|
|
294
294
|
"staff",
|
|
295
295
|
"user"
|
|
@@ -301,7 +301,7 @@ function ct() {
|
|
|
301
301
|
p(void 0);
|
|
302
302
|
return;
|
|
303
303
|
}
|
|
304
|
-
let t = await
|
|
304
|
+
let t = await R(f(e));
|
|
305
305
|
p(t.exists() ? t.data() : void 0);
|
|
306
306
|
}
|
|
307
307
|
t.watchStarted || (t.watchStarted = !0, P(i, async (e) => {
|
|
@@ -337,17 +337,17 @@ function ct() {
|
|
|
337
337
|
restricted: !1,
|
|
338
338
|
messageSuffix: ""
|
|
339
339
|
};
|
|
340
|
-
let n = f(e), r = await
|
|
341
|
-
r.exists() ? await
|
|
342
|
-
moderationBlockCount:
|
|
343
|
-
moderationBlockAt:
|
|
344
|
-
updatedAt:
|
|
345
|
-
}) : await
|
|
340
|
+
let n = f(e), r = await R(n);
|
|
341
|
+
r.exists() ? await J(n, {
|
|
342
|
+
moderationBlockCount: B(1),
|
|
343
|
+
moderationBlockAt: G(),
|
|
344
|
+
updatedAt: G()
|
|
345
|
+
}) : await K(n, {
|
|
346
346
|
role: "user",
|
|
347
347
|
displayName: i.value?.displayName ?? null,
|
|
348
348
|
moderationBlockCount: 1,
|
|
349
|
-
moderationBlockAt:
|
|
350
|
-
updatedAt:
|
|
349
|
+
moderationBlockAt: G(),
|
|
350
|
+
updatedAt: G()
|
|
351
351
|
});
|
|
352
352
|
let a = (r.exists() ? Math.max(0, Math.floor(Number(r.data().moderationBlockCount) || 0)) : 0) + 1, o = Date.now();
|
|
353
353
|
s.value = a, l.value = o;
|
|
@@ -389,10 +389,10 @@ function ct() {
|
|
|
389
389
|
function I(e) {
|
|
390
390
|
return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
|
|
391
391
|
}
|
|
392
|
-
function
|
|
392
|
+
function te(e) {
|
|
393
393
|
return v.value && i.value?.uid === e.authorUid;
|
|
394
394
|
}
|
|
395
|
-
function
|
|
395
|
+
function L(e) {
|
|
396
396
|
return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
|
|
397
397
|
}
|
|
398
398
|
return {
|
|
@@ -414,8 +414,8 @@ function ct() {
|
|
|
414
414
|
signOut: N,
|
|
415
415
|
canEdit: I,
|
|
416
416
|
canDelete: I,
|
|
417
|
-
canEditComment:
|
|
418
|
-
canDeleteComment:
|
|
417
|
+
canEditComment: te,
|
|
418
|
+
canDeleteComment: L,
|
|
419
419
|
recordContentModerationBlock: D,
|
|
420
420
|
refreshBoardUser: _
|
|
421
421
|
};
|
|
@@ -461,26 +461,26 @@ async function mt(e, t) {
|
|
|
461
461
|
}
|
|
462
462
|
var ht = 450;
|
|
463
463
|
async function gt(e, t) {
|
|
464
|
-
let n = await
|
|
464
|
+
let n = await z(t);
|
|
465
465
|
for (let t = 0; t < n.docs.length; t += ht) {
|
|
466
|
-
let r =
|
|
466
|
+
let r = X(e);
|
|
467
467
|
n.docs.slice(t, t + ht).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
async function _t(e, t, n) {
|
|
471
|
-
await gt(e,
|
|
471
|
+
await gt(e, W(O(e, t), Y("postId", "==", n)));
|
|
472
472
|
}
|
|
473
473
|
async function vt(e, t, n) {
|
|
474
|
-
await gt(e,
|
|
474
|
+
await gt(e, W(i(e, t), Y("postId", "==", n)));
|
|
475
475
|
}
|
|
476
476
|
async function yt(e, t, n) {
|
|
477
|
-
await gt(e,
|
|
477
|
+
await gt(e, W(O(e, t), Y("commentId", "==", n)));
|
|
478
478
|
}
|
|
479
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([
|
|
480
|
+
let o = t(e, r, i), s = a(e, r, i), [c, l] = await Promise.all([R(o), R(s)]);
|
|
481
481
|
if (!c.exists()) return;
|
|
482
482
|
let u = c.data(), d = l.exists() ? String(l.data().content ?? "") : "";
|
|
483
|
-
await gt(e,
|
|
483
|
+
await gt(e, W(E(e, r), Y("postId", "==", i))), await _t(e, r, i), await vt(e, r, i);
|
|
484
484
|
let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
485
485
|
for (let e of u.attachments ?? []) {
|
|
486
486
|
if (!e?.path) continue;
|
|
@@ -599,7 +599,7 @@ function Nt(e, t, n) {
|
|
|
599
599
|
};
|
|
600
600
|
}
|
|
601
601
|
function Pt() {
|
|
602
|
-
let t = () => h(), n = Q(), r = se(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = ct(), l = A(() =>
|
|
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
603
|
function v(e) {
|
|
604
604
|
if (e) return f.value.find((t) => t.id === e)?.label ?? e;
|
|
605
605
|
}
|
|
@@ -627,9 +627,9 @@ function Pt() {
|
|
|
627
627
|
async function M(e, t, r) {
|
|
628
628
|
let i;
|
|
629
629
|
for (;;) {
|
|
630
|
-
let a = await
|
|
630
|
+
let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
|
|
631
631
|
if (a.empty) break;
|
|
632
|
-
let o =
|
|
632
|
+
let o = X(n), s = 0;
|
|
633
633
|
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
634
634
|
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
635
635
|
}
|
|
@@ -648,7 +648,7 @@ function Pt() {
|
|
|
648
648
|
commentWriteRole: e.commentWriteRole ?? "user",
|
|
649
649
|
allowedStaffUids: e.allowedStaffUids ?? [],
|
|
650
650
|
order: t,
|
|
651
|
-
updatedAt:
|
|
651
|
+
updatedAt: G()
|
|
652
652
|
};
|
|
653
653
|
}
|
|
654
654
|
async function I(e, r = e.order ?? 0) {
|
|
@@ -660,12 +660,12 @@ function Pt() {
|
|
|
660
660
|
label: o,
|
|
661
661
|
visibility: s
|
|
662
662
|
}, r);
|
|
663
|
-
await
|
|
663
|
+
await K(g(n, t(), a), c, { merge: !0 }), await N(a, s !== "hidden");
|
|
664
664
|
}
|
|
665
|
-
let
|
|
666
|
-
async function
|
|
665
|
+
let te = I;
|
|
666
|
+
async function L(e) {
|
|
667
667
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
668
|
-
let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a =
|
|
668
|
+
let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
|
|
669
669
|
e.forEach((e, i) => {
|
|
670
670
|
let s = e.id.trim(), c = e.label.trim();
|
|
671
671
|
if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
@@ -681,23 +681,23 @@ function Pt() {
|
|
|
681
681
|
}), await a.commit();
|
|
682
682
|
for (let e of o) await N(e.id, e.listed);
|
|
683
683
|
}
|
|
684
|
-
let
|
|
685
|
-
async function
|
|
684
|
+
let ne = L;
|
|
685
|
+
async function R(e) {
|
|
686
686
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
687
687
|
let a = e.trim();
|
|
688
688
|
if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
|
|
689
689
|
for (;;) {
|
|
690
|
-
let e = await
|
|
690
|
+
let e = await z(W(o(n, t()), Y("boardId", "==", a), V(25)));
|
|
691
691
|
if (e.empty) break;
|
|
692
692
|
for (let i of e.docs) await bt(n, _e(r), t(), i.id);
|
|
693
693
|
}
|
|
694
694
|
await F(g(n, t(), e));
|
|
695
695
|
}
|
|
696
|
-
let
|
|
697
|
-
async function
|
|
696
|
+
let B = R;
|
|
697
|
+
async function re() {
|
|
698
698
|
return Promise.resolve();
|
|
699
699
|
}
|
|
700
|
-
async function
|
|
700
|
+
async function H(e) {
|
|
701
701
|
let t = e.trim();
|
|
702
702
|
if (!t) throw Error("보드 이름을 입력해 주세요.");
|
|
703
703
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
@@ -733,33 +733,34 @@ function Pt() {
|
|
|
733
733
|
categoryVisibility: T,
|
|
734
734
|
isCategoryHidden: O,
|
|
735
735
|
settingsPending: d,
|
|
736
|
-
ensureSettings:
|
|
737
|
-
addBoard:
|
|
738
|
-
addCategory:
|
|
736
|
+
ensureSettings: re,
|
|
737
|
+
addBoard: H,
|
|
738
|
+
addCategory: H,
|
|
739
739
|
saveBoard: I,
|
|
740
|
-
saveCategory:
|
|
741
|
-
saveBoards:
|
|
742
|
-
saveCategories:
|
|
743
|
-
deleteBoard:
|
|
744
|
-
deleteCategory:
|
|
740
|
+
saveCategory: te,
|
|
741
|
+
saveBoards: L,
|
|
742
|
+
saveCategories: ne,
|
|
743
|
+
deleteBoard: R,
|
|
744
|
+
deleteCategory: B,
|
|
745
745
|
DEFAULT_BOARDS: At,
|
|
746
746
|
DEFAULT_CATEGORIES: jt
|
|
747
747
|
};
|
|
748
748
|
}
|
|
749
749
|
//#endregion
|
|
750
750
|
//#region src/composables/useMemiBoardPosts.ts
|
|
751
|
-
|
|
751
|
+
var Ft = /* @__PURE__ */ new Map();
|
|
752
|
+
function It(e) {
|
|
752
753
|
if (!e || typeof e != "object") return !1;
|
|
753
754
|
let t = "code" in e ? String(e.code) : "";
|
|
754
755
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
755
756
|
}
|
|
756
|
-
function
|
|
757
|
+
function Lt(e) {
|
|
757
758
|
let t = N(e)?.trim();
|
|
758
759
|
if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
|
|
759
760
|
return t;
|
|
760
761
|
}
|
|
761
|
-
function
|
|
762
|
-
let n = Q(), r = se(), { isBoardHidden: i, getBoard: s } = Pt(), c = () => h(), l = () =>
|
|
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);
|
|
763
764
|
function p() {
|
|
764
765
|
return !i(l());
|
|
765
766
|
}
|
|
@@ -776,12 +777,12 @@ function Lt(e) {
|
|
|
776
777
|
if (!e.title?.trim()) throw Error("제목을 입력해 주세요.");
|
|
777
778
|
}
|
|
778
779
|
function _() {
|
|
779
|
-
return
|
|
780
|
+
return te(u()).id;
|
|
780
781
|
}
|
|
781
782
|
async function v(e = {}) {
|
|
782
|
-
let t = e.pageSize ?? 20, n = [
|
|
783
|
-
e.publicOnly !== !1 && n.push(
|
|
784
|
-
let r = await
|
|
783
|
+
let t = e.pageSize ?? 20, n = [Y("boardId", "==", l()), Y("isPublished", "==", !0)];
|
|
784
|
+
e.publicOnly !== !1 && n.push(Y("listed", "==", !0)), n.push(U("createdAt", "desc")), e.cursor && n.push(q(e.cursor)), n.push(V(t + 1));
|
|
785
|
+
let r = await z(W(u(), ...n)), i = r.docs.slice(0, t);
|
|
785
786
|
return {
|
|
786
787
|
posts: i.map((e) => ({
|
|
787
788
|
id: e.id,
|
|
@@ -792,7 +793,7 @@ function Lt(e) {
|
|
|
792
793
|
};
|
|
793
794
|
}
|
|
794
795
|
async function y(e) {
|
|
795
|
-
let [t, n] = await Promise.all([
|
|
796
|
+
let [t, n] = await Promise.all([R(d(e)), R(f(e))]);
|
|
796
797
|
return t.exists() ? {
|
|
797
798
|
id: t.id,
|
|
798
799
|
...t.data(),
|
|
@@ -801,7 +802,7 @@ function Lt(e) {
|
|
|
801
802
|
}
|
|
802
803
|
async function x(e) {
|
|
803
804
|
try {
|
|
804
|
-
let t = (await
|
|
805
|
+
let t = (await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", e.trim()), V(1)))).docs[0];
|
|
805
806
|
if (!t) return { status: "not-found" };
|
|
806
807
|
try {
|
|
807
808
|
let e = await y(t.id);
|
|
@@ -810,30 +811,54 @@ function Lt(e) {
|
|
|
810
811
|
post: e
|
|
811
812
|
} : { status: "not-found" };
|
|
812
813
|
} catch (e) {
|
|
813
|
-
return
|
|
814
|
+
return It(e) ? { status: "permission-denied" } : {
|
|
814
815
|
status: "error",
|
|
815
816
|
message: e instanceof Error ? e.message : String(e)
|
|
816
817
|
};
|
|
817
818
|
}
|
|
818
819
|
} catch (e) {
|
|
819
|
-
return
|
|
820
|
+
return It(e) ? { status: "permission-denied" } : {
|
|
820
821
|
status: "error",
|
|
821
822
|
message: e instanceof Error ? e.message : String(e)
|
|
822
823
|
};
|
|
823
824
|
}
|
|
824
825
|
}
|
|
825
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) {
|
|
826
851
|
if (!e.createdAt) return {
|
|
827
852
|
previous: null,
|
|
828
853
|
next: null
|
|
829
854
|
};
|
|
830
855
|
try {
|
|
831
856
|
let t = [
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
...e.listed === !1 ? [] : [
|
|
835
|
-
|
|
836
|
-
], [n, r] = await Promise.all([
|
|
857
|
+
Y("boardId", "==", l()),
|
|
858
|
+
Y("isPublished", "==", !0),
|
|
859
|
+
...e.listed === !1 ? [] : [Y("listed", "==", !0)],
|
|
860
|
+
U("createdAt", "desc")
|
|
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) => {
|
|
837
862
|
let t = e.docs[0];
|
|
838
863
|
return t ? {
|
|
839
864
|
id: t.id,
|
|
@@ -851,43 +876,36 @@ function Lt(e) {
|
|
|
851
876
|
};
|
|
852
877
|
}
|
|
853
878
|
}
|
|
854
|
-
async function
|
|
879
|
+
async function T(e) {
|
|
855
880
|
g(e);
|
|
856
|
-
let t = e.postId || _(),
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
let t = b(i) || "post";
|
|
861
|
-
r = t;
|
|
862
|
-
let n = 1;
|
|
863
|
-
for (; !(await B(G(u(), X("boardId", "==", l()), X("slug", "==", r), H(1)))).empty;) n++, r = `${t}-${n}`;
|
|
864
|
-
}
|
|
865
|
-
let a = kt(e.content, e.attachments), o = p();
|
|
866
|
-
return await q(d(t), {
|
|
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), {
|
|
867
885
|
boardId: l(),
|
|
868
|
-
slug:
|
|
869
|
-
title:
|
|
870
|
-
...
|
|
886
|
+
slug: i,
|
|
887
|
+
title: a,
|
|
888
|
+
...o,
|
|
871
889
|
tags: e.tags ?? [],
|
|
872
890
|
attachments: e.attachments ?? [],
|
|
873
891
|
commentCount: 0,
|
|
874
892
|
likeCount: 0,
|
|
875
893
|
viewCount: 0,
|
|
876
|
-
listed:
|
|
894
|
+
listed: s,
|
|
877
895
|
isPublished: !1,
|
|
878
896
|
authorUid: e.authorUid,
|
|
879
897
|
authorName: e.authorName,
|
|
880
898
|
authorPhoto: e.authorPhoto,
|
|
881
899
|
moderationStatus: "approved",
|
|
882
900
|
moderationModel: e.moderationModel ?? null,
|
|
883
|
-
createdAt:
|
|
884
|
-
updatedAt:
|
|
885
|
-
}),
|
|
901
|
+
createdAt: G(),
|
|
902
|
+
updatedAt: G()
|
|
903
|
+
}), c.set(f(t), { content: e.content }), await c.commit(), Ft.set(`${l()}/${i}`, t), i;
|
|
886
904
|
}
|
|
887
|
-
async function
|
|
905
|
+
async function E(e, t) {
|
|
888
906
|
g(t);
|
|
889
907
|
let n = m() ? St(t.content) : t.title.trim(), r = kt(t.content, t.attachments), i = p();
|
|
890
|
-
await Promise.all([
|
|
908
|
+
await Promise.all([J(d(e), {
|
|
891
909
|
title: n,
|
|
892
910
|
...r,
|
|
893
911
|
previewImage: r.previewImage ? r.previewImage : I(),
|
|
@@ -895,34 +913,37 @@ function Lt(e) {
|
|
|
895
913
|
tags: t.tags ?? [],
|
|
896
914
|
listed: i,
|
|
897
915
|
attachments: t.attachments ?? [],
|
|
898
|
-
updatedAt:
|
|
899
|
-
}),
|
|
916
|
+
updatedAt: G()
|
|
917
|
+
}), K(f(e), { content: t.content }, { merge: !0 })]);
|
|
900
918
|
}
|
|
901
|
-
async function
|
|
902
|
-
await
|
|
919
|
+
async function D(e) {
|
|
920
|
+
await J(d(e), {
|
|
903
921
|
isPublished: !0,
|
|
904
|
-
publishedAt:
|
|
905
|
-
updatedAt:
|
|
922
|
+
publishedAt: G(),
|
|
923
|
+
updatedAt: G()
|
|
906
924
|
});
|
|
907
925
|
}
|
|
908
|
-
async function
|
|
926
|
+
async function O(e) {
|
|
909
927
|
await bt(n, _e(r), c(), e);
|
|
928
|
+
for (let [t, n] of Ft) n === e && Ft.delete(t);
|
|
910
929
|
}
|
|
911
930
|
return {
|
|
912
931
|
createPostId: _,
|
|
913
932
|
getPosts: v,
|
|
914
933
|
getPost: y,
|
|
915
934
|
getPostBySlug: x,
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
935
|
+
resolvePostIdBySlug: S,
|
|
936
|
+
resolveUniqueSlug: C,
|
|
937
|
+
getAdjacentPosts: w,
|
|
938
|
+
createPost: T,
|
|
939
|
+
updatePost: E,
|
|
940
|
+
publishPost: D,
|
|
941
|
+
deletePost: O
|
|
921
942
|
};
|
|
922
943
|
}
|
|
923
|
-
var
|
|
924
|
-
function
|
|
925
|
-
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = ct(), s = () => h(), c = t.pageSize ??
|
|
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;
|
|
926
947
|
function T(e) {
|
|
927
948
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
928
949
|
}
|
|
@@ -936,11 +957,11 @@ function zt(e, t = {}) {
|
|
|
936
957
|
return [...e.values()].sort((e, t) => T(t) - T(e) || String(t.id).localeCompare(String(e.id)));
|
|
937
958
|
});
|
|
938
959
|
function D() {
|
|
939
|
-
let e = [
|
|
940
|
-
return f.value && e.push(
|
|
960
|
+
let e = [Y("boardId", "==", u.value), Y("isPublished", "==", !0)];
|
|
961
|
+
return f.value && e.push(Y("listed", "==", !0)), e;
|
|
941
962
|
}
|
|
942
963
|
function O() {
|
|
943
|
-
C?.(), C =
|
|
964
|
+
C?.(), C = H(W(d(), ...D(), U("createdAt", "desc"), U(L(), "desc"), V(c)), (e) => {
|
|
944
965
|
p.value = e.docs.map((e) => ({
|
|
945
966
|
id: e.id,
|
|
946
967
|
...e.data()
|
|
@@ -956,7 +977,7 @@ function zt(e, t = {}) {
|
|
|
956
977
|
g.value = [];
|
|
957
978
|
return;
|
|
958
979
|
}
|
|
959
|
-
w =
|
|
980
|
+
w = H(W(d(), Y("boardId", "==", u.value), Y("authorUid", "==", e), Y("isPublished", "==", !1), U("createdAt", "desc"), V(20)), (e) => {
|
|
960
981
|
g.value = e.docs.map((e) => ({
|
|
961
982
|
id: e.id,
|
|
962
983
|
...e.data()
|
|
@@ -971,11 +992,11 @@ function zt(e, t = {}) {
|
|
|
971
992
|
try {
|
|
972
993
|
let e = S ?? x, t = [
|
|
973
994
|
...D(),
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
...e ? [
|
|
977
|
-
|
|
978
|
-
], n = await
|
|
995
|
+
U("createdAt", "desc"),
|
|
996
|
+
U(L(), "desc"),
|
|
997
|
+
...e ? [q(e)] : [],
|
|
998
|
+
V(c + 1)
|
|
999
|
+
], n = await z(W(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
|
|
979
1000
|
id: e.id,
|
|
980
1001
|
...e.data()
|
|
981
1002
|
})), a = new Set([...m.value, ...p.value].map((e) => e.id));
|
|
@@ -1005,8 +1026,8 @@ function zt(e, t = {}) {
|
|
|
1005
1026
|
loadMore: ee
|
|
1006
1027
|
};
|
|
1007
1028
|
}
|
|
1008
|
-
function
|
|
1009
|
-
let r = Q(), i = () => h(), o = A(() =>
|
|
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);
|
|
1010
1031
|
return {
|
|
1011
1032
|
post: A(() => !u.value || u.value.boardId !== o.value ? null : {
|
|
1012
1033
|
...u.value,
|
|
@@ -1018,9 +1039,9 @@ function Bt(e, n) {
|
|
|
1018
1039
|
}
|
|
1019
1040
|
//#endregion
|
|
1020
1041
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
1021
|
-
var
|
|
1022
|
-
function
|
|
1023
|
-
let n = Q(), r = () => h(), i = () => o(n, r()), a = t.pageSize ??
|
|
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, _;
|
|
1024
1045
|
function v(e) {
|
|
1025
1046
|
return {
|
|
1026
1047
|
id: e.id,
|
|
@@ -1037,9 +1058,9 @@ function Ht(e, t = {}) {
|
|
|
1037
1058
|
});
|
|
1038
1059
|
function x() {
|
|
1039
1060
|
return [
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1061
|
+
Y("authorUid", "==", s.value),
|
|
1062
|
+
Y("isPublished", "==", !0),
|
|
1063
|
+
Y("listed", "==", !0)
|
|
1043
1064
|
];
|
|
1044
1065
|
}
|
|
1045
1066
|
function S() {
|
|
@@ -1047,7 +1068,7 @@ function Ht(e, t = {}) {
|
|
|
1047
1068
|
u.value = !1;
|
|
1048
1069
|
return;
|
|
1049
1070
|
}
|
|
1050
|
-
_ =
|
|
1071
|
+
_ = H(W(i(), ...x(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
|
|
1051
1072
|
c.value = e.docs.map(v), m = e.docs.at(-1) ?? null, g || (d.value = e.docs.length >= a), u.value = !1;
|
|
1052
1073
|
}, (e) => {
|
|
1053
1074
|
console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
|
|
@@ -1059,11 +1080,11 @@ function Ht(e, t = {}) {
|
|
|
1059
1080
|
try {
|
|
1060
1081
|
let e = g ?? m, t = [
|
|
1061
1082
|
...x(),
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
...e ? [
|
|
1065
|
-
|
|
1066
|
-
], n = await
|
|
1083
|
+
U("createdAt", "desc"),
|
|
1084
|
+
U(L(), "desc"),
|
|
1085
|
+
...e ? [q(e)] : [],
|
|
1086
|
+
V(a + 1)
|
|
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));
|
|
1067
1088
|
l.value.push(...o.filter((e) => !s.has(e.id))), g = r.at(-1) ?? g, d.value = n.docs.length > a;
|
|
1068
1089
|
} catch (e) {
|
|
1069
1090
|
p.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
|
|
@@ -1086,9 +1107,9 @@ function Ht(e, t = {}) {
|
|
|
1086
1107
|
}
|
|
1087
1108
|
//#endregion
|
|
1088
1109
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1089
|
-
var
|
|
1090
|
-
function
|
|
1091
|
-
let n = Q(), r = () => h(), i = () => E(n, r()), a = t.pageSize ??
|
|
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;
|
|
1092
1113
|
function _(e) {
|
|
1093
1114
|
return {
|
|
1094
1115
|
id: e.id,
|
|
@@ -1104,14 +1125,14 @@ function Wt(e, t = {}) {
|
|
|
1104
1125
|
return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
|
|
1105
1126
|
});
|
|
1106
1127
|
function b() {
|
|
1107
|
-
return [
|
|
1128
|
+
return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
|
|
1108
1129
|
}
|
|
1109
1130
|
function x() {
|
|
1110
1131
|
if (g?.(), !o.value) {
|
|
1111
1132
|
l.value = !1;
|
|
1112
1133
|
return;
|
|
1113
1134
|
}
|
|
1114
|
-
g =
|
|
1135
|
+
g = H(W(i(), ...b(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
|
|
1115
1136
|
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1116
1137
|
}, (e) => {
|
|
1117
1138
|
console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1123,11 +1144,11 @@ function Wt(e, t = {}) {
|
|
|
1123
1144
|
try {
|
|
1124
1145
|
let e = m ?? p, t = [
|
|
1125
1146
|
...b(),
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
...e ? [
|
|
1129
|
-
|
|
1130
|
-
], n = await
|
|
1147
|
+
U("createdAt", "desc"),
|
|
1148
|
+
U(L(), "desc"),
|
|
1149
|
+
...e ? [q(e)] : [],
|
|
1150
|
+
V(a + 1)
|
|
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));
|
|
1131
1152
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1132
1153
|
} catch (e) {
|
|
1133
1154
|
f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
@@ -1150,11 +1171,11 @@ function Wt(e, t = {}) {
|
|
|
1150
1171
|
}
|
|
1151
1172
|
//#endregion
|
|
1152
1173
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1153
|
-
var
|
|
1154
|
-
function
|
|
1174
|
+
var Kt = 10;
|
|
1175
|
+
function qt(e = "latest", t = {}) {
|
|
1155
1176
|
let n = Q(), r = () => h(), i = () => E(n, r()), a = A(() => {
|
|
1156
1177
|
let e = Math.floor(Number(N(t.pageSize)));
|
|
1157
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1178
|
+
return Number.isFinite(e) && e > 0 ? e : Kt;
|
|
1158
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;
|
|
1159
1180
|
function _(e) {
|
|
1160
1181
|
return {
|
|
@@ -1179,18 +1200,18 @@ function Kt(e = "latest", t = {}) {
|
|
|
1179
1200
|
});
|
|
1180
1201
|
function x() {
|
|
1181
1202
|
return o.value === "likes" ? [
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1203
|
+
Y("listed", "==", !0),
|
|
1204
|
+
U("likeCount", "desc"),
|
|
1205
|
+
U("createdAt", "desc"),
|
|
1206
|
+
U(L(), "desc")
|
|
1186
1207
|
] : [
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1208
|
+
Y("listed", "==", !0),
|
|
1209
|
+
U("createdAt", "desc"),
|
|
1210
|
+
U(L(), "desc")
|
|
1190
1211
|
];
|
|
1191
1212
|
}
|
|
1192
1213
|
function S() {
|
|
1193
|
-
g?.(), g =
|
|
1214
|
+
g?.(), g = H(W(i(), ...x(), V(a.value)), (e) => {
|
|
1194
1215
|
s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
|
|
1195
1216
|
}, (e) => {
|
|
1196
1217
|
console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1200,7 +1221,7 @@ function Kt(e = "latest", t = {}) {
|
|
|
1200
1221
|
if (!(d.value || !u.value || l.value)) {
|
|
1201
1222
|
d.value = !0, f.value = "";
|
|
1202
1223
|
try {
|
|
1203
|
-
let e = m ?? p, t = await
|
|
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));
|
|
1204
1225
|
c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
|
|
1205
1226
|
} catch (e) {
|
|
1206
1227
|
f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
@@ -1224,9 +1245,9 @@ function Kt(e = "latest", t = {}) {
|
|
|
1224
1245
|
}
|
|
1225
1246
|
//#endregion
|
|
1226
1247
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1227
|
-
var
|
|
1248
|
+
var Jt = 100, Yt = /* @__PURE__ */ new Map();
|
|
1228
1249
|
function $(e) {
|
|
1229
|
-
let t =
|
|
1250
|
+
let t = Yt.get(e);
|
|
1230
1251
|
return t || (t = ee({
|
|
1231
1252
|
text: "",
|
|
1232
1253
|
sentiment: null,
|
|
@@ -1234,9 +1255,9 @@ function $(e) {
|
|
|
1234
1255
|
pending: !1,
|
|
1235
1256
|
saving: !1,
|
|
1236
1257
|
error: ""
|
|
1237
|
-
}),
|
|
1258
|
+
}), Yt.set(e, t)), t;
|
|
1238
1259
|
}
|
|
1239
|
-
function
|
|
1260
|
+
function Xt(e) {
|
|
1240
1261
|
let t = Q(), n = () => h(), { user: r } = ct(), i = A(() => N(e)), a = A(() => `${r.value?.uid ?? ""}:${i.value}`), o = A(() => {
|
|
1241
1262
|
let e = r.value?.uid, a = i.value;
|
|
1242
1263
|
return !e || !a ? null : f(t, n(), e, a);
|
|
@@ -1252,7 +1273,7 @@ function Yt(e) {
|
|
|
1252
1273
|
let t = $(a.value);
|
|
1253
1274
|
t.pending = !0, t.error = "";
|
|
1254
1275
|
try {
|
|
1255
|
-
let n = (await
|
|
1276
|
+
let n = (await R(e)).data();
|
|
1256
1277
|
t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
|
|
1257
1278
|
} catch (e) {
|
|
1258
1279
|
t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
|
|
@@ -1268,10 +1289,10 @@ function Yt(e) {
|
|
|
1268
1289
|
let i = $(a.value);
|
|
1269
1290
|
i.saving = !0, i.error = "";
|
|
1270
1291
|
try {
|
|
1271
|
-
await
|
|
1292
|
+
await K(n, {
|
|
1272
1293
|
text: r,
|
|
1273
1294
|
sentiment: t,
|
|
1274
|
-
updatedAt:
|
|
1295
|
+
updatedAt: G()
|
|
1275
1296
|
}), i.text = r, i.sentiment = t, i.loaded = !0;
|
|
1276
1297
|
} catch (e) {
|
|
1277
1298
|
throw i.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
|
|
@@ -1306,14 +1327,14 @@ function Yt(e) {
|
|
|
1306
1327
|
}
|
|
1307
1328
|
//#endregion
|
|
1308
1329
|
//#region src/composables/useMemiBoardComments.ts
|
|
1309
|
-
var
|
|
1310
|
-
function
|
|
1330
|
+
var Zt = 10, Qt = 5, $t = 1e3;
|
|
1331
|
+
function en(e) {
|
|
1311
1332
|
let t = e.trim();
|
|
1312
1333
|
if (!t) throw Error("댓글 내용을 입력해 주세요.");
|
|
1313
|
-
if (t.length > 1e3) throw Error(`댓글은 ${
|
|
1334
|
+
if (t.length > 1e3) throw Error(`댓글은 ${$t.toLocaleString()}자까지 작성할 수 있습니다.`);
|
|
1314
1335
|
return t;
|
|
1315
1336
|
}
|
|
1316
|
-
function
|
|
1337
|
+
function tn(e, n, r = {}) {
|
|
1317
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;
|
|
1318
1339
|
function C(e) {
|
|
1319
1340
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
@@ -1331,7 +1352,7 @@ function en(e, n, r = {}) {
|
|
|
1331
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) {
|
|
1332
1353
|
m.value = !0, y.value = !0;
|
|
1333
1354
|
try {
|
|
1334
|
-
let t =
|
|
1355
|
+
let t = z(W(c(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
|
|
1335
1356
|
if (await Promise.all([t.then((t) => {
|
|
1336
1357
|
if (u.value !== e) return;
|
|
1337
1358
|
let n = t.docs.map((e) => ({
|
|
@@ -1346,7 +1367,7 @@ function en(e, n, r = {}) {
|
|
|
1346
1367
|
}
|
|
1347
1368
|
}, { immediate: !0 });
|
|
1348
1369
|
function T() {
|
|
1349
|
-
S?.(), S =
|
|
1370
|
+
S?.(), S = H(W(c(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
|
|
1350
1371
|
let t = new Map(f.value.map((e) => [e.id, e]));
|
|
1351
1372
|
for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
|
|
1352
1373
|
id: n.doc.id,
|
|
@@ -1362,17 +1383,17 @@ function en(e, n, r = {}) {
|
|
|
1362
1383
|
_.value = !0;
|
|
1363
1384
|
try {
|
|
1364
1385
|
let e = [
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
...b ? [
|
|
1370
|
-
|
|
1371
|
-
], t = await
|
|
1386
|
+
Y("postId", "==", u.value),
|
|
1387
|
+
Y("parentId", "==", null),
|
|
1388
|
+
U("createdAt", "asc"),
|
|
1389
|
+
U(L(), "asc"),
|
|
1390
|
+
...b ? [q(b)] : [],
|
|
1391
|
+
V(11)
|
|
1392
|
+
], t = await z(W(c(), ...e)), n = t.docs.slice(0, Zt), r = n.map((e) => ({
|
|
1372
1393
|
id: e.id,
|
|
1373
1394
|
...e.data()
|
|
1374
1395
|
})), i = new Set(g.value.map((e) => e.id));
|
|
1375
|
-
g.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <=
|
|
1396
|
+
g.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Zt && (v.value = !1, x = setTimeout(() => {
|
|
1376
1397
|
x = void 0, T();
|
|
1377
1398
|
}, 1e3));
|
|
1378
1399
|
} finally {
|
|
@@ -1384,7 +1405,7 @@ function en(e, n, r = {}) {
|
|
|
1384
1405
|
clearTimeout(x), S?.();
|
|
1385
1406
|
});
|
|
1386
1407
|
async function O(e) {
|
|
1387
|
-
let n =
|
|
1408
|
+
let n = en(e.body), r = X(i), o = te(c());
|
|
1388
1409
|
r.set(o, {
|
|
1389
1410
|
postId: u.value,
|
|
1390
1411
|
boardId: l.value,
|
|
@@ -1402,14 +1423,14 @@ function en(e, n, r = {}) {
|
|
|
1402
1423
|
replyCount: 0,
|
|
1403
1424
|
likeCount: 0,
|
|
1404
1425
|
isReply: !1,
|
|
1405
|
-
createdAt:
|
|
1406
|
-
updatedAt:
|
|
1407
|
-
}), r.update(t(i, a(), u.value), { commentCount:
|
|
1426
|
+
createdAt: G(),
|
|
1427
|
+
updatedAt: G()
|
|
1428
|
+
}), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1408
1429
|
}
|
|
1409
1430
|
async function k(e) {
|
|
1410
|
-
let n =
|
|
1431
|
+
let n = en(e.body);
|
|
1411
1432
|
if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
|
|
1412
|
-
let r =
|
|
1433
|
+
let r = X(i), o = te(c()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1413
1434
|
if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
|
|
1414
1435
|
let p = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1415
1436
|
r.set(o, {
|
|
@@ -1429,28 +1450,28 @@ function en(e, n, r = {}) {
|
|
|
1429
1450
|
replyCount: 0,
|
|
1430
1451
|
likeCount: 0,
|
|
1431
1452
|
isReply: !0,
|
|
1432
|
-
createdAt:
|
|
1433
|
-
updatedAt:
|
|
1434
|
-
}), r.update(s(i, a(), f), { replyCount:
|
|
1453
|
+
createdAt: G(),
|
|
1454
|
+
updatedAt: G()
|
|
1455
|
+
}), r.update(s(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1435
1456
|
}
|
|
1436
1457
|
async function ee(e) {
|
|
1437
1458
|
if (!e.id) return;
|
|
1438
1459
|
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
|
|
1439
1460
|
await yt(i, a(), e.id);
|
|
1440
|
-
let n =
|
|
1441
|
-
n.delete(s(i, a(), e.id)), e.parentId && e.rootId && n.update(s(i, a(), e.rootId), { replyCount:
|
|
1461
|
+
let n = X(i);
|
|
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);
|
|
1442
1463
|
}
|
|
1443
1464
|
async function F(e, t) {
|
|
1444
|
-
let n =
|
|
1445
|
-
await
|
|
1465
|
+
let n = en(t);
|
|
1466
|
+
await J(s(i, a(), e), {
|
|
1446
1467
|
body: n,
|
|
1447
|
-
updatedAt:
|
|
1468
|
+
updatedAt: G()
|
|
1448
1469
|
});
|
|
1449
1470
|
}
|
|
1450
1471
|
async function I(e, t, n) {
|
|
1451
|
-
await
|
|
1472
|
+
await J(s(i, a(), e), {
|
|
1452
1473
|
isBlinded: t,
|
|
1453
|
-
moderatedAt:
|
|
1474
|
+
moderatedAt: G(),
|
|
1454
1475
|
moderatedBy: n
|
|
1455
1476
|
});
|
|
1456
1477
|
}
|
|
@@ -1467,13 +1488,13 @@ function en(e, n, r = {}) {
|
|
|
1467
1488
|
deleteComment: ee
|
|
1468
1489
|
};
|
|
1469
1490
|
}
|
|
1470
|
-
function
|
|
1491
|
+
function nn(e, t, n) {
|
|
1471
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;
|
|
1472
1493
|
function p(e) {
|
|
1473
1494
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
1474
1495
|
}
|
|
1475
1496
|
function m() {
|
|
1476
|
-
f ||=
|
|
1497
|
+
f ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
|
|
1477
1498
|
let t = new Map(s.value.map((e) => [e.id, e]));
|
|
1478
1499
|
for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
|
|
1479
1500
|
id: n.doc.id,
|
|
@@ -1489,18 +1510,18 @@ function tn(e, t, n) {
|
|
|
1489
1510
|
c.value = !0;
|
|
1490
1511
|
try {
|
|
1491
1512
|
let e = [
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
...d ? [
|
|
1498
|
-
|
|
1499
|
-
], t = await
|
|
1513
|
+
Y("postId", "==", o),
|
|
1514
|
+
Y("rootId", "==", n),
|
|
1515
|
+
Y("isReply", "==", !0),
|
|
1516
|
+
U("createdAt", "asc"),
|
|
1517
|
+
U(L(), "asc"),
|
|
1518
|
+
...d ? [q(d)] : [],
|
|
1519
|
+
V(6)
|
|
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) => ({
|
|
1500
1521
|
id: e.id,
|
|
1501
1522
|
...e.data()
|
|
1502
1523
|
})).filter((e) => !r.has(e.id));
|
|
1503
|
-
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());
|
|
1504
1525
|
} finally {
|
|
1505
1526
|
c.value = !1;
|
|
1506
1527
|
}
|
|
@@ -1523,8 +1544,8 @@ function tn(e, t, n) {
|
|
|
1523
1544
|
}
|
|
1524
1545
|
//#endregion
|
|
1525
1546
|
//#region src/composables/useMemiBoardLikes.ts
|
|
1526
|
-
function
|
|
1527
|
-
let r = () => h(), i = Q(), a =
|
|
1547
|
+
function rn(e, n) {
|
|
1548
|
+
let r = () => h(), i = Q(), a = Z(), o = M(!1), s = M(!1), c;
|
|
1528
1549
|
function l(e) {
|
|
1529
1550
|
return k(i, r(), n, e);
|
|
1530
1551
|
}
|
|
@@ -1533,7 +1554,7 @@ function nn(e, n) {
|
|
|
1533
1554
|
o.value = !1;
|
|
1534
1555
|
return;
|
|
1535
1556
|
}
|
|
1536
|
-
c =
|
|
1557
|
+
c = H(l(e.uid), (e) => {
|
|
1537
1558
|
o.value = e.exists();
|
|
1538
1559
|
}, (e) => {
|
|
1539
1560
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
@@ -1546,12 +1567,12 @@ function nn(e, n) {
|
|
|
1546
1567
|
s.value = !0;
|
|
1547
1568
|
try {
|
|
1548
1569
|
let a = l(c), o = t(i, r(), n);
|
|
1549
|
-
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, {
|
|
1550
1571
|
uid: c,
|
|
1551
1572
|
postId: n,
|
|
1552
1573
|
boardId: e,
|
|
1553
|
-
createdAt:
|
|
1554
|
-
}), t.update(o, { likeCount:
|
|
1574
|
+
createdAt: G()
|
|
1575
|
+
}), t.update(o, { likeCount: B(1) }), !0));
|
|
1555
1576
|
} finally {
|
|
1556
1577
|
s.value = !1;
|
|
1557
1578
|
}
|
|
@@ -1562,11 +1583,11 @@ function nn(e, n) {
|
|
|
1562
1583
|
toggleLike: u
|
|
1563
1584
|
};
|
|
1564
1585
|
}
|
|
1565
|
-
var
|
|
1566
|
-
function
|
|
1567
|
-
let n = () => h(), r = Q(), i =
|
|
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;
|
|
1568
1589
|
P([i, o], ([e, t]) => {
|
|
1569
|
-
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u =
|
|
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) => {
|
|
1570
1591
|
let t = /* @__PURE__ */ new Set();
|
|
1571
1592
|
for (let n of e.docs) {
|
|
1572
1593
|
let e = n.data().commentId;
|
|
@@ -1590,7 +1611,7 @@ function an(e, t) {
|
|
|
1590
1611
|
if (l.value) return d(e);
|
|
1591
1612
|
l.value = e;
|
|
1592
1613
|
try {
|
|
1593
|
-
let i = k(r, n(), e, t), l = s(r, n(), e), u = await
|
|
1614
|
+
let i = k(r, n(), e, t), l = s(r, n(), e), u = await ie(r, async (n) => {
|
|
1594
1615
|
let r = await n.get(i), s = await n.get(l);
|
|
1595
1616
|
if (!s.exists()) throw Error("댓글을 찾을 수 없습니다.");
|
|
1596
1617
|
let c = Number(s.data()?.likeCount ?? 0);
|
|
@@ -1600,7 +1621,7 @@ function an(e, t) {
|
|
|
1600
1621
|
boardId: a.value,
|
|
1601
1622
|
commentId: e,
|
|
1602
1623
|
target: "comment",
|
|
1603
|
-
createdAt:
|
|
1624
|
+
createdAt: G()
|
|
1604
1625
|
}), n.update(l, { likeCount: c + 1 }), !0);
|
|
1605
1626
|
}), d = new Set(c.value);
|
|
1606
1627
|
return u ? d.add(e) : d.delete(e), c.value = d, u;
|
|
@@ -1616,7 +1637,7 @@ function an(e, t) {
|
|
|
1616
1637
|
}
|
|
1617
1638
|
//#endregion
|
|
1618
1639
|
//#region src/composables/useMemiBoardReports.ts
|
|
1619
|
-
var
|
|
1640
|
+
var sn = 200, cn = [
|
|
1620
1641
|
{
|
|
1621
1642
|
value: "spam",
|
|
1622
1643
|
label: "스팸·광고"
|
|
@@ -1638,17 +1659,17 @@ var on = 200, sn = [
|
|
|
1638
1659
|
label: "기타"
|
|
1639
1660
|
}
|
|
1640
1661
|
];
|
|
1641
|
-
function
|
|
1642
|
-
return
|
|
1662
|
+
function ln(e) {
|
|
1663
|
+
return cn.find((t) => t.value === e)?.label || e || "신고";
|
|
1643
1664
|
}
|
|
1644
|
-
function
|
|
1645
|
-
let n = () => h(), i = Q(), a =
|
|
1665
|
+
function un(e, t) {
|
|
1666
|
+
let n = () => h(), i = Q(), a = Z(), o = M(!1), s = M(!1), c;
|
|
1646
1667
|
P(a, (e) => {
|
|
1647
1668
|
if (c?.(), c = void 0, !e) {
|
|
1648
1669
|
o.value = !1;
|
|
1649
1670
|
return;
|
|
1650
1671
|
}
|
|
1651
|
-
c =
|
|
1672
|
+
c = H(r(i, n(), t, e.uid), (e) => {
|
|
1652
1673
|
o.value = e.exists();
|
|
1653
1674
|
}, (e) => {
|
|
1654
1675
|
console.error("[memi-board:reports] onSnapshot failed", e);
|
|
@@ -1659,13 +1680,13 @@ function ln(e, t) {
|
|
|
1659
1680
|
if (!l) throw Error("로그인이 필요합니다.");
|
|
1660
1681
|
if (s.value) return;
|
|
1661
1682
|
if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
|
|
1662
|
-
let u =
|
|
1683
|
+
let u = cn.some((e) => e.value === c.reason) ? c.reason : null;
|
|
1663
1684
|
if (!u) throw Error("신고 사유를 선택해 주세요.");
|
|
1664
1685
|
let d = (c.detail || "").trim();
|
|
1665
1686
|
if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
|
|
1666
1687
|
s.value = !0;
|
|
1667
1688
|
try {
|
|
1668
|
-
await
|
|
1689
|
+
await K(r(i, n(), t, l), {
|
|
1669
1690
|
uid: l,
|
|
1670
1691
|
target: "post",
|
|
1671
1692
|
postId: t,
|
|
@@ -1676,7 +1697,7 @@ function ln(e, t) {
|
|
|
1676
1697
|
postTitle: (c.postTitle || "").trim().slice(0, 200),
|
|
1677
1698
|
authorUid: c.authorUid || "",
|
|
1678
1699
|
authorName: c.authorName ?? null,
|
|
1679
|
-
createdAt:
|
|
1700
|
+
createdAt: G()
|
|
1680
1701
|
}), o.value = !0;
|
|
1681
1702
|
} finally {
|
|
1682
1703
|
s.value = !1;
|
|
@@ -1688,9 +1709,9 @@ function ln(e, t) {
|
|
|
1688
1709
|
submitReport: l
|
|
1689
1710
|
};
|
|
1690
1711
|
}
|
|
1691
|
-
var
|
|
1692
|
-
function
|
|
1693
|
-
let t = () => h(), n = Q(), { canManageContent: a, canManageBoard: o } = ct(), s = e.pageSize ??
|
|
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;
|
|
1694
1715
|
function _(e) {
|
|
1695
1716
|
return {
|
|
1696
1717
|
id: e.id,
|
|
@@ -1703,8 +1724,8 @@ function dn(e = {}) {
|
|
|
1703
1724
|
return;
|
|
1704
1725
|
}
|
|
1705
1726
|
u.value = !0;
|
|
1706
|
-
let e = c.value ? [
|
|
1707
|
-
g =
|
|
1727
|
+
let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")];
|
|
1728
|
+
g = H(W(i(n, t()), ...e, U("createdAt", "desc"), U(L(), "desc"), V(s)), (e) => {
|
|
1708
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;
|
|
1709
1730
|
}, (e) => {
|
|
1710
1731
|
console.error("[memi-board:report-queue] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
|
|
@@ -1715,7 +1736,7 @@ function dn(e = {}) {
|
|
|
1715
1736
|
if (!d.value && f.value && m && a.value) {
|
|
1716
1737
|
d.value = !0, p.value = "";
|
|
1717
1738
|
try {
|
|
1718
|
-
let e = c.value ? [
|
|
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));
|
|
1719
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;
|
|
1720
1741
|
} catch (e) {
|
|
1721
1742
|
p.value = e.message || String(e);
|
|
@@ -1725,10 +1746,10 @@ function dn(e = {}) {
|
|
|
1725
1746
|
}
|
|
1726
1747
|
}
|
|
1727
1748
|
async function b(e, i) {
|
|
1728
|
-
e.id && o(e.boardId) && await
|
|
1749
|
+
e.id && o(e.boardId) && await J(r(n, t(), e.postId, e.uid), {
|
|
1729
1750
|
status: i,
|
|
1730
|
-
reviewedAt:
|
|
1731
|
-
reviewedBy:
|
|
1751
|
+
reviewedAt: G(),
|
|
1752
|
+
reviewedBy: Z().value?.uid || ""
|
|
1732
1753
|
});
|
|
1733
1754
|
}
|
|
1734
1755
|
return {
|
|
@@ -1744,7 +1765,7 @@ function dn(e = {}) {
|
|
|
1744
1765
|
}
|
|
1745
1766
|
//#endregion
|
|
1746
1767
|
//#region src/composables/useMemiBoardViews.ts
|
|
1747
|
-
function
|
|
1768
|
+
function pn() {
|
|
1748
1769
|
let e = () => h(), n = Q();
|
|
1749
1770
|
function r(t, n) {
|
|
1750
1771
|
let { postsCollection: r } = e();
|
|
@@ -1768,7 +1789,7 @@ function fn() {
|
|
|
1768
1789
|
if (c && l && !i(c, l)) {
|
|
1769
1790
|
a(c, l);
|
|
1770
1791
|
try {
|
|
1771
|
-
await
|
|
1792
|
+
await J(t(n, e(), l), { viewCount: B(1) });
|
|
1772
1793
|
} catch (e) {
|
|
1773
1794
|
try {
|
|
1774
1795
|
sessionStorage.removeItem(r(c, l));
|
|
@@ -1784,7 +1805,7 @@ function fn() {
|
|
|
1784
1805
|
}
|
|
1785
1806
|
//#endregion
|
|
1786
1807
|
//#region src/utils/moderation-prompt.ts
|
|
1787
|
-
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 = {
|
|
1788
1809
|
abuse: "욕설·비속어·모욕",
|
|
1789
1810
|
spam: "스팸·광고",
|
|
1790
1811
|
adult: "선정적·성인 내용",
|
|
@@ -1792,17 +1813,17 @@ var pn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
1792
1813
|
other: "부적절한 내용",
|
|
1793
1814
|
none: ""
|
|
1794
1815
|
};
|
|
1795
|
-
function
|
|
1816
|
+
function gn(e) {
|
|
1796
1817
|
if (e.via === "local" && e.localHit) {
|
|
1797
1818
|
let t = e.localHit.trim();
|
|
1798
1819
|
return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
|
|
1799
1820
|
}
|
|
1800
1821
|
let t = (e.reason || "").trim();
|
|
1801
1822
|
if (t) return t;
|
|
1802
|
-
let n = e.category && e.category !== "none" ?
|
|
1823
|
+
let n = e.category && e.category !== "none" ? hn[e.category] || e.category : "";
|
|
1803
1824
|
return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
|
|
1804
1825
|
}
|
|
1805
|
-
var
|
|
1826
|
+
var _n = [
|
|
1806
1827
|
"씨발",
|
|
1807
1828
|
"시발",
|
|
1808
1829
|
"시이발",
|
|
@@ -1828,10 +1849,10 @@ var gn = [
|
|
|
1828
1849
|
"bitch",
|
|
1829
1850
|
"asshole"
|
|
1830
1851
|
];
|
|
1831
|
-
function
|
|
1852
|
+
function vn(e) {
|
|
1832
1853
|
return new RegExp(e.join("|"), "i");
|
|
1833
1854
|
}
|
|
1834
|
-
var
|
|
1855
|
+
var yn = /* @__PURE__ */ new Set([
|
|
1835
1856
|
"none",
|
|
1836
1857
|
"abuse",
|
|
1837
1858
|
"spam",
|
|
@@ -1839,7 +1860,7 @@ var vn = /* @__PURE__ */ new Set([
|
|
|
1839
1860
|
"violence",
|
|
1840
1861
|
"other"
|
|
1841
1862
|
]);
|
|
1842
|
-
function
|
|
1863
|
+
function bn(e) {
|
|
1843
1864
|
let t = e.trim();
|
|
1844
1865
|
if (!t) return null;
|
|
1845
1866
|
let n;
|
|
@@ -1857,7 +1878,7 @@ function yn(e) {
|
|
|
1857
1878
|
if (typeof n != "object" || !n) return null;
|
|
1858
1879
|
let r = n;
|
|
1859
1880
|
if (typeof r.flagged != "boolean") return null;
|
|
1860
|
-
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 : "";
|
|
1861
1882
|
return {
|
|
1862
1883
|
flagged: r.flagged,
|
|
1863
1884
|
category: i,
|
|
@@ -1866,20 +1887,20 @@ function yn(e) {
|
|
|
1866
1887
|
}
|
|
1867
1888
|
//#endregion
|
|
1868
1889
|
//#region src/composables/useMemiBoardModeration.ts
|
|
1869
|
-
var
|
|
1890
|
+
var xn = {
|
|
1870
1891
|
flagged: !1,
|
|
1871
1892
|
category: "none",
|
|
1872
1893
|
reason: "",
|
|
1873
1894
|
via: "empty"
|
|
1874
1895
|
};
|
|
1875
|
-
function
|
|
1876
|
-
let e = c().moderation ?? {}, t = se(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = ct(), a =
|
|
1896
|
+
function Sn() {
|
|
1897
|
+
let e = c().moderation ?? {}, t = se(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = ct(), a = vn([..._n, ...e.localBlocklist ?? []]);
|
|
1877
1898
|
function o(e) {
|
|
1878
1899
|
let t = e.match(a);
|
|
1879
1900
|
return t ? {
|
|
1880
1901
|
flagged: !0,
|
|
1881
1902
|
category: "abuse",
|
|
1882
|
-
reason:
|
|
1903
|
+
reason: gn({
|
|
1883
1904
|
via: "local",
|
|
1884
1905
|
localHit: t[0] || "",
|
|
1885
1906
|
category: "abuse"
|
|
@@ -1894,7 +1915,7 @@ function xn() {
|
|
|
1894
1915
|
});
|
|
1895
1916
|
return Ce(i, {
|
|
1896
1917
|
model: n,
|
|
1897
|
-
systemInstruction:
|
|
1918
|
+
systemInstruction: mn,
|
|
1898
1919
|
generationConfig: {
|
|
1899
1920
|
temperature: 0,
|
|
1900
1921
|
maxOutputTokens: 256,
|
|
@@ -1923,7 +1944,7 @@ function xn() {
|
|
|
1923
1944
|
};
|
|
1924
1945
|
}
|
|
1925
1946
|
function u(e, t = "") {
|
|
1926
|
-
let n =
|
|
1947
|
+
let n = gn({
|
|
1927
1948
|
reason: e.reason,
|
|
1928
1949
|
category: e.category,
|
|
1929
1950
|
via: e.via
|
|
@@ -1951,26 +1972,26 @@ function xn() {
|
|
|
1951
1972
|
};
|
|
1952
1973
|
let a = t.trim();
|
|
1953
1974
|
if (!a) return {
|
|
1954
|
-
...
|
|
1975
|
+
...xn,
|
|
1955
1976
|
via: "empty"
|
|
1956
1977
|
};
|
|
1957
1978
|
if (i?.skipFilter) return {
|
|
1958
|
-
...
|
|
1979
|
+
...xn,
|
|
1959
1980
|
via: "admin-override"
|
|
1960
1981
|
};
|
|
1961
1982
|
let c = o(a);
|
|
1962
1983
|
if (c) return d(c);
|
|
1963
1984
|
if (e.enabled === !1) return {
|
|
1964
|
-
...
|
|
1985
|
+
...xn,
|
|
1965
1986
|
via: "disabled"
|
|
1966
1987
|
};
|
|
1967
1988
|
try {
|
|
1968
|
-
let e =
|
|
1989
|
+
let e = bn((await s().generateContent(`심사할 텍스트:\n"""${a.slice(0, 4e3)}"""`)).response.text());
|
|
1969
1990
|
if (!e) return l();
|
|
1970
1991
|
let t = {
|
|
1971
1992
|
flagged: e.flagged,
|
|
1972
1993
|
category: e.category,
|
|
1973
|
-
reason:
|
|
1994
|
+
reason: gn({
|
|
1974
1995
|
reason: e.reason,
|
|
1975
1996
|
category: e.category,
|
|
1976
1997
|
via: "ai"
|
|
@@ -1990,14 +2011,14 @@ function xn() {
|
|
|
1990
2011
|
via: "restricted"
|
|
1991
2012
|
};
|
|
1992
2013
|
if (e.enabled === !1 || !e.moderateImages) return {
|
|
1993
|
-
...
|
|
2014
|
+
...xn,
|
|
1994
2015
|
via: "disabled"
|
|
1995
2016
|
};
|
|
1996
2017
|
try {
|
|
1997
2018
|
let e = await new Promise((e, n) => {
|
|
1998
2019
|
let r = new FileReader();
|
|
1999
2020
|
r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
|
|
2000
|
-
}), n =
|
|
2021
|
+
}), n = bn((await s().generateContent([{ inlineData: {
|
|
2001
2022
|
mimeType: t.type || "image/jpeg",
|
|
2002
2023
|
data: e
|
|
2003
2024
|
} }, "이 이미지를 심사해 주세요."])).response.text());
|
|
@@ -2005,7 +2026,7 @@ function xn() {
|
|
|
2005
2026
|
let r = {
|
|
2006
2027
|
flagged: n.flagged,
|
|
2007
2028
|
category: n.category,
|
|
2008
|
-
reason:
|
|
2029
|
+
reason: gn({
|
|
2009
2030
|
reason: n.reason,
|
|
2010
2031
|
category: n.category,
|
|
2011
2032
|
via: "ai"
|
|
@@ -2024,7 +2045,7 @@ function xn() {
|
|
|
2024
2045
|
}
|
|
2025
2046
|
//#endregion
|
|
2026
2047
|
//#region src/composables/useMemiBoardUsers.ts
|
|
2027
|
-
var
|
|
2048
|
+
var Cn = [
|
|
2028
2049
|
{
|
|
2029
2050
|
value: "admin",
|
|
2030
2051
|
label: "관리자",
|
|
@@ -2041,13 +2062,13 @@ var Sn = [
|
|
|
2041
2062
|
description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
|
|
2042
2063
|
}
|
|
2043
2064
|
];
|
|
2044
|
-
function
|
|
2045
|
-
let t = () => h(), n = Q(), r = A(() => N(e.enabled) ?? !0 ? p(n, t()) : null), i =
|
|
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") });
|
|
2046
2067
|
async function a(e, r) {
|
|
2047
|
-
if (!
|
|
2048
|
-
await
|
|
2068
|
+
if (!Cn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
|
|
2069
|
+
await J(y(n, t(), e), {
|
|
2049
2070
|
role: r,
|
|
2050
|
-
updatedAt:
|
|
2071
|
+
updatedAt: G()
|
|
2051
2072
|
});
|
|
2052
2073
|
}
|
|
2053
2074
|
return {
|
|
@@ -2058,7 +2079,7 @@ function Cn(e = {}) {
|
|
|
2058
2079
|
}
|
|
2059
2080
|
//#endregion
|
|
2060
2081
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2061
|
-
function
|
|
2082
|
+
function Tn(e) {
|
|
2062
2083
|
let t = Q(), n = () => h(), r = A(() => N(e)), i = A(() => {
|
|
2063
2084
|
let e = r.value;
|
|
2064
2085
|
return e ? y(t, n(), e) : null;
|
|
@@ -2070,7 +2091,7 @@ function wn(e) {
|
|
|
2070
2091
|
}
|
|
2071
2092
|
//#endregion
|
|
2072
2093
|
//#region src/utils/section.ts
|
|
2073
|
-
var
|
|
2094
|
+
var En = [
|
|
2074
2095
|
3,
|
|
2075
2096
|
4,
|
|
2076
2097
|
5,
|
|
@@ -2079,7 +2100,7 @@ var Tn = [
|
|
|
2079
2100
|
8,
|
|
2080
2101
|
9,
|
|
2081
2102
|
12
|
|
2082
|
-
],
|
|
2103
|
+
], Dn = [
|
|
2083
2104
|
{
|
|
2084
2105
|
value: "list",
|
|
2085
2106
|
label: "게시목록"
|
|
@@ -2092,24 +2113,24 @@ var Tn = [
|
|
|
2092
2113
|
value: "comments",
|
|
2093
2114
|
label: "댓글모음"
|
|
2094
2115
|
}
|
|
2095
|
-
],
|
|
2116
|
+
], On = [{
|
|
2096
2117
|
value: "latest",
|
|
2097
2118
|
label: "최신순"
|
|
2098
2119
|
}, {
|
|
2099
2120
|
value: "likes",
|
|
2100
2121
|
label: "좋아요순"
|
|
2101
2122
|
}];
|
|
2102
|
-
function
|
|
2123
|
+
function kn(e) {
|
|
2103
2124
|
return e === "list" ? 5 : 4;
|
|
2104
2125
|
}
|
|
2105
|
-
function
|
|
2126
|
+
function An(e, t) {
|
|
2106
2127
|
let n = Math.floor(Number(t));
|
|
2107
|
-
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);
|
|
2108
2129
|
}
|
|
2109
|
-
function
|
|
2130
|
+
function jn(e) {
|
|
2110
2131
|
return e === 12 ? null : 12 - e;
|
|
2111
2132
|
}
|
|
2112
|
-
function
|
|
2133
|
+
function Mn(e) {
|
|
2113
2134
|
return {
|
|
2114
2135
|
3: "col-span-12 md:col-span-3",
|
|
2115
2136
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2121,36 +2142,36 @@ function jn(e) {
|
|
|
2121
2142
|
12: "col-span-12"
|
|
2122
2143
|
}[e];
|
|
2123
2144
|
}
|
|
2124
|
-
function
|
|
2145
|
+
function Nn(e) {
|
|
2125
2146
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2126
2147
|
}
|
|
2127
|
-
function
|
|
2148
|
+
function Pn(e) {
|
|
2128
2149
|
let t = e.boardId || e.category || "free";
|
|
2129
2150
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2130
2151
|
}
|
|
2131
|
-
function
|
|
2152
|
+
function Fn(e) {
|
|
2132
2153
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2133
2154
|
}
|
|
2134
|
-
function
|
|
2155
|
+
function In(e) {
|
|
2135
2156
|
if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2136
2157
|
}
|
|
2137
|
-
function
|
|
2158
|
+
function Ln(e) {
|
|
2138
2159
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2139
2160
|
}
|
|
2140
|
-
function
|
|
2161
|
+
function Rn(e) {
|
|
2141
2162
|
return e.title?.trim() || e.summary || "게시물";
|
|
2142
2163
|
}
|
|
2143
|
-
function
|
|
2164
|
+
function zn(e) {
|
|
2144
2165
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2145
2166
|
}
|
|
2146
|
-
function
|
|
2147
|
-
return
|
|
2167
|
+
function Bn(e) {
|
|
2168
|
+
return zn(e) === "media" ? "미디어" : "목록";
|
|
2148
2169
|
}
|
|
2149
|
-
function
|
|
2170
|
+
function Vn(e, t) {
|
|
2150
2171
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2151
2172
|
}
|
|
2152
|
-
function
|
|
2153
|
-
let n = e.map((e) =>
|
|
2173
|
+
function Hn(e, t) {
|
|
2174
|
+
let n = e.map((e) => Vn(e.boardId, t));
|
|
2154
2175
|
if (!n.length) return "default";
|
|
2155
2176
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2156
2177
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2158,27 +2179,27 @@ function Vn(e, t) {
|
|
|
2158
2179
|
}
|
|
2159
2180
|
//#endregion
|
|
2160
2181
|
//#region src/composables/useMemiBoardSections.ts
|
|
2161
|
-
var
|
|
2162
|
-
function
|
|
2182
|
+
var Un = [];
|
|
2183
|
+
function Wn(e) {
|
|
2163
2184
|
let t = Number(e.cols);
|
|
2164
|
-
if (
|
|
2185
|
+
if (En.includes(t)) return t;
|
|
2165
2186
|
let n = e.width;
|
|
2166
2187
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2167
2188
|
}
|
|
2168
|
-
function
|
|
2189
|
+
function Gn(e) {
|
|
2169
2190
|
let t = e.height ?? e.size;
|
|
2170
2191
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2171
2192
|
}
|
|
2172
|
-
function
|
|
2193
|
+
function Kn(e) {
|
|
2173
2194
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2174
2195
|
}
|
|
2175
|
-
function
|
|
2196
|
+
function qn(e) {
|
|
2176
2197
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2177
2198
|
}
|
|
2178
|
-
function
|
|
2199
|
+
function Jn(e) {
|
|
2179
2200
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2180
2201
|
}
|
|
2181
|
-
function
|
|
2202
|
+
function Yn(e, t) {
|
|
2182
2203
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2183
2204
|
for (let e of n) {
|
|
2184
2205
|
let n = String(e || "").trim();
|
|
@@ -2186,44 +2207,44 @@ function Jn(e, t) {
|
|
|
2186
2207
|
}
|
|
2187
2208
|
return r;
|
|
2188
2209
|
}
|
|
2189
|
-
function
|
|
2190
|
-
let n =
|
|
2210
|
+
function Xn(e, t) {
|
|
2211
|
+
let n = Kn(e), r = An(n, e.count);
|
|
2191
2212
|
return {
|
|
2192
2213
|
id: String(e.id || `box-${t + 1}`),
|
|
2193
2214
|
title: String(e.title || `섹션 ${t + 1}`),
|
|
2194
2215
|
showTitle: e.showTitle !== !1,
|
|
2195
2216
|
kind: n,
|
|
2196
|
-
boardId:
|
|
2217
|
+
boardId: Jn(e),
|
|
2197
2218
|
count: r,
|
|
2198
|
-
postIds:
|
|
2199
|
-
sort:
|
|
2200
|
-
cols:
|
|
2201
|
-
height:
|
|
2219
|
+
postIds: Yn(e, r),
|
|
2220
|
+
sort: qn(e),
|
|
2221
|
+
cols: Wn(e),
|
|
2222
|
+
height: Gn(e),
|
|
2202
2223
|
order: t
|
|
2203
2224
|
};
|
|
2204
2225
|
}
|
|
2205
|
-
function
|
|
2206
|
-
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)));
|
|
2207
2228
|
async function a(e) {
|
|
2208
|
-
await
|
|
2229
|
+
await K(t, {
|
|
2209
2230
|
columns: 12,
|
|
2210
2231
|
blocks: e.map((e, t) => {
|
|
2211
|
-
let n = e, r =
|
|
2232
|
+
let n = e, r = Kn(n), i = An(r, e.count);
|
|
2212
2233
|
return {
|
|
2213
2234
|
id: e.id,
|
|
2214
2235
|
title: e.title.trim() || `섹션 ${t + 1}`,
|
|
2215
2236
|
showTitle: e.showTitle !== !1,
|
|
2216
2237
|
kind: r,
|
|
2217
|
-
boardId:
|
|
2238
|
+
boardId: Jn(n),
|
|
2218
2239
|
count: i,
|
|
2219
|
-
postIds:
|
|
2220
|
-
sort:
|
|
2221
|
-
cols:
|
|
2222
|
-
height:
|
|
2240
|
+
postIds: Yn(n, i),
|
|
2241
|
+
sort: qn(n),
|
|
2242
|
+
cols: Wn(n),
|
|
2243
|
+
height: Gn(n),
|
|
2223
2244
|
order: t
|
|
2224
2245
|
};
|
|
2225
2246
|
}),
|
|
2226
|
-
updatedAt:
|
|
2247
|
+
updatedAt: G()
|
|
2227
2248
|
});
|
|
2228
2249
|
}
|
|
2229
2250
|
return {
|
|
@@ -2238,19 +2259,19 @@ function Xn() {
|
|
|
2238
2259
|
}
|
|
2239
2260
|
//#endregion
|
|
2240
2261
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2241
|
-
function
|
|
2262
|
+
function Qn(e, t) {
|
|
2242
2263
|
let n = Q(), r = () => h(), i = N(e)?.trim() || "all", a = A(() => {
|
|
2243
|
-
let i = Math.max(1, Math.floor(Number(N(t)) || 5)), a = N(e)?.trim() || "", s = [
|
|
2244
|
-
return a && s.unshift(
|
|
2245
|
-
}), { data: s, pending: c, error: l } =
|
|
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}`) });
|
|
2246
2267
|
return {
|
|
2247
2268
|
posts: A(() => s.value ?? []),
|
|
2248
2269
|
pending: c,
|
|
2249
2270
|
error: l
|
|
2250
2271
|
};
|
|
2251
2272
|
}
|
|
2252
|
-
function
|
|
2253
|
-
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 ?
|
|
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}`) });
|
|
2254
2275
|
return {
|
|
2255
2276
|
posts: A(() => {
|
|
2256
2277
|
let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
|
|
@@ -2266,7 +2287,12 @@ function Qn(e) {
|
|
|
2266
2287
|
}
|
|
2267
2288
|
//#endregion
|
|
2268
2289
|
//#region src/data/versionHistory.ts
|
|
2269
|
-
var
|
|
2290
|
+
var er = [
|
|
2291
|
+
{
|
|
2292
|
+
version: "0.44.0",
|
|
2293
|
+
date: "2026-10-01",
|
|
2294
|
+
highlights: ["글을 저장하고 미리보기가 뜨기까지 기다리는 시간이 줄었어요 (특히 iPhone·Mac Safari)"]
|
|
2295
|
+
},
|
|
2270
2296
|
{
|
|
2271
2297
|
version: "0.42.0",
|
|
2272
2298
|
date: "2026-08-27",
|
|
@@ -2536,7 +2562,7 @@ var $n = [
|
|
|
2536
2562
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
2537
2563
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
2538
2564
|
}
|
|
2539
|
-
],
|
|
2565
|
+
], tr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2540
2566
|
(function(n, r) {
|
|
2541
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();
|
|
2542
2568
|
})(e, (function() {
|
|
@@ -2830,7 +2856,7 @@ var $n = [
|
|
|
2830
2856
|
return w(1e3 * e);
|
|
2831
2857
|
}, w.en = b[y], w.Ls = b, w.p = {}, w;
|
|
2832
2858
|
}));
|
|
2833
|
-
})),
|
|
2859
|
+
})), nr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2834
2860
|
(function(n, r) {
|
|
2835
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();
|
|
2836
2862
|
})(e, (function() {
|
|
@@ -2934,9 +2960,9 @@ var $n = [
|
|
|
2934
2960
|
};
|
|
2935
2961
|
};
|
|
2936
2962
|
}));
|
|
2937
|
-
})),
|
|
2963
|
+
})), rr = /* @__PURE__ */ Ae(((e, t) => {
|
|
2938
2964
|
(function(n, r) {
|
|
2939
|
-
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);
|
|
2940
2966
|
})(e, (function(e) {
|
|
2941
2967
|
function t(e) {
|
|
2942
2968
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
@@ -2984,24 +3010,24 @@ var $n = [
|
|
|
2984
3010
|
};
|
|
2985
3011
|
return n.default.locale(r, null, !0), r;
|
|
2986
3012
|
}));
|
|
2987
|
-
})),
|
|
2988
|
-
|
|
2989
|
-
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) {
|
|
2990
3016
|
return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
|
|
2991
3017
|
}
|
|
2992
|
-
function
|
|
2993
|
-
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)) : "방금 전";
|
|
2994
3020
|
}
|
|
2995
|
-
function
|
|
2996
|
-
return e?.toDate ? (0,
|
|
3021
|
+
function cr(e) {
|
|
3022
|
+
return e?.toDate ? (0, ir.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
|
|
2997
3023
|
}
|
|
2998
|
-
function
|
|
2999
|
-
let n =
|
|
3000
|
-
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)}`];
|
|
3001
3027
|
}
|
|
3002
3028
|
//#endregion
|
|
3003
3029
|
//#region src/composables/useMemiBoardWritingAssistant.ts
|
|
3004
|
-
var
|
|
3030
|
+
var ur = 10, dr = 864e5, fr = 200, pr = {
|
|
3005
3031
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
3006
3032
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
3007
3033
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -3010,40 +3036,40 @@ var lr = 10, ur = 864e5, dr = 200, fr = {
|
|
|
3010
3036
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
3011
3037
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
3012
3038
|
};
|
|
3013
|
-
function
|
|
3014
|
-
if (e.action !== "custom") return
|
|
3015
|
-
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);
|
|
3016
3042
|
if (!t) throw Error("어떻게 바꿀지 지시문을 입력해 주세요.");
|
|
3017
3043
|
return `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${t}`;
|
|
3018
3044
|
}
|
|
3019
|
-
function
|
|
3045
|
+
function hr(e) {
|
|
3020
3046
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
3021
3047
|
}
|
|
3022
|
-
function
|
|
3048
|
+
function gr(e) {
|
|
3023
3049
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
3024
3050
|
}
|
|
3025
|
-
function
|
|
3026
|
-
let e = se(), t = Q(), n =
|
|
3051
|
+
function _r() {
|
|
3052
|
+
let e = se(), t = Q(), n = Z(), r = c();
|
|
3027
3053
|
async function i() {
|
|
3028
3054
|
let e = n.value?.uid;
|
|
3029
3055
|
if (!e) throw Error("AI 글쓰기 도우미는 로그인 후 사용할 수 있습니다.");
|
|
3030
|
-
await
|
|
3056
|
+
await ie(t, async (n) => {
|
|
3031
3057
|
let r = y(t, h(), e), i = await n.get(r);
|
|
3032
3058
|
if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
|
|
3033
|
-
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;
|
|
3034
3060
|
if (l && s >= 10) {
|
|
3035
|
-
let e = o +
|
|
3061
|
+
let e = o + dr, t = Math.max(1, Math.ceil((e - c) / 36e5));
|
|
3036
3062
|
throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
|
|
3037
3063
|
}
|
|
3038
3064
|
n.update(r, {
|
|
3039
3065
|
aiWritingCount: l ? s + 1 : 1,
|
|
3040
|
-
aiWritingWindowAt: l ? a.aiWritingWindowAt :
|
|
3041
|
-
updatedAt:
|
|
3066
|
+
aiWritingWindowAt: l ? a.aiWritingWindowAt : G(),
|
|
3067
|
+
updatedAt: G()
|
|
3042
3068
|
});
|
|
3043
3069
|
});
|
|
3044
3070
|
}
|
|
3045
3071
|
async function a(t) {
|
|
3046
|
-
let n =
|
|
3072
|
+
let n = mr(t);
|
|
3047
3073
|
if (!t.content.trim()) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
|
|
3048
3074
|
await i();
|
|
3049
3075
|
let a = r.moderation ?? {}, o = Se(e, {
|
|
@@ -3064,7 +3090,7 @@ function gr() {
|
|
|
3064
3090
|
`작업: ${n}`,
|
|
3065
3091
|
t.title ? `현재 제목: ${t.title}` : "",
|
|
3066
3092
|
`본문:\n${c.slice(0, 12e3)}`
|
|
3067
|
-
].filter(Boolean).join("\n\n"), u =
|
|
3093
|
+
].filter(Boolean).join("\n\n"), u = hr((await s.generateContent(l)).response.text());
|
|
3068
3094
|
if (!u) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
|
|
3069
3095
|
return u;
|
|
3070
3096
|
}
|
|
@@ -3072,14 +3098,14 @@ function gr() {
|
|
|
3072
3098
|
}
|
|
3073
3099
|
//#endregion
|
|
3074
3100
|
//#region src/utils/renderMarkdown.ts
|
|
3075
|
-
function
|
|
3101
|
+
function vr(e) {
|
|
3076
3102
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3077
3103
|
}
|
|
3078
|
-
function
|
|
3104
|
+
function yr(e) {
|
|
3079
3105
|
let t = e;
|
|
3080
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;
|
|
3081
3107
|
}
|
|
3082
|
-
function
|
|
3108
|
+
function br(e) {
|
|
3083
3109
|
if (!e?.trim()) return "";
|
|
3084
3110
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
3085
3111
|
function c() {
|
|
@@ -3088,7 +3114,7 @@ function yr(e) {
|
|
|
3088
3114
|
return;
|
|
3089
3115
|
}
|
|
3090
3116
|
let e = o;
|
|
3091
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
3117
|
+
n.push(`<${e}>${s.map((e) => `<li>${yr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
3092
3118
|
}
|
|
3093
3119
|
for (; r < t.length;) {
|
|
3094
3120
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -3097,7 +3123,7 @@ function yr(e) {
|
|
|
3097
3123
|
continue;
|
|
3098
3124
|
}
|
|
3099
3125
|
if (i) {
|
|
3100
|
-
a.push(
|
|
3126
|
+
a.push(vr(e)), r++;
|
|
3101
3127
|
continue;
|
|
3102
3128
|
}
|
|
3103
3129
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -3108,24 +3134,24 @@ function yr(e) {
|
|
|
3108
3134
|
if (u) {
|
|
3109
3135
|
c();
|
|
3110
3136
|
let e = u[1].length;
|
|
3111
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${vr(
|
|
3137
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${yr(vr(u[2]))}</h${e}>`), r++;
|
|
3112
3138
|
continue;
|
|
3113
3139
|
}
|
|
3114
3140
|
if (e.startsWith("> ")) {
|
|
3115
3141
|
c();
|
|
3116
3142
|
let e = [];
|
|
3117
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
3118
|
-
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>`);
|
|
3119
3145
|
continue;
|
|
3120
3146
|
}
|
|
3121
3147
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
3122
3148
|
if (d) {
|
|
3123
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
3149
|
+
o && o !== "ul" && c(), o = "ul", s.push(vr(d[1])), r++;
|
|
3124
3150
|
continue;
|
|
3125
3151
|
}
|
|
3126
3152
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
3127
3153
|
if (f) {
|
|
3128
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
3154
|
+
o && o !== "ol" && c(), o = "ol", s.push(vr(f[1])), r++;
|
|
3129
3155
|
continue;
|
|
3130
3156
|
}
|
|
3131
3157
|
if (!e.trim()) {
|
|
@@ -3137,13 +3163,13 @@ function yr(e) {
|
|
|
3137
3163
|
for (; r < t.length;) {
|
|
3138
3164
|
let e = t[r] ?? "";
|
|
3139
3165
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
3140
|
-
p.push(
|
|
3166
|
+
p.push(vr(e)), r++;
|
|
3141
3167
|
}
|
|
3142
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
3168
|
+
n.push(`<p class="my-2 leading-relaxed">${yr(p.join("<br />"))}</p>`);
|
|
3143
3169
|
}
|
|
3144
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");
|
|
3145
3171
|
}
|
|
3146
3172
|
//#endregion
|
|
3147
|
-
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 };
|
|
3148
3174
|
|
|
3149
3175
|
//# sourceMappingURL=index.js.map
|