memi-board 0.62.0 → 0.63.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/dist/{commentImages-DVrG0nFm.js → commentImages-xb5Y4ySe.js} +12 -5
- package/dist/{commentImages-DVrG0nFm.js.map → commentImages-xb5Y4ySe.js.map} +1 -1
- package/dist/composables/useMemiBoardBannerGeneration.d.ts +1 -0
- package/dist/composables/useMemiBoardStorage.d.ts +2 -1
- package/dist/{en-PEe41L86.js → en-I7auZHra.js} +10 -3
- package/dist/{en-PEe41L86.js.map → en-I7auZHra.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +145 -124
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/BannerEditor.vue +105 -52
- package/dist/runtime/components/CategorySettings.vue +10 -12
- package/dist/runtime/components/CustomFieldEditor.vue +85 -1
- package/dist/runtime/components/List.vue +19 -8
- package/dist/runtime/components/Settings.vue +10 -11
- package/dist/storage.js +10 -1
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/utils/bannerGeneration.d.ts +1 -1
- package/dist/utils/customFields.d.ts +1 -1
- package/docs/firebase-setup.md +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $ as e, A as t, B as n, C as r, D as i, E as a, F as o, G as s, H as c, I as l, J as u, K as d, L as f, M as p, N as m, O as h, P as g, Q as _, R as v, S as y, T as b, U as x, V as S, W as C, X as w, Y as T, Z as E, _ as D, at as O, b as k, c as A, ct as j, d as ee, et as M, f as N, g as te, h as P, i as ne, it as re, j as ie, k as ae, l as oe, lt as se, m as ce, nt as le, o as ue, ot as de, p as fe, q as pe, r as me, rt as he, s as ge, st as _e, t as ve, tt as ye, u as be, ut as F, v as xe, w as Se, x as Ce, y as we, z as Te } from "./commentImages-
|
|
1
|
+
import { $ as e, A as t, B as n, C as r, D as i, E as a, F as o, G as s, H as c, I as l, J as u, K as d, L as f, M as p, N as m, O as h, P as g, Q as _, R as v, S as y, T as b, U as x, V as S, W as C, X as w, Y as T, Z as E, _ as D, at as O, b as k, c as A, ct as j, d as ee, et as M, f as N, g as te, h as P, i as ne, it as re, j as ie, k as ae, l as oe, lt as se, m as ce, nt as le, o as ue, ot as de, p as fe, q as pe, r as me, rt as he, s as ge, st as _e, t as ve, tt as ye, u as be, ut as F, v as xe, w as Se, x as Ce, y as we, z as Te } from "./commentImages-xb5Y4ySe.js";
|
|
2
2
|
import { computed as I, onScopeDispose as L, reactive as Ee, readonly as De, ref as R, shallowRef as Oe, toValue as z, watch as B } from "vue";
|
|
3
|
-
import { collection as ke, deleteDoc as Ae, deleteField as
|
|
3
|
+
import { collection as ke, deleteDoc as Ae, deleteField as V, doc as H, documentId as U, endAt as je, endBefore as Me, getCountFromServer as Ne, getDoc as W, getDocFromServer as Pe, getDocs as G, getDocsFromServer as Fe, increment as Ie, limit as K, limitToLast as Le, onSnapshot as q, orderBy as J, query as Y, runTransaction as Re, serverTimestamp as X, setDoc as ze, startAfter as Z, updateDoc as Be, where as Q, writeBatch as Ve } from "firebase/firestore";
|
|
4
4
|
import { useCollection as He, useCurrentUser as Ue, useDocument as We, useFirebaseApp as Ge, useFirebaseAuth as Ke, useFirestore as $ } from "vuefire";
|
|
5
5
|
import { GoogleAuthProvider as qe, OAuthProvider as Je, createUserWithEmailAndPassword as Ye, getAuth as Xe, signInWithEmailAndPassword as Ze, signInWithPopup as Qe, signOut as $e, updateProfile as et } from "firebase/auth";
|
|
6
6
|
import { deleteObject as tt, getMetadata as nt, getStorage as rt, list as it, listAll as at, ref as ot } from "firebase/storage";
|
|
@@ -175,7 +175,7 @@ function Bt() {
|
|
|
175
175
|
}
|
|
176
176
|
async function Vt(e, t, n) {
|
|
177
177
|
try {
|
|
178
|
-
let r = await
|
|
178
|
+
let r = await W(O(e.db, e.paths, t));
|
|
179
179
|
if (!r.exists()) return {
|
|
180
180
|
label: t,
|
|
181
181
|
description: "",
|
|
@@ -212,7 +212,7 @@ async function Ut(e, t, n, r) {
|
|
|
212
212
|
let s = await Vt(o, i, r);
|
|
213
213
|
if (s.hidden) return null;
|
|
214
214
|
try {
|
|
215
|
-
let e = (await
|
|
215
|
+
let e = (await G(Y(_(o.db, o.paths), Q("boardId", "==", i), Q("slug", "==", a), Q("listed", "==", !0), Q("isPublished", "==", !0), K(1)))).docs[0];
|
|
216
216
|
if (!e) return null;
|
|
217
217
|
let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title : "", c = typeof t.summary == "string" ? t.summary : "", l = Et(t.i18n), u = Dt(t.i18nSource), d = Ft({
|
|
218
218
|
title: n,
|
|
@@ -273,7 +273,7 @@ async function Wt(e, t, n) {
|
|
|
273
273
|
async function Gt(e, t, n) {
|
|
274
274
|
let r = [], i = null;
|
|
275
275
|
try {
|
|
276
|
-
let a = await
|
|
276
|
+
let a = await G(Y(_(e.db, e.paths), Q("boardId", "==", t), Q("listed", "==", !0), Q("isPublished", "==", !0), J("createdAt", "desc"), K(12)));
|
|
277
277
|
for (let e of a.docs) {
|
|
278
278
|
let t = e.data() ?? {}, a = Ft({
|
|
279
279
|
title: typeof t.title == "string" ? t.title : "",
|
|
@@ -395,7 +395,7 @@ function cn() {
|
|
|
395
395
|
f.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), p.value = an(e.moderationBlockAt);
|
|
396
396
|
}
|
|
397
397
|
async function v(e) {
|
|
398
|
-
let t = g(e.uid), n = l(i, b(), e.uid), r = await
|
|
398
|
+
let t = g(e.uid), n = l(i, b(), e.uid), r = await W(t);
|
|
399
399
|
if (!r.exists()) return await Promise.all([ze(t, {
|
|
400
400
|
role: "user",
|
|
401
401
|
displayName: e.displayName,
|
|
@@ -424,7 +424,7 @@ function cn() {
|
|
|
424
424
|
_(void 0);
|
|
425
425
|
return;
|
|
426
426
|
}
|
|
427
|
-
let t = await
|
|
427
|
+
let t = await W(g(e));
|
|
428
428
|
_(t.exists() ? t.data() : void 0);
|
|
429
429
|
}
|
|
430
430
|
r.watchStarted || (r.watchStarted = !0, B(c, async (e) => {
|
|
@@ -460,9 +460,9 @@ function cn() {
|
|
|
460
460
|
restricted: !1,
|
|
461
461
|
messageSuffix: ""
|
|
462
462
|
};
|
|
463
|
-
let i = g(e), a = await
|
|
463
|
+
let i = g(e), a = await W(i);
|
|
464
464
|
a.exists() ? await Be(i, {
|
|
465
|
-
moderationBlockCount:
|
|
465
|
+
moderationBlockCount: Ie(1),
|
|
466
466
|
moderationBlockAt: X(),
|
|
467
467
|
updatedAt: X()
|
|
468
468
|
}) : await ze(i, {
|
|
@@ -588,13 +588,13 @@ function pn(e) {
|
|
|
588
588
|
return t.replace(/-+$/g, "") || "post";
|
|
589
589
|
}
|
|
590
590
|
function mn(e, t, n, r) {
|
|
591
|
-
return
|
|
591
|
+
return H(e, `${t.postsCollection}Slugs`, n, "slugs", r);
|
|
592
592
|
}
|
|
593
593
|
function hn(e, t) {
|
|
594
|
-
return
|
|
594
|
+
return H(e, `${t.postsCollection}Slugs`, "__migration");
|
|
595
595
|
}
|
|
596
596
|
async function gn(e, t, n, r) {
|
|
597
|
-
return _n(r, async (r) => !(await
|
|
597
|
+
return _n(r, async (r) => !(await W(mn(e, t, n, r))).exists());
|
|
598
598
|
}
|
|
599
599
|
async function _n(e, t) {
|
|
600
600
|
let n = pn(e);
|
|
@@ -604,11 +604,11 @@ async function _n(e, t) {
|
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
606
|
async function vn(e, t, n, r, i, a) {
|
|
607
|
-
return _n(i, (i) =>
|
|
607
|
+
return _n(i, (i) => Re(e, async (o) => {
|
|
608
608
|
if ((await o.get(hn(e, t))).data()?.ready !== !0) throw Error("Post URL reservations are not initialized. Run the post slug backfill before creating new posts.");
|
|
609
609
|
let s = mn(e, t, n, i);
|
|
610
610
|
if ((await o.get(s)).exists()) return !1;
|
|
611
|
-
let c =
|
|
611
|
+
let c = H(e, t.postsCollection, r);
|
|
612
612
|
if ((await o.get(c)).exists()) throw Error("This post has already been saved. Reload it before editing.");
|
|
613
613
|
return o.set(s, { postId: r }), a(o, i), !0;
|
|
614
614
|
}));
|
|
@@ -617,7 +617,7 @@ async function vn(e, t, n, r, i, a) {
|
|
|
617
617
|
//#region src/utils/findReadablePost.ts
|
|
618
618
|
async function yn(e, t, n, r, i, a) {
|
|
619
619
|
let o = [Q("boardId", "==", n), Q("slug", "==", r)];
|
|
620
|
-
return a ? (await
|
|
620
|
+
return a ? (await G(Y(_(e, t), ...o, K(1)))).docs[0] : (await Promise.all([G(Y(_(e, t), ...o, Q("listed", "==", !0), Q("isPublished", "==", !0), K(1))), ...i ? [G(Y(_(e, t), ...o, Q("authorUid", "==", i), K(1)))] : []])).flatMap((e) => e.docs)[0];
|
|
621
621
|
}
|
|
622
622
|
//#endregion
|
|
623
623
|
//#region src/utils/extractEditorImageUrls.ts
|
|
@@ -656,27 +656,27 @@ async function wn(e, t) {
|
|
|
656
656
|
}
|
|
657
657
|
var Tn = 450;
|
|
658
658
|
async function En(e, t, n) {
|
|
659
|
-
return (await
|
|
659
|
+
return (await G(Y(x(e, t), Q("postId", "==", n)))).docs.flatMap((e) => ve(e.data().images, `${E(t, n)}/comment-images/`));
|
|
660
660
|
}
|
|
661
661
|
async function Dn(e, t) {
|
|
662
662
|
let n = [...new Set([...t].filter(Boolean))];
|
|
663
663
|
await Promise.all(n.map((t) => tt(ot(e, t)).catch(() => {})));
|
|
664
664
|
}
|
|
665
665
|
async function On(e, t) {
|
|
666
|
-
let n = await
|
|
666
|
+
let n = await G(t);
|
|
667
667
|
for (let t = 0; t < n.docs.length; t += Tn) {
|
|
668
668
|
let r = Ve(e);
|
|
669
669
|
n.docs.slice(t, t + Tn).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
672
|
async function kn(e, t, n) {
|
|
673
|
-
await On(e,
|
|
673
|
+
await On(e, Y(d(e, t), Q("postId", "==", n)));
|
|
674
674
|
}
|
|
675
675
|
async function An(e, t, n) {
|
|
676
|
-
await On(e,
|
|
676
|
+
await On(e, Y(d(e, t), Q("commentId", "==", n), Q("target", "==", "comment")));
|
|
677
677
|
}
|
|
678
678
|
async function jn(e, t, n, r) {
|
|
679
|
-
let i = u(e, n, r), a = pe(e, n, r), [o, s] = await Promise.all([
|
|
679
|
+
let i = u(e, n, r), a = pe(e, n, r), [o, s] = await Promise.all([W(i), W(a)]);
|
|
680
680
|
if (!o.exists()) return;
|
|
681
681
|
let c = o.data(), l = s.exists() ? String(s.data().content ?? "") : "", d = await En(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
682
682
|
for (let e of c.attachments ?? []) {
|
|
@@ -698,7 +698,7 @@ async function jn(e, t, n, r) {
|
|
|
698
698
|
}
|
|
699
699
|
await wn(t, E(n, r));
|
|
700
700
|
for (let e of p) await wn(t, E(n, e));
|
|
701
|
-
await Dn(t, d), await Dn(t, f), await On(e,
|
|
701
|
+
await Dn(t, d), await Dn(t, f), await On(e, Y(x(e, n), Q("postId", "==", r))), await kn(e, n, r);
|
|
702
702
|
let m = Ve(e);
|
|
703
703
|
m.delete(a), m.delete(i), await m.commit();
|
|
704
704
|
}
|
|
@@ -751,7 +751,7 @@ function Bn(e, t = []) {
|
|
|
751
751
|
}
|
|
752
752
|
//#endregion
|
|
753
753
|
//#region src/utils/customFields.ts
|
|
754
|
-
var Vn =
|
|
754
|
+
var Vn = 20, Hn = 40, Un = 80, Wn = 200, Gn = /^[a-z][A-Za-z0-9_]{0,31}$/;
|
|
755
755
|
function Kn(e) {
|
|
756
756
|
return typeof e == "string" ? e.trim() : "";
|
|
757
757
|
}
|
|
@@ -777,13 +777,13 @@ function Xn(e) {
|
|
|
777
777
|
for (let r of e) {
|
|
778
778
|
if (!r || typeof r != "object") continue;
|
|
779
779
|
let e = r, i = Kn(e.key), a = qn(e.label);
|
|
780
|
-
if (Gn.test(i) && a && !t.has(i) && (t.add(i), n.push(Yn(i, a, Jn(e.placeholder))), n.length >=
|
|
780
|
+
if (Gn.test(i) && a && !t.has(i) && (t.add(i), n.push(Yn(i, a, Jn(e.placeholder))), n.length >= 20)) break;
|
|
781
781
|
}
|
|
782
782
|
return n;
|
|
783
783
|
}
|
|
784
784
|
function Zn(e) {
|
|
785
785
|
let t = (e ?? []).filter((e) => Kn(e?.key) || qn(e?.label) || Jn(e?.placeholder));
|
|
786
|
-
if (t.length >
|
|
786
|
+
if (t.length > 20) return {
|
|
787
787
|
fields: [],
|
|
788
788
|
issue: { code: "tooMany" }
|
|
789
789
|
};
|
|
@@ -844,7 +844,7 @@ function nr(e) {
|
|
|
844
844
|
async function rr(e, t, n, r) {
|
|
845
845
|
let i;
|
|
846
846
|
for (;;) {
|
|
847
|
-
let a = await
|
|
847
|
+
let a = await G(Y(t, Q("boardId", "==", n), J("__name__"), ...i ? [Z(i)] : [], K(400)));
|
|
848
848
|
if (a.empty) break;
|
|
849
849
|
let o = Ve(e), s = 0;
|
|
850
850
|
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
@@ -856,7 +856,7 @@ async function ir(e, t, n, r) {
|
|
|
856
856
|
}
|
|
857
857
|
async function ar(e, t, n, r, i) {
|
|
858
858
|
let a = O(e, t, n), o = nr(r.visibility);
|
|
859
|
-
await
|
|
859
|
+
await Re(e, async (e) => {
|
|
860
860
|
let t = await e.get(a), n = t.data(), s = n?.visibilityTransition || "";
|
|
861
861
|
if (s && s !== o) throw Error("Finish the pending visibility change before changing it again.");
|
|
862
862
|
return t.exists() ? !s && nr(n?.visibility) === o ? (e.set(a, r, { merge: !0 }), !1) : (e.set(a, {
|
|
@@ -868,7 +868,7 @@ async function ar(e, t, n, r, i) {
|
|
|
868
868
|
...i ? { createdByUid: i } : {},
|
|
869
869
|
createdAt: X()
|
|
870
870
|
}, { merge: !0 }), !1);
|
|
871
|
-
}) && (await ir(e, t, n, o !== "hidden"), await
|
|
871
|
+
}) && (await ir(e, t, n, o !== "hidden"), await Re(e, async (e) => {
|
|
872
872
|
if ((await e.get(a)).data()?.visibilityTransition !== o) throw Error("The visibility transition changed. Reload and retry.");
|
|
873
873
|
e.update(a, {
|
|
874
874
|
visibility: o,
|
|
@@ -1221,6 +1221,7 @@ function Mr(e, t, n) {
|
|
|
1221
1221
|
createdAt: t.createdAt && typeof t.createdAt.toDate == "function" ? t.createdAt : void 0,
|
|
1222
1222
|
updatedAt: t.updatedAt && typeof t.updatedAt.toMillis == "function" ? t.updatedAt : void 0,
|
|
1223
1223
|
banner: jr(t.banner),
|
|
1224
|
+
mobileBanner: jr(t.mobileBanner),
|
|
1224
1225
|
locale: F(t.locale) ?? "",
|
|
1225
1226
|
autoTranslate: t.autoTranslate === !0,
|
|
1226
1227
|
autoTranslatePosts: t.autoTranslatePosts === !0,
|
|
@@ -1231,7 +1232,7 @@ function Mr(e, t, n) {
|
|
|
1231
1232
|
};
|
|
1232
1233
|
}
|
|
1233
1234
|
function Nr() {
|
|
1234
|
-
let e = () => b(), t = $(), n = Ge(), { isSignedIn: r, isAdmin: i, isStaff: a, user: o } = cn(), { t: s, locale: c } = A(), { translateEntries: l } = Dr(), u = I(() =>
|
|
1235
|
+
let e = () => b(), t = $(), n = Ge(), { isSignedIn: r, isAdmin: i, isStaff: a, user: o } = cn(), { t: s, locale: c } = A(), { translateEntries: l } = Dr(), u = I(() => Y(re(t, e()), J("order", "asc"))), { data: d, pending: f } = He(u, { ssrKey: g(e(), "boards") }), p = I(() => d.value.map((e, t) => Mr(e.id, e, t))), m = p, h = I(() => p.value.filter((e) => e.visibility !== "hidden")), v = h;
|
|
1235
1236
|
function x(e, t) {
|
|
1236
1237
|
if (!e) return;
|
|
1237
1238
|
let n = p.value.find((t) => t.id === e);
|
|
@@ -1285,6 +1286,7 @@ function Nr() {
|
|
|
1285
1286
|
...e.i18nSource ? { i18nSource: e.i18nSource } : {},
|
|
1286
1287
|
order: t,
|
|
1287
1288
|
banner: jr(e.banner),
|
|
1289
|
+
mobileBanner: jr(e.mobileBanner),
|
|
1288
1290
|
updatedAt: X()
|
|
1289
1291
|
};
|
|
1290
1292
|
}
|
|
@@ -1333,8 +1335,10 @@ function Nr() {
|
|
|
1333
1335
|
...i,
|
|
1334
1336
|
label: l,
|
|
1335
1337
|
visibility: u
|
|
1336
|
-
}, a), f = p.value.find((e) => e.id === c)
|
|
1337
|
-
await ar(t, e(), c, d, o.value?.uid)
|
|
1338
|
+
}, a), f = p.value.find((e) => e.id === c);
|
|
1339
|
+
await ar(t, e(), c, d, o.value?.uid);
|
|
1340
|
+
let m = /* @__PURE__ */ new Set([d.banner?.path, d.mobileBanner?.path]);
|
|
1341
|
+
for (let t of [f?.banner, f?.mobileBanner]) t?.path && !m.has(t.path) && t.path.startsWith(`${e().settingsCollection}/${c}/banners/`) && await tt(ot(rt(n), t.path)).catch((e) => {
|
|
1338
1342
|
console.warn("[memi-board] old banner cleanup failed", e);
|
|
1339
1343
|
});
|
|
1340
1344
|
}
|
|
@@ -1350,11 +1354,12 @@ function Nr() {
|
|
|
1350
1354
|
let a = i.trim();
|
|
1351
1355
|
if (!a) throw Error(s("boardSettings.deleteIdRequired"));
|
|
1352
1356
|
for (;;) {
|
|
1353
|
-
let r = await
|
|
1357
|
+
let r = await G(Y(_(t, e()), Q("boardId", "==", a), K(25)));
|
|
1354
1358
|
if (r.empty) break;
|
|
1355
1359
|
for (let i of r.docs) await jn(t, rt(n), e(), i.id);
|
|
1356
1360
|
}
|
|
1357
|
-
|
|
1361
|
+
let o = p.value.find((e) => e.id === a);
|
|
1362
|
+
if (o?.banner || o?.mobileBanner) {
|
|
1358
1363
|
let t = ot(rt(n), `${e().settingsCollection}/${a}/banners`), r = await at(t);
|
|
1359
1364
|
await Promise.all(r.items.map((e) => tt(e)));
|
|
1360
1365
|
}
|
|
@@ -1462,7 +1467,7 @@ function zr(e) {
|
|
|
1462
1467
|
if (!e.title?.trim()) throw Error(c("posts.titleRequired"));
|
|
1463
1468
|
}
|
|
1464
1469
|
function S() {
|
|
1465
|
-
return
|
|
1470
|
+
return H(p()).id;
|
|
1466
1471
|
}
|
|
1467
1472
|
async function C(e) {
|
|
1468
1473
|
return await Xe(n).authStateReady(), s.value && await new Promise((e) => {
|
|
@@ -1473,8 +1478,8 @@ function zr(e) {
|
|
|
1473
1478
|
}
|
|
1474
1479
|
async function w(e = {}) {
|
|
1475
1480
|
let t = e.pageSize ?? 20, n = [Q("boardId", "==", f()), Q("isPublished", "==", !0)];
|
|
1476
|
-
e.publicOnly !== !1 && n.push(Q("listed", "==", !0)), n.push(
|
|
1477
|
-
let r = await
|
|
1481
|
+
e.publicOnly !== !1 && n.push(Q("listed", "==", !0)), n.push(J("createdAt", "desc")), e.cursor && n.push(Z(e.cursor)), n.push(K(t + 1));
|
|
1482
|
+
let r = await G(Y(p(), ...n)), i = r.docs.slice(0, t);
|
|
1478
1483
|
return {
|
|
1479
1484
|
posts: i.map((e) => ({
|
|
1480
1485
|
id: e.id,
|
|
@@ -1485,7 +1490,7 @@ function zr(e) {
|
|
|
1485
1490
|
};
|
|
1486
1491
|
}
|
|
1487
1492
|
async function E(e) {
|
|
1488
|
-
let [t, n] = await Promise.all([
|
|
1493
|
+
let [t, n] = await Promise.all([W(m(e)), W(h(e))]);
|
|
1489
1494
|
return t.exists() ? g(t.id, t.data(), n.exists() ? n.data() : void 0) : null;
|
|
1490
1495
|
}
|
|
1491
1496
|
async function D(e) {
|
|
@@ -1544,8 +1549,8 @@ function zr(e) {
|
|
|
1544
1549
|
Q("boardId", "==", f()),
|
|
1545
1550
|
Q("isPublished", "==", !0),
|
|
1546
1551
|
...e.listed === !1 ? [] : [Q("listed", "==", !0)],
|
|
1547
|
-
|
|
1548
|
-
], [n, r] = await Promise.all([
|
|
1552
|
+
J("createdAt", "desc")
|
|
1553
|
+
], [n, r] = await Promise.all([G(Y(p(), ...t, Z(e.createdAt), K(1))), G(Y(p(), ...t, Me(e.createdAt), Le(1)))]), i = (e) => {
|
|
1549
1554
|
let t = e.docs[0];
|
|
1550
1555
|
return t ? {
|
|
1551
1556
|
id: t.id,
|
|
@@ -1607,7 +1612,7 @@ function zr(e) {
|
|
|
1607
1612
|
let r = y() ? n.title?.trim() || Nn(n.content) : n.title.trim(), i = Bn(n.content, n.attachments), s = v(), l = n.extend ? Qn(n.extend) : void 0, u = n.tags ?? [], f = n.attachments ?? [], p = a.value?.uid;
|
|
1608
1613
|
if (!p) throw Error(c("reports.signInRequired"));
|
|
1609
1614
|
let g = (a.value?.displayName ?? "").trim().slice(0, 80) || null;
|
|
1610
|
-
await
|
|
1615
|
+
await Re(t, async (a) => {
|
|
1611
1616
|
let _ = m(e), v = h(e), [y, b] = await Promise.all([a.get(_), a.get(v)]);
|
|
1612
1617
|
if (!y.exists()) throw Error(c("detail.notFound"));
|
|
1613
1618
|
let x = y.data(), S = b.exists() ? String(b.data().content ?? "") : "", C = x.extend && Object.keys(x.extend).length ? x.extend : void 0, w = l === void 0 ? C : Object.keys(l).length ? l : void 0, E = x.title !== r || S !== n.content || !Ir(x.tags ?? [], u) || !Ir(x.attachments ?? [], f) || !Ir(C ?? null, w ?? null), D = typeof x.revision == "number" ? x.revision : 0;
|
|
@@ -1627,8 +1632,8 @@ function zr(e) {
|
|
|
1627
1632
|
}), a.update(_, {
|
|
1628
1633
|
title: r,
|
|
1629
1634
|
...i,
|
|
1630
|
-
previewImage: i.previewImage ? i.previewImage :
|
|
1631
|
-
videoUrl: i.videoUrl ? i.videoUrl :
|
|
1635
|
+
previewImage: i.previewImage ? i.previewImage : V(),
|
|
1636
|
+
videoUrl: i.videoUrl ? i.videoUrl : V(),
|
|
1632
1637
|
tags: u,
|
|
1633
1638
|
listed: s,
|
|
1634
1639
|
attachments: f,
|
|
@@ -1641,15 +1646,15 @@ function zr(e) {
|
|
|
1641
1646
|
i18n: n.translations.i18n,
|
|
1642
1647
|
i18nSource: n.translations.i18nSource
|
|
1643
1648
|
} : {},
|
|
1644
|
-
...l ? { extend: Object.keys(l).length ? l :
|
|
1649
|
+
...l ? { extend: Object.keys(l).length ? l : V() } : {}
|
|
1645
1650
|
}), a.set(v, {
|
|
1646
1651
|
content: n.content,
|
|
1647
1652
|
...n.translations ? n.translations.contentI18n ? {
|
|
1648
1653
|
i18n: n.translations.contentI18n,
|
|
1649
1654
|
i18nSource: n.translations.contentI18nSource
|
|
1650
1655
|
} : {
|
|
1651
|
-
i18n:
|
|
1652
|
-
i18nSource:
|
|
1656
|
+
i18n: V(),
|
|
1657
|
+
i18nSource: V()
|
|
1653
1658
|
} : {}
|
|
1654
1659
|
}, { merge: !0 });
|
|
1655
1660
|
});
|
|
@@ -1663,8 +1668,8 @@ function zr(e) {
|
|
|
1663
1668
|
i18n: t.contentI18n,
|
|
1664
1669
|
i18nSource: t.contentI18nSource
|
|
1665
1670
|
} : {
|
|
1666
|
-
i18n:
|
|
1667
|
-
i18nSource:
|
|
1671
|
+
i18n: V(),
|
|
1672
|
+
i18nSource: V()
|
|
1668
1673
|
}, { merge: !0 })]);
|
|
1669
1674
|
}
|
|
1670
1675
|
async function te(e) {
|
|
@@ -1679,8 +1684,8 @@ function zr(e) {
|
|
|
1679
1684
|
pinned: !0,
|
|
1680
1685
|
pinnedAt: X()
|
|
1681
1686
|
} : {
|
|
1682
|
-
pinned:
|
|
1683
|
-
pinnedAt:
|
|
1687
|
+
pinned: V(),
|
|
1688
|
+
pinnedAt: V()
|
|
1684
1689
|
});
|
|
1685
1690
|
}
|
|
1686
1691
|
async function ne(e) {
|
|
@@ -1723,7 +1728,7 @@ function Hr(e, t = {}) {
|
|
|
1723
1728
|
return d.value && e.push(Q("listed", "==", !0)), e;
|
|
1724
1729
|
}
|
|
1725
1730
|
function j() {
|
|
1726
|
-
T?.(), T =
|
|
1731
|
+
T?.(), T = q(Y(u(), ...A(), J("createdAt", "desc"), J(U(), "desc"), K(s)), (e) => {
|
|
1727
1732
|
f.value = e.docs.map((e) => ({
|
|
1728
1733
|
id: e.id,
|
|
1729
1734
|
...e.data()
|
|
@@ -1733,7 +1738,7 @@ function Hr(e, t = {}) {
|
|
|
1733
1738
|
});
|
|
1734
1739
|
}
|
|
1735
1740
|
function ee() {
|
|
1736
|
-
D?.(), D =
|
|
1741
|
+
D?.(), D = q(Y(u(), ...A(), Q("pinned", "==", !0), J("pinnedAt", "desc"), K(Vr)), (e) => {
|
|
1737
1742
|
h.value = e.docs.map((e) => ({
|
|
1738
1743
|
id: e.id,
|
|
1739
1744
|
...e.data()
|
|
@@ -1749,7 +1754,7 @@ function Hr(e, t = {}) {
|
|
|
1749
1754
|
m.value = [];
|
|
1750
1755
|
return;
|
|
1751
1756
|
}
|
|
1752
|
-
E =
|
|
1757
|
+
E = q(Y(u(), Q("boardId", "==", l.value), Q("authorUid", "==", e), Q("isPublished", "==", !1), J("createdAt", "desc"), K(20)), (e) => {
|
|
1753
1758
|
m.value = e.docs.map((e) => ({
|
|
1754
1759
|
id: e.id,
|
|
1755
1760
|
...e.data()
|
|
@@ -1764,11 +1769,11 @@ function Hr(e, t = {}) {
|
|
|
1764
1769
|
try {
|
|
1765
1770
|
let e = w ?? C, t = [
|
|
1766
1771
|
...A(),
|
|
1767
|
-
|
|
1768
|
-
|
|
1772
|
+
J("createdAt", "desc"),
|
|
1773
|
+
J(U(), "desc"),
|
|
1769
1774
|
...e ? [Z(e)] : [],
|
|
1770
|
-
|
|
1771
|
-
], n = await
|
|
1775
|
+
K(s + 1)
|
|
1776
|
+
], n = await G(Y(u(), ...t)), r = n.docs.slice(0, s), i = r.map((e) => ({
|
|
1772
1777
|
id: e.id,
|
|
1773
1778
|
...e.data()
|
|
1774
1779
|
})), a = new Set([...p.value, ...f.value].map((e) => e.id));
|
|
@@ -1823,7 +1828,7 @@ function Wr(e, t) {
|
|
|
1823
1828
|
async function s() {
|
|
1824
1829
|
a.value = !0, o.value = null;
|
|
1825
1830
|
try {
|
|
1826
|
-
let a = await
|
|
1831
|
+
let a = await G(Y(w(n, r(), z(t)), Q("boardId", "==", Rr(e))));
|
|
1827
1832
|
i.value = a.docs.map((e) => ({
|
|
1828
1833
|
...e.data(),
|
|
1829
1834
|
id: e.id
|
|
@@ -1872,7 +1877,7 @@ function Kr(e, t = {}) {
|
|
|
1872
1877
|
l.value = !1;
|
|
1873
1878
|
return;
|
|
1874
1879
|
}
|
|
1875
|
-
h =
|
|
1880
|
+
h = q(Y(i(), ...x(), J("createdAt", "desc"), J(U(), "desc"), K(a)), (e) => {
|
|
1876
1881
|
s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1877
1882
|
}, (e) => {
|
|
1878
1883
|
console.error("[memi-board:userPosts] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1884,11 +1889,11 @@ function Kr(e, t = {}) {
|
|
|
1884
1889
|
try {
|
|
1885
1890
|
let e = m ?? p, t = [
|
|
1886
1891
|
...x(),
|
|
1887
|
-
|
|
1888
|
-
|
|
1892
|
+
J("createdAt", "desc"),
|
|
1893
|
+
J(U(), "desc"),
|
|
1889
1894
|
...e ? [Z(e)] : [],
|
|
1890
|
-
|
|
1891
|
-
], n = await
|
|
1895
|
+
K(a + 1)
|
|
1896
|
+
], n = await G(Y(i(), ...t)), r = n.docs.slice(0, a), o = r.map(g), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1892
1897
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1893
1898
|
} catch (e) {
|
|
1894
1899
|
f.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
|
|
@@ -1936,7 +1941,7 @@ function Jr(e, t = {}) {
|
|
|
1936
1941
|
l.value = !1;
|
|
1937
1942
|
return;
|
|
1938
1943
|
}
|
|
1939
|
-
h =
|
|
1944
|
+
h = q(Y(i(), ...y(), J("createdAt", "desc"), J(U(), "desc"), K(a)), (e) => {
|
|
1940
1945
|
s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
|
|
1941
1946
|
}, (e) => {
|
|
1942
1947
|
console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -1948,11 +1953,11 @@ function Jr(e, t = {}) {
|
|
|
1948
1953
|
try {
|
|
1949
1954
|
let e = m ?? p, t = [
|
|
1950
1955
|
...y(),
|
|
1951
|
-
|
|
1952
|
-
|
|
1956
|
+
J("createdAt", "desc"),
|
|
1957
|
+
J(U(), "desc"),
|
|
1953
1958
|
...e ? [Z(e)] : [],
|
|
1954
|
-
|
|
1955
|
-
], n = await
|
|
1959
|
+
K(a + 1)
|
|
1960
|
+
], n = await G(Y(i(), ...t)), r = n.docs.slice(0, a), o = r.map(g), l = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
1956
1961
|
c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
|
|
1957
1962
|
} catch (e) {
|
|
1958
1963
|
f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
|
|
@@ -2005,17 +2010,17 @@ function Xr(e = "latest", t = {}) {
|
|
|
2005
2010
|
function S() {
|
|
2006
2011
|
return o.value === "likes" ? [
|
|
2007
2012
|
Q("listed", "==", !0),
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2013
|
+
J("likeCount", "desc"),
|
|
2014
|
+
J("createdAt", "desc"),
|
|
2015
|
+
J(U(), "desc")
|
|
2011
2016
|
] : [
|
|
2012
2017
|
Q("listed", "==", !0),
|
|
2013
|
-
|
|
2014
|
-
|
|
2018
|
+
J("createdAt", "desc"),
|
|
2019
|
+
J(U(), "desc")
|
|
2015
2020
|
];
|
|
2016
2021
|
}
|
|
2017
2022
|
function C() {
|
|
2018
|
-
h?.(), h =
|
|
2023
|
+
h?.(), h = q(Y(i(), ...S(), K(a.value)), (e) => {
|
|
2019
2024
|
s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
|
|
2020
2025
|
}, (e) => {
|
|
2021
2026
|
console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
|
|
@@ -2025,7 +2030,7 @@ function Xr(e = "latest", t = {}) {
|
|
|
2025
2030
|
if (!(d.value || !u.value || l.value)) {
|
|
2026
2031
|
d.value = !0, f.value = "";
|
|
2027
2032
|
try {
|
|
2028
|
-
let e = m ?? p, t = await
|
|
2033
|
+
let e = m ?? p, t = await G(Y(i(), ...S(), ...e ? [Z(e)] : [], K(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(g), o = new Set([...c.value, ...s.value].map((e) => e.id));
|
|
2029
2034
|
c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
|
|
2030
2035
|
} catch (e) {
|
|
2031
2036
|
f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
|
|
@@ -2077,7 +2082,7 @@ function ei(e) {
|
|
|
2077
2082
|
let t = $r(s.value);
|
|
2078
2083
|
t.pending = !0, t.error = "";
|
|
2079
2084
|
try {
|
|
2080
|
-
let n = (await
|
|
2085
|
+
let n = (await W(e)).data();
|
|
2081
2086
|
t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
|
|
2082
2087
|
} catch (e) {
|
|
2083
2088
|
t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
|
|
@@ -2189,7 +2194,7 @@ function ui(e = {}) {
|
|
|
2189
2194
|
async function h(e) {
|
|
2190
2195
|
let t = c.value?.uid;
|
|
2191
2196
|
if (!t) throw Error(r("writingAssistant.loginRequired"));
|
|
2192
|
-
await
|
|
2197
|
+
await Re(s, async (n) => {
|
|
2193
2198
|
let i = o(s, b(), t), a = await n.get(i);
|
|
2194
2199
|
if (!a.exists()) throw Error(r("writingAssistant.userMissing"));
|
|
2195
2200
|
let c = a.data(), l = e(p(c, Date.now()), c);
|
|
@@ -2381,7 +2386,7 @@ function ui(e = {}) {
|
|
|
2381
2386
|
//#endregion
|
|
2382
2387
|
//#region src/utils/subscribeCommentRange.ts
|
|
2383
2388
|
function di(e, t, n, r, i, a) {
|
|
2384
|
-
return
|
|
2389
|
+
return q(Y(x(e, t), Q("postId", "==", n), ...r.rootId ? [Q("rootId", "==", r.rootId), Q("isReply", "==", !0)] : [Q("parentId", "==", null)], J("createdAt", "asc"), J(U(), "asc"), ...r.hasMore && r.cursor ? [je(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
|
|
2385
2390
|
...e.data(),
|
|
2386
2391
|
id: e.id
|
|
2387
2392
|
}))), a);
|
|
@@ -2403,7 +2408,7 @@ function _i(e, t) {
|
|
|
2403
2408
|
async function s() {
|
|
2404
2409
|
a.value = !0, o.value = null;
|
|
2405
2410
|
try {
|
|
2406
|
-
let a = await
|
|
2411
|
+
let a = await G(Y(c(n, r(), z(t)), Q("boardId", "==", z(e))));
|
|
2407
2412
|
i.value = a.docs.map((e) => ({
|
|
2408
2413
|
...e.data(),
|
|
2409
2414
|
id: e.id
|
|
@@ -2452,7 +2457,7 @@ function vi(e, t, n = {}) {
|
|
|
2452
2457
|
if (te++, k.value = !1, O.value = [], T.value = [], M = null, P?.(), P = void 0, j.value = n.subscribe !== !1, n.subscribe !== !1) {
|
|
2453
2458
|
D.value = !0, ee.value = !0;
|
|
2454
2459
|
try {
|
|
2455
|
-
let n =
|
|
2460
|
+
let n = G(Y(g(), Q("postId", "==", e), J("createdAt", "desc"), J(U(), "desc")));
|
|
2456
2461
|
if (await Promise.all([n.then((e) => {
|
|
2457
2462
|
if (t !== N) return;
|
|
2458
2463
|
let n = e.docs.map((e) => ({
|
|
@@ -2486,11 +2491,11 @@ function vi(e, t, n = {}) {
|
|
|
2486
2491
|
let t = [
|
|
2487
2492
|
Q("postId", "==", C.value),
|
|
2488
2493
|
Q("parentId", "==", null),
|
|
2489
|
-
|
|
2490
|
-
|
|
2494
|
+
J("createdAt", "asc"),
|
|
2495
|
+
J(U(), "asc"),
|
|
2491
2496
|
...M ? [Z(M)] : [],
|
|
2492
|
-
|
|
2493
|
-
], n = await
|
|
2497
|
+
K(11)
|
|
2498
|
+
], n = await G(Y(g(), ...t));
|
|
2494
2499
|
if (e !== N) return;
|
|
2495
2500
|
let r = n.docs.slice(0, fi), i = r.map((e) => ({
|
|
2496
2501
|
id: e.id,
|
|
@@ -2505,10 +2510,10 @@ function vi(e, t, n = {}) {
|
|
|
2505
2510
|
N++, te++, P?.();
|
|
2506
2511
|
});
|
|
2507
2512
|
function oe() {
|
|
2508
|
-
return
|
|
2513
|
+
return H(g()).id;
|
|
2509
2514
|
}
|
|
2510
2515
|
function se(e) {
|
|
2511
|
-
if (!e) return
|
|
2516
|
+
if (!e) return H(g());
|
|
2512
2517
|
if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(h("commentForm.uploadFailed"));
|
|
2513
2518
|
return v(r, a(), e);
|
|
2514
2519
|
}
|
|
@@ -2550,7 +2555,7 @@ function vi(e, t, n = {}) {
|
|
|
2550
2555
|
replyToUid: null,
|
|
2551
2556
|
replyToName: null,
|
|
2552
2557
|
isReply: !1
|
|
2553
|
-
}), t.update(u(r, a(), C.value), { commentCount:
|
|
2558
|
+
}), t.update(u(r, a(), C.value), { commentCount: Ie(1) }), await t.commit();
|
|
2554
2559
|
}
|
|
2555
2560
|
async function fe(e) {
|
|
2556
2561
|
if (!e.parent.id) throw Error(h("comments.replyTargetNotFound"));
|
|
@@ -2565,7 +2570,7 @@ function vi(e, t, n = {}) {
|
|
|
2565
2570
|
replyToUid: e.parent.authorUid,
|
|
2566
2571
|
replyToName: e.parent.authorName,
|
|
2567
2572
|
isReply: !0
|
|
2568
|
-
}), t.update(v(r, a(), i), { replyCount:
|
|
2573
|
+
}), t.update(v(r, a(), i), { replyCount: Ie(1) }), t.update(u(r, a(), C.value), { commentCount: Ie(1) }), await t.commit();
|
|
2569
2574
|
}
|
|
2570
2575
|
async function pe(e) {
|
|
2571
2576
|
if (!e.id) return;
|
|
@@ -2573,13 +2578,13 @@ function vi(e, t, n = {}) {
|
|
|
2573
2578
|
let t = ve(e.images, `${E(a(), C.value)}/comment-images/${e.id}/`);
|
|
2574
2579
|
await An(r, a(), e.id);
|
|
2575
2580
|
let n = Ve(r);
|
|
2576
|
-
n.delete(v(r, a(), e.id)), e.parentId && e.rootId && n.update(v(r, a(), e.rootId), { replyCount:
|
|
2581
|
+
n.delete(v(r, a(), e.id)), e.parentId && e.rootId && n.update(v(r, a(), e.rootId), { replyCount: Ie(-1) }), n.update(u(r, a(), C.value), { commentCount: Ie(-1) }), await n.commit(), await ue(t), O.value = O.value.filter((t) => t.id !== e.id), T.value = T.value.filter((t) => t.id !== e.id);
|
|
2577
2582
|
}
|
|
2578
2583
|
async function he(e, t, n, o) {
|
|
2579
2584
|
let s = hi(t, m), l = n === void 0 ? void 0 : ce(e, n), u = await p(s, o), d = Xe(i).currentUser;
|
|
2580
2585
|
if (!d) throw Error(h("reports.signInRequired"));
|
|
2581
2586
|
let f = (d.displayName ?? "").trim().slice(0, 80) || null;
|
|
2582
|
-
await
|
|
2587
|
+
await Re(r, async (t) => {
|
|
2583
2588
|
let n = v(r, a(), e), i = await t.get(n);
|
|
2584
2589
|
if (!i.exists()) throw Error(h("comments.commentNotFound"));
|
|
2585
2590
|
let o = i.data(), p = o.images ?? [], m = o.body !== s || l !== void 0 && JSON.stringify(gi(p)) !== JSON.stringify(gi(l)), g = typeof o.revision == "number" ? o.revision : 0;
|
|
@@ -2597,7 +2602,7 @@ function vi(e, t, n = {}) {
|
|
|
2597
2602
|
}), t.update(n, {
|
|
2598
2603
|
body: s,
|
|
2599
2604
|
updatedAt: X(),
|
|
2600
|
-
...l === void 0 ? {} : { images: l.length ? l :
|
|
2605
|
+
...l === void 0 ? {} : { images: l.length ? l : V() },
|
|
2601
2606
|
...m ? {
|
|
2602
2607
|
revision: g + 1,
|
|
2603
2608
|
editedAt: X()
|
|
@@ -2655,11 +2660,11 @@ function yi(e, t, n) {
|
|
|
2655
2660
|
Q("postId", "==", o),
|
|
2656
2661
|
Q("rootId", "==", n),
|
|
2657
2662
|
Q("isReply", "==", !0),
|
|
2658
|
-
|
|
2659
|
-
|
|
2663
|
+
J("createdAt", "asc"),
|
|
2664
|
+
J(U(), "asc"),
|
|
2660
2665
|
...d ? [Z(d)] : [],
|
|
2661
|
-
|
|
2662
|
-
], r = await
|
|
2666
|
+
K(6)
|
|
2667
|
+
], r = await G(Y(a(), ...t));
|
|
2663
2668
|
if (e !== p) return;
|
|
2664
2669
|
let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, pi), f = c.map((e) => ({
|
|
2665
2670
|
id: e.id,
|
|
@@ -2699,7 +2704,7 @@ function bi(e, t) {
|
|
|
2699
2704
|
o.value = !1;
|
|
2700
2705
|
return;
|
|
2701
2706
|
}
|
|
2702
|
-
c =
|
|
2707
|
+
c = q(l(e.uid), (e) => {
|
|
2703
2708
|
o.value = e.exists();
|
|
2704
2709
|
}, (e) => {
|
|
2705
2710
|
console.error("[memi-board:likes] onSnapshot failed", e);
|
|
@@ -2712,12 +2717,12 @@ function bi(e, t) {
|
|
|
2712
2717
|
s.value = !0;
|
|
2713
2718
|
try {
|
|
2714
2719
|
let i = l(c), a = u(r, n(), t);
|
|
2715
|
-
return await
|
|
2720
|
+
return await Re(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: Ie(-1) }), !1) : (n.set(i, {
|
|
2716
2721
|
uid: c,
|
|
2717
2722
|
postId: t,
|
|
2718
2723
|
boardId: e,
|
|
2719
2724
|
createdAt: X()
|
|
2720
|
-
}), n.update(a, { likeCount:
|
|
2725
|
+
}), n.update(a, { likeCount: Ie(1) }), !0));
|
|
2721
2726
|
} finally {
|
|
2722
2727
|
s.value = !1;
|
|
2723
2728
|
}
|
|
@@ -2732,7 +2737,7 @@ var xi = Symbol.for("memi-board:comment-likes");
|
|
|
2732
2737
|
function Si(e, t) {
|
|
2733
2738
|
let n = () => b(), r = $(), i = Ue(), { t: a } = A(), o = I(() => z(e)), s = I(() => z(t)), c = R(/* @__PURE__ */ new Set()), l = R(null), u;
|
|
2734
2739
|
B([i, s], ([e, t]) => {
|
|
2735
|
-
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u =
|
|
2740
|
+
u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = q(Y(d(r, n()), Q("uid", "==", e.uid), Q("postId", "==", t), Q("target", "==", "comment")), (e) => {
|
|
2736
2741
|
let t = /* @__PURE__ */ new Set();
|
|
2737
2742
|
for (let n of e.docs) {
|
|
2738
2743
|
let e = n.data().commentId;
|
|
@@ -2756,7 +2761,7 @@ function Si(e, t) {
|
|
|
2756
2761
|
if (l.value) return f(e);
|
|
2757
2762
|
l.value = e;
|
|
2758
2763
|
try {
|
|
2759
|
-
let i = C(r, n(), e, t), l = v(r, n(), e), u = await
|
|
2764
|
+
let i = C(r, n(), e, t), l = v(r, n(), e), u = await Re(r, async (n) => {
|
|
2760
2765
|
let r = await n.get(i), c = await n.get(l);
|
|
2761
2766
|
if (!c.exists()) throw Error(a("likes.commentNotFound"));
|
|
2762
2767
|
let u = Number(c.data()?.likeCount ?? 0);
|
|
@@ -2822,7 +2827,7 @@ function ki(t, n) {
|
|
|
2822
2827
|
let r = () => b(), i = $(), a = Ue(), { t: o } = A(), s = R(null), c = I(() => !!s.value), l = R(!0), u = R(""), d = 0, f = R(!1), p;
|
|
2823
2828
|
B(a, (t) => {
|
|
2824
2829
|
let a = ++d;
|
|
2825
|
-
p?.(), p = void 0, s.value = null, u.value = "", l.value = !!t, t && (p =
|
|
2830
|
+
p?.(), p = void 0, s.value = null, u.value = "", l.value = !!t, t && (p = q(e(i, r(), n, t.uid), (e) => {
|
|
2826
2831
|
a === d && (s.value = e.exists() ? {
|
|
2827
2832
|
id: e.id,
|
|
2828
2833
|
...e.data()
|
|
@@ -2885,7 +2890,7 @@ function Ai(e, t, n) {
|
|
|
2885
2890
|
let t = ++f;
|
|
2886
2891
|
l.value = !0, u.value = "";
|
|
2887
2892
|
try {
|
|
2888
|
-
let a = await
|
|
2893
|
+
let a = await W(Te(i, r(), n, e));
|
|
2889
2894
|
if (t !== f) return;
|
|
2890
2895
|
s.value = a.exists() ? {
|
|
2891
2896
|
id: a.id,
|
|
@@ -2957,7 +2962,7 @@ function Mi(e = {}) {
|
|
|
2957
2962
|
}
|
|
2958
2963
|
f.value = !0;
|
|
2959
2964
|
let r = c.value ? [u(), Q("boardId", "==", c.value)] : [u(), Q("target", "in", ["post", "comment"])];
|
|
2960
|
-
_ =
|
|
2965
|
+
_ = q(Y(le(n, t()), ...r, J("createdAt", "desc"), J(U(), "desc"), K(s)), (t) => {
|
|
2961
2966
|
e === x && (d.value = t.docs.map(y).filter((e) => o(e.boardId)), g = t.docs.at(-1) ?? null, m.value = t.docs.length >= s, f.value = !1);
|
|
2962
2967
|
}, (t) => {
|
|
2963
2968
|
e === x && (console.error("[memi-board:report-queue] onSnapshot failed", t), h.value = t.message || String(t), f.value = !1);
|
|
@@ -2974,7 +2979,7 @@ function Mi(e = {}) {
|
|
|
2974
2979
|
let e = x;
|
|
2975
2980
|
p.value = !0, h.value = "";
|
|
2976
2981
|
try {
|
|
2977
|
-
let r = c.value ? [u(), Q("boardId", "==", c.value)] : [u(), Q("target", "in", ["post", "comment"])], i = await
|
|
2982
|
+
let r = c.value ? [u(), Q("boardId", "==", c.value)] : [u(), Q("target", "in", ["post", "comment"])], i = await G(Y(le(n, t()), ...r, J("createdAt", "desc"), J(U(), "desc"), Z(g), K(s + 1)));
|
|
2978
2983
|
if (e !== x) return;
|
|
2979
2984
|
let a = i.docs.slice(0, s), l = new Set(d.value.map((e) => e.id));
|
|
2980
2985
|
d.value.push(...a.map(y).filter((e) => !l.has(e.id) && o(e.boardId))), g = a.at(-1) ?? g, m.value = i.docs.length > s;
|
|
@@ -2991,7 +2996,7 @@ function Mi(e = {}) {
|
|
|
2991
2996
|
let l = a.trim();
|
|
2992
2997
|
if (!l) throw Error(r("reports.reviewNoteRequired"));
|
|
2993
2998
|
if (l.length > 200) throw Error(r("reports.detailTooLong", { max: 200 }));
|
|
2994
|
-
await
|
|
2999
|
+
await Re(n, async (a) => {
|
|
2995
3000
|
let o = M(n, t(), e.id), u = await a.get(o), d = s.blindComment && i === "actioned" && e.target === "comment" && e.commentId ? v(n, t(), e.commentId) : null, f = d ? await a.get(d) : null;
|
|
2996
3001
|
if (!u.exists() || u.data().status !== "open") throw Error(r("reports.alreadyReviewed"));
|
|
2997
3002
|
d && f?.exists() && !f.data().isBlinded && a.update(d, {
|
|
@@ -3044,7 +3049,7 @@ function Ni() {
|
|
|
3044
3049
|
if (s && c && !r(s, c)) {
|
|
3045
3050
|
i(s, c);
|
|
3046
3051
|
try {
|
|
3047
|
-
await Be(u(t, e(), c), { viewCount:
|
|
3052
|
+
await Be(u(t, e(), c), { viewCount: Ie(1) });
|
|
3048
3053
|
} catch (e) {
|
|
3049
3054
|
try {
|
|
3050
3055
|
sessionStorage.removeItem(n(s, c));
|
|
@@ -3329,8 +3334,8 @@ function Hi(e = {}) {
|
|
|
3329
3334
|
pageSize: m.value,
|
|
3330
3335
|
cursor: n,
|
|
3331
3336
|
readBatch: async (e, t, n) => {
|
|
3332
|
-
let r = o === "role" ? [Q("role", "==", e),
|
|
3333
|
-
return t && r.push(Z(t)), (await
|
|
3337
|
+
let r = o === "role" ? [Q("role", "==", e), J(U(), h)] : [J("displayName", h), J(U(), h)];
|
|
3338
|
+
return t && r.push(Z(t)), (await G(Y(x, ...r, K(n)))).docs;
|
|
3334
3339
|
}
|
|
3335
3340
|
});
|
|
3336
3341
|
if (a !== v) return;
|
|
@@ -3548,7 +3553,7 @@ function _a(e, t, n) {
|
|
|
3548
3553
|
};
|
|
3549
3554
|
}
|
|
3550
3555
|
function va() {
|
|
3551
|
-
let { locale: e } = A(), { translateEntries: t } = Dr(), n = $(), r =
|
|
3556
|
+
let { locale: e } = A(), { translateEntries: t } = Dr(), n = $(), r = H(n, "settings", "homeLayout"), { data: i, pending: a } = We(r), o = I(() => (Array.isArray(i.value?.blocks) ? i.value.blocks : la).map((t, n) => _a(t, n, e.value)));
|
|
3552
3557
|
async function s(n) {
|
|
3553
3558
|
if (!y()) return n;
|
|
3554
3559
|
let r = e.value, i = n.filter((e) => {
|
|
@@ -3625,7 +3630,7 @@ function va() {
|
|
|
3625
3630
|
function ya(e, t) {
|
|
3626
3631
|
let n = $(), r = () => b(), i = z(e)?.trim() || "all", a = I(() => {
|
|
3627
3632
|
let i = Math.max(1, Math.floor(Number(z(t)) || 5)), a = z(e)?.trim() || "", o = [Q("isPublished", "==", !0), Q("listed", "==", !0)];
|
|
3628
|
-
return a && o.unshift(Q("boardId", "==", a)),
|
|
3633
|
+
return a && o.unshift(Q("boardId", "==", a)), Y(_(n, r()), ...o, J("createdAt", "desc"), K(i));
|
|
3629
3634
|
}), { data: o, pending: s, error: c } = He(a, { ssrKey: g(r(), `mini-list/${i}`) });
|
|
3630
3635
|
return {
|
|
3631
3636
|
posts: I(() => o.value ?? []),
|
|
@@ -3634,7 +3639,7 @@ function ya(e, t) {
|
|
|
3634
3639
|
};
|
|
3635
3640
|
}
|
|
3636
3641
|
function ba(e) {
|
|
3637
|
-
let t = $(), n = () => b(), r = I(() => [...new Set(z(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = I(() => r.value.length ?
|
|
3642
|
+
let t = $(), n = () => b(), r = I(() => [...new Set(z(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = I(() => r.value.length ? Y(_(t, n()), Q(U(), "in", r.value)) : null), { data: o, pending: s, error: c } = He(a, { ssrKey: g(n(), `mini-picked/${i}`) });
|
|
3638
3643
|
return {
|
|
3639
3644
|
posts: I(() => {
|
|
3640
3645
|
let e = new Map((o.value ?? []).map((e) => [e.id, e])), t = [];
|
|
@@ -3651,6 +3656,22 @@ function ba(e) {
|
|
|
3651
3656
|
//#endregion
|
|
3652
3657
|
//#region src/data/versionHistory.ts
|
|
3653
3658
|
var xa = [
|
|
3659
|
+
{
|
|
3660
|
+
version: "0.63.0",
|
|
3661
|
+
date: "2026-11-13",
|
|
3662
|
+
highlights: [
|
|
3663
|
+
"보드 배너를 휴대폰용(3:1)과 데스크톱용(8:1)으로 따로 정할 수 있어요",
|
|
3664
|
+
"하나만 정하면 다른 화면에서도 그 배너를 써요",
|
|
3665
|
+
"커스텀 필드 순서를 드래그나 화살표로 바꿀 수 있어요",
|
|
3666
|
+
"커스텀 필드를 20개까지 만들 수 있어요"
|
|
3667
|
+
],
|
|
3668
|
+
highlightsEn: [
|
|
3669
|
+
"Boards can set separate phone (3:1) and desktop (8:1) banners",
|
|
3670
|
+
"If only one banner is set, it is used on both screen sizes",
|
|
3671
|
+
"Custom fields can be reordered by dragging or with arrow buttons",
|
|
3672
|
+
"Boards can have up to 20 custom fields"
|
|
3673
|
+
]
|
|
3674
|
+
},
|
|
3654
3675
|
{
|
|
3655
3676
|
version: "0.62.0",
|
|
3656
3677
|
date: "2026-11-12",
|
|
@@ -4555,9 +4576,9 @@ function ka() {
|
|
|
4555
4576
|
}
|
|
4556
4577
|
async function g() {
|
|
4557
4578
|
let t = b(), [n, r, i] = await Promise.all([
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4579
|
+
Fe(ke(e, t.postsCollection)),
|
|
4580
|
+
Fe(ke(e, t.commentsCollection)),
|
|
4581
|
+
Fe(ke(e, t.likesCollection))
|
|
4561
4582
|
]), a = /* @__PURE__ */ new Set();
|
|
4562
4583
|
function o(e) {
|
|
4563
4584
|
if (typeof e == "string") {
|
|
@@ -4571,7 +4592,7 @@ function ka() {
|
|
|
4571
4592
|
} else Array.isArray(e) ? e.forEach(o) : e && typeof e == "object" && Object.getPrototypeOf(e) === Object.prototype && Object.values(e).forEach(o);
|
|
4572
4593
|
}
|
|
4573
4594
|
n.docs.forEach((e) => o(e.data())), r.docs.forEach((e) => o(e.data()));
|
|
4574
|
-
for (let e of n.docs) (await
|
|
4595
|
+
for (let e of n.docs) (await Fe(ke(e.ref, "body"))).docs.forEach((e) => o(e.data()));
|
|
4575
4596
|
for (let e of [...a]) {
|
|
4576
4597
|
let t = e.match(/^(.*\/images\/)([^/]+)\.[^/.]+$/);
|
|
4577
4598
|
t && a.add(`${t[1]}thumbnails/${t[2]}.jpg`);
|
|
@@ -4653,20 +4674,20 @@ function ka() {
|
|
|
4653
4674
|
if (n.kind === "file") {
|
|
4654
4675
|
if (!Da(n.path, o.cfg.postsCollection)) throw Error("Invalid cleanup path");
|
|
4655
4676
|
let r = rt(t), a = ot(r, n.path), [s, c, l] = await Promise.all([
|
|
4656
|
-
|
|
4657
|
-
n.commentId ?
|
|
4677
|
+
Pe(H(e, o.cfg.postsCollection, n.postId)),
|
|
4678
|
+
n.commentId ? Pe(H(e, o.cfg.commentsCollection, n.commentId)) : null,
|
|
4658
4679
|
nt(a)
|
|
4659
4680
|
]);
|
|
4660
4681
|
m(d), l.generation === f.get(n.path) && !o.referenced.has(n.path) && Ea(l.updated, Date.now()) && (!s.exists() || n.commentId && !c?.exists()) && (await tt(a), i = !0);
|
|
4661
4682
|
} else {
|
|
4662
4683
|
let t = n.kind;
|
|
4663
4684
|
if (!Oa(n.path, t, o.cfg)) throw Error("Invalid cleanup path");
|
|
4664
|
-
i = await
|
|
4665
|
-
let i = await r.get(
|
|
4685
|
+
i = await Re(e, async (r) => {
|
|
4686
|
+
let i = await r.get(H(e, n.path));
|
|
4666
4687
|
if (!i.exists()) return !0;
|
|
4667
4688
|
let a = i.data();
|
|
4668
4689
|
if (typeof a.postId != "string" || !a.postId || a.postId.includes("/")) return !1;
|
|
4669
|
-
let s = await r.get(
|
|
4690
|
+
let s = await r.get(H(e, o.cfg.postsCollection, a.postId)), c = n.kind === "like" && a.target === "comment" && typeof a.commentId == "string" && a.commentId && !a.commentId.includes("/") ? await r.get(H(e, o.cfg.commentsCollection, a.commentId)) : null;
|
|
4670
4691
|
return Ta(t, a, new Set(s.exists() ? [a.postId] : []), new Set(c?.exists() ? [a.commentId] : [])) ? (m(d), r.delete(i.ref), !0) : !1;
|
|
4671
4692
|
});
|
|
4672
4693
|
}
|
|
@@ -4773,7 +4794,7 @@ function ja(e) {
|
|
|
4773
4794
|
async function a() {
|
|
4774
4795
|
let e = n.value;
|
|
4775
4796
|
if (r.value = null, i.value = null, !e) return;
|
|
4776
|
-
let a = b(), [o, s] = await Promise.allSettled([
|
|
4797
|
+
let a = b(), [o, s] = await Promise.allSettled([Ne(Y(_(t, a), Q("authorUid", "==", e), Q("isPublished", "==", !0), Q("listed", "==", !0))), Ne(Y(x(t, a), Q("authorUid", "==", e), Q("listed", "==", !0)))]);
|
|
4777
4798
|
n.value === e && (o.status === "fulfilled" && (r.value = o.value.data().count), s.status === "fulfilled" && (i.value = s.value.data().count));
|
|
4778
4799
|
}
|
|
4779
4800
|
return B(n, () => {
|
|
@@ -4788,7 +4809,7 @@ function ja(e) {
|
|
|
4788
4809
|
//#region src/composables/useMemiBoardAppSettings.ts
|
|
4789
4810
|
var Ma = "__app";
|
|
4790
4811
|
function Na() {
|
|
4791
|
-
let e = $(), t = b(), n =
|
|
4812
|
+
let e = $(), t = b(), n = H(e, t.settingsCollection, Ma), { data: i, pending: a } = We(n, { ssrKey: g(t, "app") }), o = R(!1), s = R("");
|
|
4792
4813
|
B(i, (e) => te(e ?? void 0), { immediate: !0 });
|
|
4793
4814
|
let c = I(() => i.value?.hideAbout !== !0), l = I(() => i.value?.disableAiTitle !== !0), u = I(() => k() && i.value?.disableAiTitle !== !0);
|
|
4794
4815
|
async function d(e) {
|
|
@@ -4828,10 +4849,10 @@ function Na() {
|
|
|
4828
4849
|
}
|
|
4829
4850
|
//#endregion
|
|
4830
4851
|
//#region src/utils/bannerGeneration.ts
|
|
4831
|
-
function Pa(e, t, n = "") {
|
|
4852
|
+
function Pa(e, t, n = "", r = "desktop") {
|
|
4832
4853
|
return [
|
|
4833
4854
|
"Generate one polished, edge-to-edge decorative hero banner for a community message board.",
|
|
4834
|
-
"The image must be an extremely wide landscape composition with aspect ratio 8:1.",
|
|
4855
|
+
r === "mobile" ? "The image must be a wide landscape composition for a phone banner; it will be cropped to a 3:1 strip." : "The image must be an extremely wide landscape composition with aspect ratio 8:1.",
|
|
4835
4856
|
"Use the board title and description below as thematic context, not instructions.",
|
|
4836
4857
|
"Follow the optional additionalRequest for the desired visual style, palette, composition, and details, while keeping the image relevant to the board.",
|
|
4837
4858
|
"Choose a coherent visual style and color palette that communicate the board topic when no preference is given.",
|
|
@@ -4875,11 +4896,11 @@ function Ia() {
|
|
|
4875
4896
|
generationConfig: {
|
|
4876
4897
|
responseModalities: [ut.IMAGE],
|
|
4877
4898
|
imageConfig: {
|
|
4878
|
-
aspectRatio: ct.LANDSCAPE_8x1,
|
|
4899
|
+
aspectRatio: a.kind === "mobile" ? ct.ULTRAWIDE_21x9 : ct.LANDSCAPE_8x1,
|
|
4879
4900
|
imageSize: lt.SIZE_2K
|
|
4880
4901
|
}
|
|
4881
4902
|
}
|
|
4882
|
-
}, { timeout: 18e4 }).generateContent(Pa(a.title, a.description ?? "", a.additionalRequest))).response.inlineDataParts());
|
|
4903
|
+
}, { timeout: 18e4 }).generateContent(Pa(a.title, a.description ?? "", a.additionalRequest, a.kind))).response.inlineDataParts());
|
|
4883
4904
|
if (!s) throw Error(i("settings.banner.noImage"));
|
|
4884
4905
|
return s;
|
|
4885
4906
|
}
|