memi-board 0.37.2 → 0.38.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 +6 -5
- package/dist/composables/useMemiBoardCommentFeed.d.ts +2 -1
- package/dist/composables/useMemiBoardComments.d.ts +2 -0
- package/dist/composables/useMemiBoardUserComments.d.ts +2 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +381 -358
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/CategorySettings.vue +22 -4
- package/dist/runtime/components/CommentForm.vue +12 -10
- package/dist/runtime/components/Settings.vue +17 -3
- package/dist/types.d.ts +7 -2
- package/dist/utils/boardAccess.d.ts +10 -0
- package/docs/firestore.indexes.json.example +51 -0
- package/docs/firestore.rules.example +36 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -174,19 +174,25 @@ function Be(e, t) {
|
|
|
174
174
|
function Ve(e, t, n, r) {
|
|
175
175
|
return e ? t ? !0 : n ? Be(e, r) : !1 : !1;
|
|
176
176
|
}
|
|
177
|
+
function He(e, t, n, r) {
|
|
178
|
+
if (!e) return !1;
|
|
179
|
+
if (r?.visibility === "hidden") return Ve(e, t, n, r);
|
|
180
|
+
let i = r?.commentWriteRole ?? "user";
|
|
181
|
+
return i === "admin" ? t : i !== "staff" || Ve(e, t, n, r);
|
|
182
|
+
}
|
|
177
183
|
//#endregion
|
|
178
184
|
//#region src/composables/boardLookup.ts
|
|
179
|
-
var
|
|
180
|
-
function Ue(e) {
|
|
181
|
-
He = e;
|
|
182
|
-
}
|
|
185
|
+
var Ue;
|
|
183
186
|
function We(e) {
|
|
184
|
-
|
|
187
|
+
Ue = e;
|
|
188
|
+
}
|
|
189
|
+
function Ge(e) {
|
|
190
|
+
return Ue?.(e);
|
|
185
191
|
}
|
|
186
192
|
//#endregion
|
|
187
193
|
//#region src/utils/moderation-strike.ts
|
|
188
|
-
var
|
|
189
|
-
function
|
|
194
|
+
var Ke = 10, qe = 864e5;
|
|
195
|
+
function Je(e, t, n = Date.now(), r = qe) {
|
|
190
196
|
let i = Math.max(0, Math.floor(Number(e) || 0));
|
|
191
197
|
if (i <= 0) return 0;
|
|
192
198
|
let a = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -194,7 +200,7 @@ function qe(e, t, n = Date.now(), r = Ke) {
|
|
|
194
200
|
let o = Math.floor((n - a) / r);
|
|
195
201
|
return Math.max(0, i - o);
|
|
196
202
|
}
|
|
197
|
-
function
|
|
203
|
+
function Ye(e, t, n = Date.now(), r = 10, i = qe) {
|
|
198
204
|
let a = Math.max(0, Math.floor(Number(e) || 0));
|
|
199
205
|
if (a < r) return null;
|
|
200
206
|
let o = typeof t == "number" && Number.isFinite(t) ? t : null;
|
|
@@ -202,17 +208,17 @@ function Je(e, t, n = Date.now(), r = 10, i = Ke) {
|
|
|
202
208
|
let s = o + (a - (r - 1)) * i;
|
|
203
209
|
return s > n ? s : null;
|
|
204
210
|
}
|
|
205
|
-
function
|
|
206
|
-
return
|
|
211
|
+
function Xe(e, t, n = Date.now(), r = 10, i = qe) {
|
|
212
|
+
return Ye(e, t, n, r, i) != null;
|
|
207
213
|
}
|
|
208
|
-
function
|
|
214
|
+
function Ze(e, t = Date.now()) {
|
|
209
215
|
let n = Math.max(0, e - t), r = Math.ceil(n / 36e5);
|
|
210
216
|
if (r <= 0) return "곧 해제";
|
|
211
217
|
if (r < 24) return `약 ${r}시간 후 해제`;
|
|
212
218
|
let i = Math.floor(r / 24), a = r % 24;
|
|
213
219
|
return a === 0 ? `약 ${i}일 후 해제` : `약 ${i}일 ${a}시간 후 해제`;
|
|
214
220
|
}
|
|
215
|
-
function
|
|
221
|
+
function Qe(e) {
|
|
216
222
|
if (e == null) return null;
|
|
217
223
|
if (typeof e == "number" && Number.isFinite(e)) return e;
|
|
218
224
|
if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
|
|
@@ -224,26 +230,26 @@ function Ze(e) {
|
|
|
224
230
|
}
|
|
225
231
|
//#endregion
|
|
226
232
|
//#region src/composables/useMemiBoardAuth.ts
|
|
227
|
-
var
|
|
228
|
-
function
|
|
233
|
+
var $e = "__MEMI_BOARD_AUTH_STATE__";
|
|
234
|
+
function et() {
|
|
229
235
|
let e = globalThis;
|
|
230
|
-
return e[
|
|
236
|
+
return e[$e] || (e[$e] = {
|
|
231
237
|
role: P("user"),
|
|
232
238
|
rolePending: P(!0),
|
|
233
239
|
rawBlockCount: P(0),
|
|
234
240
|
blockAtMs: P(null),
|
|
235
241
|
watchStarted: !1
|
|
236
|
-
}), e[
|
|
242
|
+
}), e[$e];
|
|
237
243
|
}
|
|
238
|
-
function
|
|
239
|
-
let e = u(), t =
|
|
244
|
+
function tt() {
|
|
245
|
+
let e = u(), t = et(), n = Q(), r = ue(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: c } = t;
|
|
240
246
|
function l() {
|
|
241
247
|
let t = e.moderation?.blockBanThreshold;
|
|
242
248
|
return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
|
|
243
249
|
}
|
|
244
250
|
function d() {
|
|
245
251
|
let t = e.moderation?.blockBanDecayMs;
|
|
246
|
-
return typeof t == "number" && t > 0 ? t :
|
|
252
|
+
return typeof t == "number" && t > 0 ? t : qe;
|
|
247
253
|
}
|
|
248
254
|
function f(e) {
|
|
249
255
|
return x(n, _(), e);
|
|
@@ -253,7 +259,7 @@ function et() {
|
|
|
253
259
|
s.value = 0, c.value = null;
|
|
254
260
|
return;
|
|
255
261
|
}
|
|
256
|
-
s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), c.value =
|
|
262
|
+
s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), c.value = Qe(e.moderationBlockAt);
|
|
257
263
|
}
|
|
258
264
|
async function m(e) {
|
|
259
265
|
let t = f(e.uid), r = g(n, _(), e.uid), i = await R(t);
|
|
@@ -299,14 +305,14 @@ function et() {
|
|
|
299
305
|
o.value = !1;
|
|
300
306
|
}
|
|
301
307
|
}, { immediate: !0 }));
|
|
302
|
-
let v = M(() => !!i.value), y = M(() => a.value === "admin"), b = M(() => a.value === "staff"), S = M(() => y.value || b.value), C = M(() =>
|
|
308
|
+
let v = M(() => !!i.value), y = M(() => a.value === "admin"), b = M(() => a.value === "staff"), S = M(() => y.value || b.value), C = M(() => Je(s.value, c.value, Date.now(), d())), w = M(() => {
|
|
303
309
|
let e = l();
|
|
304
|
-
return e <= 0 ? !1 :
|
|
310
|
+
return e <= 0 ? !1 : Xe(s.value, c.value, Date.now(), e, d());
|
|
305
311
|
}), T = M(() => {
|
|
306
312
|
let e = l();
|
|
307
313
|
if (e <= 0) return null;
|
|
308
|
-
let t =
|
|
309
|
-
return t == null ? null :
|
|
314
|
+
let t = Ye(s.value, c.value, Date.now(), e, d());
|
|
315
|
+
return t == null ? null : Ze(t);
|
|
310
316
|
}), E = M(() => {
|
|
311
317
|
if (!w.value) return null;
|
|
312
318
|
let e = T.value;
|
|
@@ -333,9 +339,9 @@ function et() {
|
|
|
333
339
|
});
|
|
334
340
|
let a = (r.exists() ? Math.max(0, Math.floor(Number(r.data().moderationBlockCount) || 0)) : 0) + 1, o = Date.now();
|
|
335
341
|
s.value = a, c.value = o;
|
|
336
|
-
let u =
|
|
342
|
+
let u = Je(a, o, o, d()), p = Xe(a, o, o, t, d()), m = ` (경고 ${u}/${t})`;
|
|
337
343
|
if (p) {
|
|
338
|
-
let e =
|
|
344
|
+
let e = Ye(a, o, o, t, d()), n = e == null ? null : Ze(e, o);
|
|
339
345
|
m = n ? ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다 (${n}).` : ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다.`;
|
|
340
346
|
}
|
|
341
347
|
return {
|
|
@@ -366,7 +372,7 @@ function et() {
|
|
|
366
372
|
await ge(O());
|
|
367
373
|
}
|
|
368
374
|
function P(e) {
|
|
369
|
-
return Ve(i.value?.uid, y.value, b.value, e ?
|
|
375
|
+
return Ve(i.value?.uid, y.value, b.value, e ? Ge(e) : void 0);
|
|
370
376
|
}
|
|
371
377
|
function F(e) {
|
|
372
378
|
return v.value && (i.value?.uid === e.authorUid || P(e.boardId));
|
|
@@ -404,7 +410,7 @@ function et() {
|
|
|
404
410
|
}
|
|
405
411
|
//#endregion
|
|
406
412
|
//#region src/utils/extractEditorImageUrls.ts
|
|
407
|
-
function
|
|
413
|
+
function nt(e) {
|
|
408
414
|
if (!e) return [];
|
|
409
415
|
let t = /* @__PURE__ */ new Set();
|
|
410
416
|
for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&", "&"));
|
|
@@ -413,7 +419,7 @@ function tt(e) {
|
|
|
413
419
|
}
|
|
414
420
|
//#endregion
|
|
415
421
|
//#region src/utils/storagePath.ts
|
|
416
|
-
function
|
|
422
|
+
function rt(e) {
|
|
417
423
|
try {
|
|
418
424
|
let t = new URL(e), n = t.hostname.toLowerCase();
|
|
419
425
|
if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
|
|
@@ -423,104 +429,104 @@ function nt(e) {
|
|
|
423
429
|
return null;
|
|
424
430
|
}
|
|
425
431
|
}
|
|
426
|
-
function
|
|
432
|
+
function it(e) {
|
|
427
433
|
return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
|
|
428
434
|
}
|
|
429
435
|
//#endregion
|
|
430
436
|
//#region src/utils/deletePostCascade.ts
|
|
431
|
-
async function
|
|
437
|
+
async function at(e) {
|
|
432
438
|
let t = await be(e);
|
|
433
|
-
await Promise.all([...t.items.map((e) => ve(e)), ...t.prefixes.map((e) =>
|
|
439
|
+
await Promise.all([...t.items.map((e) => ve(e)), ...t.prefixes.map((e) => at(e))]);
|
|
434
440
|
}
|
|
435
|
-
async function
|
|
441
|
+
async function ot(e, t) {
|
|
436
442
|
let n = [...new Set([...t].filter(Boolean))];
|
|
437
443
|
await Promise.all(n.map((t) => ve(xe(e, t)).catch(() => {})));
|
|
438
444
|
}
|
|
439
|
-
var
|
|
440
|
-
async function
|
|
445
|
+
var st = 450;
|
|
446
|
+
async function ct(e, t) {
|
|
441
447
|
let n = await z(t);
|
|
442
|
-
for (let t = 0; t < n.docs.length; t +=
|
|
448
|
+
for (let t = 0; t < n.docs.length; t += st) {
|
|
443
449
|
let r = X(e);
|
|
444
|
-
n.docs.slice(t, t +
|
|
450
|
+
n.docs.slice(t, t + st).forEach((e) => r.delete(e.ref)), await r.commit();
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
|
-
async function ct(e, t, n) {
|
|
448
|
-
await st(e, W(A(e, t), Y("postId", "==", n)));
|
|
449
|
-
}
|
|
450
453
|
async function lt(e, t, n) {
|
|
451
|
-
await
|
|
454
|
+
await ct(e, W(A(e, t), Y("postId", "==", n)));
|
|
452
455
|
}
|
|
453
456
|
async function ut(e, t, n) {
|
|
454
|
-
await
|
|
457
|
+
await ct(e, W(o(e, t), Y("postId", "==", n)));
|
|
458
|
+
}
|
|
459
|
+
async function dt(e, t, n) {
|
|
460
|
+
await ct(e, W(A(e, t), Y("commentId", "==", n)));
|
|
455
461
|
}
|
|
456
|
-
async function
|
|
462
|
+
async function ft(e, n, r, i) {
|
|
457
463
|
let a = t(e, r, i), o = s(e, r, i), [c, l] = await Promise.all([R(a), R(o)]);
|
|
458
464
|
if (!c.exists()) return;
|
|
459
465
|
let u = c.data(), d = l.exists() ? String(l.data().content ?? "") : "";
|
|
460
|
-
await
|
|
466
|
+
await ct(e, W(O(e, r), Y("postId", "==", i))), await lt(e, r, i), await ut(e, r, i);
|
|
461
467
|
let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
|
|
462
468
|
for (let e of u.attachments ?? []) {
|
|
463
469
|
if (!e?.path) continue;
|
|
464
470
|
f.add(e.path);
|
|
465
|
-
let t =
|
|
471
|
+
let t = it(e.path);
|
|
466
472
|
t && t !== i && p.add(t);
|
|
467
473
|
}
|
|
468
|
-
for (let e of
|
|
469
|
-
let t =
|
|
474
|
+
for (let e of nt(d)) {
|
|
475
|
+
let t = rt(e);
|
|
470
476
|
if (!t) continue;
|
|
471
477
|
f.add(t);
|
|
472
478
|
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
473
479
|
n !== t && n.includes("/images/thumbnails/") && f.add(n);
|
|
474
480
|
let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
475
481
|
r && !t.includes("/thumbnails/") && f.add(`${r[1]}thumbnails/${r[2]}.jpg`);
|
|
476
|
-
let a =
|
|
482
|
+
let a = it(t);
|
|
477
483
|
a && a !== i && p.add(a);
|
|
478
484
|
}
|
|
479
|
-
await te(o), await
|
|
480
|
-
for (let e of p) await
|
|
481
|
-
await
|
|
485
|
+
await te(o), await at(xe(n, k(r, i)));
|
|
486
|
+
for (let e of p) await at(xe(n, k(r, e)));
|
|
487
|
+
await ot(n, f), await te(a);
|
|
482
488
|
}
|
|
483
489
|
//#endregion
|
|
484
490
|
//#region src/utils/postBody.ts
|
|
485
|
-
function
|
|
491
|
+
function pt(e) {
|
|
486
492
|
return String(e || "").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<img\b[^>]*>/gi, " ").replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, " ").replace(/<video\b[\s\S]*?<\/video>/gi, " ").replace(/<audio\b[\s\S]*?<\/audio>/gi, " ").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/ /g, " ").replace(/&[a-z]+;/gi, " ").replace(/&#\d+;/g, " ").replace(/[\u200B-\u200D\uFEFF\u00AD]/g, "").replace(/\s+/g, " ").trim();
|
|
487
493
|
}
|
|
488
|
-
function
|
|
489
|
-
let n =
|
|
494
|
+
function mt(e, t = 40) {
|
|
495
|
+
let n = pt(e);
|
|
490
496
|
if (!n) return "";
|
|
491
497
|
if (n.length <= t) return n;
|
|
492
498
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
493
499
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
494
500
|
}
|
|
495
|
-
function
|
|
496
|
-
return
|
|
501
|
+
function ht(e) {
|
|
502
|
+
return pt(e).length > 0;
|
|
497
503
|
}
|
|
498
|
-
function
|
|
504
|
+
function gt(e, t) {
|
|
499
505
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
500
506
|
}
|
|
501
|
-
function
|
|
502
|
-
return !
|
|
507
|
+
function _t(e) {
|
|
508
|
+
return !ht(e);
|
|
503
509
|
}
|
|
504
510
|
//#endregion
|
|
505
511
|
//#region src/utils/postPreview.ts
|
|
506
|
-
function
|
|
512
|
+
function vt(e) {
|
|
507
513
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
508
514
|
}
|
|
509
|
-
function
|
|
515
|
+
function yt(e) {
|
|
510
516
|
if (e) try {
|
|
511
|
-
let t = new URL(
|
|
517
|
+
let t = new URL(vt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
512
518
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
513
519
|
if (n.endsWith("youtube.com") || n === "youtube-nocookie.com") return t.pathname.startsWith("/embed/") || t.pathname.startsWith("/shorts/") ? t.pathname.split("/")[2] : t.searchParams.get("v") || void 0;
|
|
514
520
|
} catch {
|
|
515
521
|
return;
|
|
516
522
|
}
|
|
517
523
|
}
|
|
518
|
-
function
|
|
519
|
-
let t =
|
|
524
|
+
function bt(e) {
|
|
525
|
+
let t = yt(e.videoUrl);
|
|
520
526
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
521
527
|
}
|
|
522
|
-
function
|
|
523
|
-
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ?
|
|
528
|
+
function xt(e, t = []) {
|
|
529
|
+
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? vt(r[1]) : a, s = o ? yt(o) : void 0, c = n ? vt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
524
530
|
return {
|
|
525
531
|
summary: e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|blockquote)>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/[^\S\n]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{2,}/g, "\n").trim().slice(0, 160),
|
|
526
532
|
...c ? { previewImage: c } : {},
|
|
@@ -529,7 +535,7 @@ function bt(e, t = []) {
|
|
|
529
535
|
}
|
|
530
536
|
//#endregion
|
|
531
537
|
//#region src/composables/useMemiBoardSettings.ts
|
|
532
|
-
var
|
|
538
|
+
var St = [
|
|
533
539
|
{
|
|
534
540
|
id: "free",
|
|
535
541
|
label: "자유",
|
|
@@ -557,25 +563,25 @@ var xt = [
|
|
|
557
563
|
visibility: "public",
|
|
558
564
|
order: 2
|
|
559
565
|
}
|
|
560
|
-
],
|
|
561
|
-
function
|
|
566
|
+
], Ct = St;
|
|
567
|
+
function wt(e) {
|
|
562
568
|
return e === "hidden" ? "hidden" : "public";
|
|
563
569
|
}
|
|
564
|
-
function
|
|
570
|
+
function Tt(e, t, n) {
|
|
565
571
|
return {
|
|
566
572
|
id: e,
|
|
567
573
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
568
574
|
order: typeof t.order == "number" ? t.order : n,
|
|
569
575
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
570
|
-
visibility:
|
|
576
|
+
visibility: wt(t.visibility),
|
|
571
577
|
listView: t.listView ?? "default",
|
|
572
578
|
writeRole: t.writeRole ?? "user",
|
|
573
579
|
commentWriteRole: t.commentWriteRole ?? "user",
|
|
574
580
|
allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
|
|
575
581
|
};
|
|
576
582
|
}
|
|
577
|
-
function
|
|
578
|
-
let t = () => _(), n = Q(), r = le(), { isSignedIn: i, isAdmin: a, isStaff: o, user: s } =
|
|
583
|
+
function Et() {
|
|
584
|
+
let t = () => _(), n = Q(), r = le(), { isSignedIn: i, isAdmin: a, isStaff: o, user: s } = tt(), l = M(() => W(e(n, t()), U("order", "asc"))), { data: u, pending: d } = se(l, { ssrKey: w(t(), "boards") }), f = M(() => u.value.map((e, t) => Tt(e.id, e, t))), p = f, m = M(() => f.value.filter((e) => e.visibility !== "hidden")), h = m;
|
|
579
585
|
function g(e) {
|
|
580
586
|
if (e) return f.value.find((t) => t.id === e)?.label ?? e;
|
|
581
587
|
}
|
|
@@ -592,29 +598,32 @@ function Tt() {
|
|
|
592
598
|
return C(e) === "hidden";
|
|
593
599
|
}
|
|
594
600
|
let D = E;
|
|
595
|
-
function
|
|
601
|
+
function k(e) {
|
|
596
602
|
if (e) return f.value.find((t) => t.id === e);
|
|
597
603
|
}
|
|
598
|
-
|
|
599
|
-
function
|
|
600
|
-
return Ve(s.value?.uid, a.value, o.value, e ?
|
|
604
|
+
We((e) => k(e));
|
|
605
|
+
function A(e) {
|
|
606
|
+
return Ve(s.value?.uid, a.value, o.value, e ? k(e) : void 0);
|
|
601
607
|
}
|
|
602
|
-
let
|
|
603
|
-
async function
|
|
608
|
+
let j = M(() => f.value.filter((e) => A(e.id)));
|
|
609
|
+
async function N(e, t, r) {
|
|
604
610
|
let i;
|
|
605
611
|
for (;;) {
|
|
606
|
-
let a = await z(W(
|
|
612
|
+
let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
|
|
607
613
|
if (a.empty) break;
|
|
608
|
-
let o = X(n);
|
|
609
|
-
for (let e of a.docs) o.update(e.ref, { listed: r });
|
|
610
|
-
if (await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
614
|
+
let o = X(n), s = 0;
|
|
615
|
+
for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
|
|
616
|
+
if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
611
617
|
}
|
|
612
618
|
}
|
|
613
|
-
function
|
|
619
|
+
async function ee(e, r) {
|
|
620
|
+
await N(c(n, t()), e, r), await N(O(n, t()), e, r);
|
|
621
|
+
}
|
|
622
|
+
function P(e, t) {
|
|
614
623
|
return {
|
|
615
624
|
label: e.label.trim(),
|
|
616
625
|
description: (e.description ?? "").trim(),
|
|
617
|
-
visibility:
|
|
626
|
+
visibility: wt(e.visibility),
|
|
618
627
|
listView: e.listView ?? "default",
|
|
619
628
|
writeRole: e.writeRole ?? "user",
|
|
620
629
|
commentWriteRole: e.commentWriteRole ?? "user",
|
|
@@ -623,53 +632,53 @@ function Tt() {
|
|
|
623
632
|
updatedAt: G()
|
|
624
633
|
};
|
|
625
634
|
}
|
|
626
|
-
async function
|
|
635
|
+
async function F(e, r = e.order ?? 0) {
|
|
627
636
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
628
637
|
let a = e.id.trim(), o = e.label.trim();
|
|
629
638
|
if (!a || !o) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
630
|
-
let s =
|
|
639
|
+
let s = wt(e.visibility), c = P({
|
|
631
640
|
...e,
|
|
632
641
|
label: o,
|
|
633
642
|
visibility: s
|
|
634
643
|
}, r);
|
|
635
|
-
await K(v(n, t(), a),
|
|
644
|
+
await K(v(n, t(), a), c, { merge: !0 }), await ee(a, s !== "hidden");
|
|
636
645
|
}
|
|
637
|
-
let
|
|
638
|
-
async function
|
|
646
|
+
let I = F;
|
|
647
|
+
async function ne(e) {
|
|
639
648
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
640
649
|
let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
|
|
641
650
|
e.forEach((e, i) => {
|
|
642
651
|
let s = e.id.trim(), c = e.label.trim();
|
|
643
652
|
if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
644
|
-
let l =
|
|
653
|
+
let l = wt(e.visibility), u = P({
|
|
645
654
|
...e,
|
|
646
655
|
label: c,
|
|
647
656
|
visibility: l
|
|
648
657
|
}, i);
|
|
649
|
-
a.set(v(n, t(), s), u, { merge: !0 }), (r.get(s) ?? "public") !== l && o.push({
|
|
658
|
+
a.set(v(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
|
|
650
659
|
id: s,
|
|
651
660
|
listed: l !== "hidden"
|
|
652
661
|
});
|
|
653
662
|
}), await a.commit();
|
|
654
|
-
for (let e of o) await
|
|
663
|
+
for (let e of o) await ee(e.id, e.listed);
|
|
655
664
|
}
|
|
656
|
-
let
|
|
657
|
-
async function
|
|
665
|
+
let re = ne;
|
|
666
|
+
async function L(e) {
|
|
658
667
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
659
668
|
let a = e.trim();
|
|
660
669
|
if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
|
|
661
670
|
for (;;) {
|
|
662
671
|
let e = await z(W(c(n, t()), Y("boardId", "==", a), V(25)));
|
|
663
672
|
if (e.empty) break;
|
|
664
|
-
for (let i of e.docs) await
|
|
673
|
+
for (let i of e.docs) await ft(n, ye(r), t(), i.id);
|
|
665
674
|
}
|
|
666
675
|
await te(v(n, t(), e));
|
|
667
676
|
}
|
|
668
|
-
let
|
|
669
|
-
async function
|
|
677
|
+
let ie = L;
|
|
678
|
+
async function R() {
|
|
670
679
|
return Promise.resolve();
|
|
671
680
|
}
|
|
672
|
-
async function
|
|
681
|
+
async function B(e) {
|
|
673
682
|
let t = e.trim();
|
|
674
683
|
if (!t) throw Error("보드 이름을 입력해 주세요.");
|
|
675
684
|
if (!i.value) throw Error("로그인이 필요합니다.");
|
|
@@ -677,7 +686,7 @@ function Tt() {
|
|
|
677
686
|
if (n) return n.id;
|
|
678
687
|
let r = S(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
|
|
679
688
|
for (; s.has(a);) a = `${r}-${o++}`;
|
|
680
|
-
return await
|
|
689
|
+
return await F({
|
|
681
690
|
id: a,
|
|
682
691
|
label: t,
|
|
683
692
|
visibility: "public",
|
|
@@ -694,9 +703,9 @@ function Tt() {
|
|
|
694
703
|
boardDescription: b,
|
|
695
704
|
boardVisibility: C,
|
|
696
705
|
isBoardHidden: E,
|
|
697
|
-
getBoard:
|
|
698
|
-
canManageBoard:
|
|
699
|
-
managedBoards:
|
|
706
|
+
getBoard: k,
|
|
707
|
+
canManageBoard: A,
|
|
708
|
+
managedBoards: j,
|
|
700
709
|
categories: p,
|
|
701
710
|
publicCategories: h,
|
|
702
711
|
categoryLabel: y,
|
|
@@ -704,33 +713,33 @@ function Tt() {
|
|
|
704
713
|
categoryVisibility: T,
|
|
705
714
|
isCategoryHidden: D,
|
|
706
715
|
settingsPending: d,
|
|
707
|
-
ensureSettings:
|
|
708
|
-
addBoard:
|
|
709
|
-
addCategory:
|
|
710
|
-
saveBoard:
|
|
711
|
-
saveCategory:
|
|
712
|
-
saveBoards:
|
|
713
|
-
saveCategories:
|
|
714
|
-
deleteBoard:
|
|
715
|
-
deleteCategory:
|
|
716
|
-
DEFAULT_BOARDS:
|
|
717
|
-
DEFAULT_CATEGORIES:
|
|
716
|
+
ensureSettings: R,
|
|
717
|
+
addBoard: B,
|
|
718
|
+
addCategory: B,
|
|
719
|
+
saveBoard: F,
|
|
720
|
+
saveCategory: I,
|
|
721
|
+
saveBoards: ne,
|
|
722
|
+
saveCategories: re,
|
|
723
|
+
deleteBoard: L,
|
|
724
|
+
deleteCategory: ie,
|
|
725
|
+
DEFAULT_BOARDS: St,
|
|
726
|
+
DEFAULT_CATEGORIES: Ct
|
|
718
727
|
};
|
|
719
728
|
}
|
|
720
729
|
//#endregion
|
|
721
730
|
//#region src/composables/useMemiBoardPosts.ts
|
|
722
|
-
function
|
|
731
|
+
function Dt(e) {
|
|
723
732
|
if (!e || typeof e != "object") return !1;
|
|
724
733
|
let t = "code" in e ? String(e.code) : "";
|
|
725
734
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
726
735
|
}
|
|
727
|
-
function
|
|
736
|
+
function Ot(e) {
|
|
728
737
|
let t = F(e)?.trim();
|
|
729
738
|
if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
|
|
730
739
|
return t;
|
|
731
740
|
}
|
|
732
|
-
function
|
|
733
|
-
let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } =
|
|
741
|
+
function kt(e) {
|
|
742
|
+
let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } = Et(), o = () => _(), l = () => Ot(e), u = () => c(n, o()), d = (e) => t(n, o(), e), f = (e) => s(n, o(), e);
|
|
734
743
|
function p() {
|
|
735
744
|
return !i(l());
|
|
736
745
|
}
|
|
@@ -738,7 +747,7 @@ function Ot(e) {
|
|
|
738
747
|
return a(l())?.listView === "image";
|
|
739
748
|
}
|
|
740
749
|
function h(e) {
|
|
741
|
-
let t = m(), n =
|
|
750
|
+
let t = m(), n = ht(e.content), r = gt(e.content, e.attachments);
|
|
742
751
|
if (t) {
|
|
743
752
|
if (!r) throw Error("사진을 올려 주세요.");
|
|
744
753
|
return;
|
|
@@ -781,13 +790,13 @@ function Ot(e) {
|
|
|
781
790
|
post: e
|
|
782
791
|
} : { status: "not-found" };
|
|
783
792
|
} catch (e) {
|
|
784
|
-
return
|
|
793
|
+
return Dt(e) ? { status: "permission-denied" } : {
|
|
785
794
|
status: "error",
|
|
786
795
|
message: e instanceof Error ? e.message : String(e)
|
|
787
796
|
};
|
|
788
797
|
}
|
|
789
798
|
} catch (e) {
|
|
790
|
-
return
|
|
799
|
+
return Dt(e) ? { status: "permission-denied" } : {
|
|
791
800
|
status: "error",
|
|
792
801
|
message: e instanceof Error ? e.message : String(e)
|
|
793
802
|
};
|
|
@@ -825,7 +834,7 @@ function Ot(e) {
|
|
|
825
834
|
async function C(e) {
|
|
826
835
|
h(e);
|
|
827
836
|
let t = e.postId || g(), n = m(), r, i;
|
|
828
|
-
if (n) r = t, i =
|
|
837
|
+
if (n) r = t, i = mt(e.content);
|
|
829
838
|
else {
|
|
830
839
|
i = e.title.trim();
|
|
831
840
|
let t = S(i) || "post";
|
|
@@ -833,7 +842,7 @@ function Ot(e) {
|
|
|
833
842
|
let n = 1;
|
|
834
843
|
for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
|
|
835
844
|
}
|
|
836
|
-
let a =
|
|
845
|
+
let a = xt(e.content, e.attachments), o = p();
|
|
837
846
|
return await K(d(t), {
|
|
838
847
|
boardId: l(),
|
|
839
848
|
slug: r,
|
|
@@ -857,7 +866,7 @@ function Ot(e) {
|
|
|
857
866
|
}
|
|
858
867
|
async function w(e, t) {
|
|
859
868
|
h(t);
|
|
860
|
-
let n = m() ?
|
|
869
|
+
let n = m() ? mt(t.content) : t.title.trim(), r = xt(t.content, t.attachments), i = p();
|
|
861
870
|
await Promise.all([J(d(e), {
|
|
862
871
|
title: n,
|
|
863
872
|
...r,
|
|
@@ -877,7 +886,7 @@ function Ot(e) {
|
|
|
877
886
|
});
|
|
878
887
|
}
|
|
879
888
|
async function E(e) {
|
|
880
|
-
await
|
|
889
|
+
await ft(n, ye(r), o(), e);
|
|
881
890
|
}
|
|
882
891
|
return {
|
|
883
892
|
createPostId: g,
|
|
@@ -891,9 +900,9 @@ function Ot(e) {
|
|
|
891
900
|
deletePost: E
|
|
892
901
|
};
|
|
893
902
|
}
|
|
894
|
-
var
|
|
895
|
-
function
|
|
896
|
-
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } =
|
|
903
|
+
var At = 10;
|
|
904
|
+
function jt(e, t = {}) {
|
|
905
|
+
let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = tt(), o = () => _(), s = t.pageSize ?? At, l = M(() => i.value?.uid ?? ""), u = M(() => Ot(e)), d = () => c(n, o()), f = M(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = P([]), m = P([]), h = P([]), g = P(!0), v = P(!0), y = P(!1), b = P(""), x = null, S = null, C, w;
|
|
897
906
|
function T(e) {
|
|
898
907
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
899
908
|
}
|
|
@@ -976,8 +985,8 @@ function At(e, t = {}) {
|
|
|
976
985
|
loadMore: A
|
|
977
986
|
};
|
|
978
987
|
}
|
|
979
|
-
function
|
|
980
|
-
let r = Q(), i = () => _(), a = M(() =>
|
|
988
|
+
function Mt(e, n) {
|
|
989
|
+
let r = Q(), i = () => _(), a = M(() => Ot(e)), o = M(() => typeof n == "string" ? n : n.value), c = M(() => t(r, i(), o.value)), l = M(() => s(r, i(), o.value)), { data: u, pending: d } = ce(c), { data: f, pending: p } = ce(l);
|
|
981
990
|
return {
|
|
982
991
|
post: M(() => !u.value || u.value.boardId !== a.value ? null : {
|
|
983
992
|
...u.value,
|
|
@@ -989,9 +998,9 @@ function jt(e, n) {
|
|
|
989
998
|
}
|
|
990
999
|
//#endregion
|
|
991
1000
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
992
|
-
var
|
|
993
|
-
function
|
|
994
|
-
let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ??
|
|
1001
|
+
var Nt = 10;
|
|
1002
|
+
function Pt(e, t = {}) {
|
|
1003
|
+
let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ?? Nt, o = M(() => F(e)?.trim() || ""), s = P([]), l = P([]), u = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
|
|
995
1004
|
function v(e) {
|
|
996
1005
|
return {
|
|
997
1006
|
id: e.id,
|
|
@@ -1057,9 +1066,9 @@ function Nt(e, t = {}) {
|
|
|
1057
1066
|
}
|
|
1058
1067
|
//#endregion
|
|
1059
1068
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1060
|
-
var
|
|
1061
|
-
function
|
|
1062
|
-
let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ??
|
|
1069
|
+
var Ft = 10;
|
|
1070
|
+
function It(e, t = {}) {
|
|
1071
|
+
let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ?? Ft, o = M(() => F(e)?.trim() || ""), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
|
|
1063
1072
|
function g(e) {
|
|
1064
1073
|
return {
|
|
1065
1074
|
id: e.id,
|
|
@@ -1075,7 +1084,7 @@ function Ft(e, t = {}) {
|
|
|
1075
1084
|
return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
|
|
1076
1085
|
});
|
|
1077
1086
|
function b() {
|
|
1078
|
-
return [Y("authorUid", "==", o.value)];
|
|
1087
|
+
return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
|
|
1079
1088
|
}
|
|
1080
1089
|
function x() {
|
|
1081
1090
|
if (h?.(), !o.value) {
|
|
@@ -1121,11 +1130,11 @@ function Ft(e, t = {}) {
|
|
|
1121
1130
|
}
|
|
1122
1131
|
//#endregion
|
|
1123
1132
|
//#region src/composables/useMemiBoardCommentFeed.ts
|
|
1124
|
-
var
|
|
1125
|
-
function
|
|
1133
|
+
var Lt = 10;
|
|
1134
|
+
function Rt(e = "latest", t = {}) {
|
|
1126
1135
|
let n = Q(), r = () => _(), i = () => O(n, r()), a = M(() => {
|
|
1127
1136
|
let e = Math.floor(Number(F(t.pageSize)));
|
|
1128
|
-
return Number.isFinite(e) && e > 0 ? e :
|
|
1137
|
+
return Number.isFinite(e) && e > 0 ? e : Lt;
|
|
1129
1138
|
}), o = M(() => F(e) === "likes" ? "likes" : "latest"), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
|
|
1130
1139
|
function g(e) {
|
|
1131
1140
|
return {
|
|
@@ -1150,10 +1159,15 @@ function Lt(e = "latest", t = {}) {
|
|
|
1150
1159
|
});
|
|
1151
1160
|
function x() {
|
|
1152
1161
|
return o.value === "likes" ? [
|
|
1162
|
+
Y("listed", "==", !0),
|
|
1153
1163
|
U("likeCount", "desc"),
|
|
1154
1164
|
U("createdAt", "desc"),
|
|
1155
1165
|
U(L(), "desc")
|
|
1156
|
-
] : [
|
|
1166
|
+
] : [
|
|
1167
|
+
Y("listed", "==", !0),
|
|
1168
|
+
U("createdAt", "desc"),
|
|
1169
|
+
U(L(), "desc")
|
|
1170
|
+
];
|
|
1157
1171
|
}
|
|
1158
1172
|
function S() {
|
|
1159
1173
|
h?.(), h = H(W(i(), ...x(), V(a.value)), (e) => {
|
|
@@ -1190,9 +1204,9 @@ function Lt(e = "latest", t = {}) {
|
|
|
1190
1204
|
}
|
|
1191
1205
|
//#endregion
|
|
1192
1206
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1193
|
-
var
|
|
1207
|
+
var zt = 100, Bt = /* @__PURE__ */ new Map();
|
|
1194
1208
|
function $(e) {
|
|
1195
|
-
let t =
|
|
1209
|
+
let t = Bt.get(e);
|
|
1196
1210
|
return t || (t = ee({
|
|
1197
1211
|
text: "",
|
|
1198
1212
|
sentiment: null,
|
|
@@ -1200,10 +1214,10 @@ function $(e) {
|
|
|
1200
1214
|
pending: !1,
|
|
1201
1215
|
saving: !1,
|
|
1202
1216
|
error: ""
|
|
1203
|
-
}),
|
|
1217
|
+
}), Bt.set(e, t)), t;
|
|
1204
1218
|
}
|
|
1205
|
-
function
|
|
1206
|
-
let t = Q(), n = () => _(), { user: r } =
|
|
1219
|
+
function Vt(e) {
|
|
1220
|
+
let t = Q(), n = () => _(), { user: r } = tt(), i = M(() => F(e)), a = M(() => `${r.value?.uid ?? ""}:${i.value}`), o = M(() => {
|
|
1207
1221
|
let e = r.value?.uid, a = i.value;
|
|
1208
1222
|
return !e || !a ? null : m(t, n(), e, a);
|
|
1209
1223
|
}), s = M({
|
|
@@ -1272,95 +1286,96 @@ function Bt(e) {
|
|
|
1272
1286
|
}
|
|
1273
1287
|
//#endregion
|
|
1274
1288
|
//#region src/composables/useMemiBoardComments.ts
|
|
1275
|
-
var
|
|
1276
|
-
function
|
|
1289
|
+
var Ht = 10, Ut = 5, Wt = 1e3;
|
|
1290
|
+
function Gt(e) {
|
|
1277
1291
|
let t = e.trim();
|
|
1278
1292
|
if (!t) throw Error("댓글 내용을 입력해 주세요.");
|
|
1279
|
-
if (t.length > 1e3) throw Error(`댓글은 ${
|
|
1293
|
+
if (t.length > 1e3) throw Error(`댓글은 ${Wt.toLocaleString()}자까지 작성할 수 있습니다.`);
|
|
1280
1294
|
return t;
|
|
1281
1295
|
}
|
|
1282
|
-
function
|
|
1283
|
-
let i = Q(), a = () => _(), o = () => O(i, a()),
|
|
1284
|
-
function
|
|
1296
|
+
function Kt(e, n, r = {}) {
|
|
1297
|
+
let i = Q(), a = () => _(), { isBoardHidden: o } = Et(), s = () => O(i, a()), c = M(() => F(e)), u = M(() => F(n)), d = () => !o(c.value), f = P([]), p = P([]), m = P(r.subscribe !== !1), h = P([]), g = P(!1), v = P(r.subscribe !== !1), y = P(r.subscribe !== !1), b = null, x, S;
|
|
1298
|
+
function C(e) {
|
|
1285
1299
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
1286
1300
|
}
|
|
1287
|
-
let
|
|
1301
|
+
let w = M(() => {
|
|
1288
1302
|
let e = /* @__PURE__ */ new Map();
|
|
1289
1303
|
for (let t of [
|
|
1290
|
-
...
|
|
1291
|
-
...
|
|
1292
|
-
...
|
|
1304
|
+
...f.value,
|
|
1305
|
+
...p.value,
|
|
1306
|
+
...h.value
|
|
1293
1307
|
]) t.id && e.set(t.id, t);
|
|
1294
|
-
return [...e.values()].sort((e, t) =>
|
|
1308
|
+
return [...e.values()].sort((e, t) => C(e) - C(t) || String(e.id).localeCompare(String(t.id)));
|
|
1295
1309
|
});
|
|
1296
|
-
I([
|
|
1297
|
-
if (
|
|
1298
|
-
|
|
1310
|
+
I([u, c], async ([e]) => {
|
|
1311
|
+
if (h.value = [], f.value = [], p.value = [], b = null, clearTimeout(x), x = void 0, S?.(), S = void 0, v.value = r.subscribe !== !1, r.subscribe !== !1) {
|
|
1312
|
+
m.value = !0, y.value = !0;
|
|
1299
1313
|
try {
|
|
1300
|
-
let t = z(W(
|
|
1314
|
+
let t = z(W(s(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
|
|
1301
1315
|
if (await Promise.all([t.then((t) => {
|
|
1302
|
-
if (
|
|
1316
|
+
if (u.value !== e) return;
|
|
1303
1317
|
let n = t.docs.map((e) => ({
|
|
1304
1318
|
id: e.id,
|
|
1305
1319
|
...e.data()
|
|
1306
1320
|
}));
|
|
1307
|
-
|
|
1308
|
-
}),
|
|
1321
|
+
p.value = n.filter((e) => e.parentId === void 0);
|
|
1322
|
+
}), E()]), u.value !== e) return;
|
|
1309
1323
|
} finally {
|
|
1310
|
-
|
|
1324
|
+
u.value === e && (m.value = !1, y.value = !1);
|
|
1311
1325
|
}
|
|
1312
1326
|
}
|
|
1313
1327
|
}, { immediate: !0 });
|
|
1314
|
-
function
|
|
1315
|
-
|
|
1316
|
-
let t = new Map(
|
|
1328
|
+
function T() {
|
|
1329
|
+
S?.(), S = H(W(s(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
|
|
1330
|
+
let t = new Map(f.value.map((e) => [e.id, e]));
|
|
1317
1331
|
for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
|
|
1318
1332
|
id: n.doc.id,
|
|
1319
1333
|
...n.doc.data()
|
|
1320
1334
|
});
|
|
1321
|
-
|
|
1335
|
+
f.value = [...t.values()];
|
|
1322
1336
|
}, (e) => {
|
|
1323
1337
|
console.error("[memi-board:comments] onSnapshot failed", e);
|
|
1324
1338
|
});
|
|
1325
1339
|
}
|
|
1326
|
-
async function
|
|
1327
|
-
if (!(
|
|
1328
|
-
|
|
1340
|
+
async function E() {
|
|
1341
|
+
if (!(g.value || !v.value)) {
|
|
1342
|
+
g.value = !0;
|
|
1329
1343
|
try {
|
|
1330
1344
|
let e = [
|
|
1331
|
-
Y("postId", "==",
|
|
1345
|
+
Y("postId", "==", u.value),
|
|
1332
1346
|
Y("parentId", "==", null),
|
|
1333
1347
|
U("createdAt", "asc"),
|
|
1334
1348
|
U(L(), "asc"),
|
|
1335
|
-
...
|
|
1349
|
+
...b ? [q(b)] : [],
|
|
1336
1350
|
V(11)
|
|
1337
|
-
], t = await z(W(
|
|
1351
|
+
], t = await z(W(s(), ...e)), n = t.docs.slice(0, Ht), r = n.map((e) => ({
|
|
1338
1352
|
id: e.id,
|
|
1339
1353
|
...e.data()
|
|
1340
|
-
})), i = new Set(
|
|
1341
|
-
|
|
1342
|
-
|
|
1354
|
+
})), i = new Set(h.value.map((e) => e.id));
|
|
1355
|
+
h.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Ht && (v.value = !1, x = setTimeout(() => {
|
|
1356
|
+
x = void 0, T();
|
|
1343
1357
|
}, 1e3));
|
|
1344
1358
|
} finally {
|
|
1345
|
-
|
|
1359
|
+
g.value = !1;
|
|
1346
1360
|
}
|
|
1347
1361
|
}
|
|
1348
1362
|
}
|
|
1349
1363
|
N(() => {
|
|
1350
|
-
clearTimeout(
|
|
1364
|
+
clearTimeout(x), S?.();
|
|
1351
1365
|
});
|
|
1352
|
-
async function
|
|
1353
|
-
let n =
|
|
1354
|
-
r.set(
|
|
1355
|
-
postId:
|
|
1356
|
-
boardId:
|
|
1366
|
+
async function D(e) {
|
|
1367
|
+
let n = Gt(e.body), r = X(i), o = re(s());
|
|
1368
|
+
r.set(o, {
|
|
1369
|
+
postId: u.value,
|
|
1370
|
+
boardId: c.value,
|
|
1371
|
+
listed: d(),
|
|
1357
1372
|
body: n,
|
|
1358
1373
|
authorUid: e.authorUid,
|
|
1359
1374
|
authorName: e.authorName,
|
|
1360
1375
|
authorPhoto: e.authorPhoto,
|
|
1361
1376
|
moderationStatus: "approved",
|
|
1362
1377
|
parentId: null,
|
|
1363
|
-
rootId:
|
|
1378
|
+
rootId: o.id,
|
|
1364
1379
|
depth: 0,
|
|
1365
1380
|
replyToUid: null,
|
|
1366
1381
|
replyToName: null,
|
|
@@ -1369,25 +1384,26 @@ function Gt(e, n, r = {}) {
|
|
|
1369
1384
|
isReply: !1,
|
|
1370
1385
|
createdAt: G(),
|
|
1371
1386
|
updatedAt: G()
|
|
1372
|
-
}), r.update(t(i, a(),
|
|
1387
|
+
}), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1373
1388
|
}
|
|
1374
|
-
async function
|
|
1375
|
-
let n =
|
|
1389
|
+
async function k(e) {
|
|
1390
|
+
let n = Gt(e.body);
|
|
1376
1391
|
if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
|
|
1377
|
-
let r = X(i),
|
|
1378
|
-
if (!
|
|
1379
|
-
let
|
|
1380
|
-
r.set(
|
|
1381
|
-
postId:
|
|
1382
|
-
boardId:
|
|
1392
|
+
let r = X(i), o = re(s()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1393
|
+
if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
|
|
1394
|
+
let p = Math.min((e.parent.depth ?? 0) + 1, 2);
|
|
1395
|
+
r.set(o, {
|
|
1396
|
+
postId: u.value,
|
|
1397
|
+
boardId: c.value,
|
|
1398
|
+
listed: d(),
|
|
1383
1399
|
body: n,
|
|
1384
1400
|
authorUid: e.authorUid,
|
|
1385
1401
|
authorName: e.authorName,
|
|
1386
1402
|
authorPhoto: e.authorPhoto,
|
|
1387
1403
|
moderationStatus: "approved",
|
|
1388
1404
|
parentId: e.parent.id,
|
|
1389
|
-
rootId:
|
|
1390
|
-
depth:
|
|
1405
|
+
rootId: f,
|
|
1406
|
+
depth: p,
|
|
1391
1407
|
replyToUid: e.parent.authorUid,
|
|
1392
1408
|
replyToName: e.parent.authorName,
|
|
1393
1409
|
replyCount: 0,
|
|
@@ -1395,23 +1411,23 @@ function Gt(e, n, r = {}) {
|
|
|
1395
1411
|
isReply: !0,
|
|
1396
1412
|
createdAt: G(),
|
|
1397
1413
|
updatedAt: G()
|
|
1398
|
-
}), r.update(l(i, a(),
|
|
1414
|
+
}), r.update(l(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
|
|
1399
1415
|
}
|
|
1400
|
-
async function
|
|
1416
|
+
async function A(e) {
|
|
1401
1417
|
if (!e.id) return;
|
|
1402
1418
|
if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
|
|
1403
|
-
await
|
|
1419
|
+
await dt(i, a(), e.id);
|
|
1404
1420
|
let n = X(i);
|
|
1405
|
-
n.delete(l(i, a(), e.id)), e.parentId && e.rootId && n.update(l(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(),
|
|
1421
|
+
n.delete(l(i, a(), e.id)), e.parentId && e.rootId && n.update(l(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(), u.value), { commentCount: B(-1) }), await n.commit(), h.value = h.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);
|
|
1406
1422
|
}
|
|
1407
|
-
async function
|
|
1408
|
-
let n =
|
|
1423
|
+
async function j(e, t) {
|
|
1424
|
+
let n = Gt(t);
|
|
1409
1425
|
await J(l(i, a(), e), {
|
|
1410
1426
|
body: n,
|
|
1411
1427
|
updatedAt: G()
|
|
1412
1428
|
});
|
|
1413
1429
|
}
|
|
1414
|
-
async function
|
|
1430
|
+
async function ee(e, t, n) {
|
|
1415
1431
|
await J(l(i, a(), e), {
|
|
1416
1432
|
isBlinded: t,
|
|
1417
1433
|
moderatedAt: G(),
|
|
@@ -1419,74 +1435,75 @@ function Gt(e, n, r = {}) {
|
|
|
1419
1435
|
});
|
|
1420
1436
|
}
|
|
1421
1437
|
return {
|
|
1422
|
-
comments:
|
|
1423
|
-
commentsPending: M(() =>
|
|
1424
|
-
hasMore:
|
|
1425
|
-
loadingMore:
|
|
1426
|
-
loadMore:
|
|
1427
|
-
addComment:
|
|
1428
|
-
addReply:
|
|
1429
|
-
updateComment:
|
|
1430
|
-
setCommentBlinded:
|
|
1431
|
-
deleteComment:
|
|
1438
|
+
comments: w,
|
|
1439
|
+
commentsPending: M(() => y.value || m.value),
|
|
1440
|
+
hasMore: v,
|
|
1441
|
+
loadingMore: g,
|
|
1442
|
+
loadMore: E,
|
|
1443
|
+
addComment: D,
|
|
1444
|
+
addReply: k,
|
|
1445
|
+
updateComment: j,
|
|
1446
|
+
setCommentBlinded: ee,
|
|
1447
|
+
deleteComment: A
|
|
1432
1448
|
};
|
|
1433
1449
|
}
|
|
1434
|
-
function
|
|
1435
|
-
let r = Q(), i = () => _(), a = () => O(r, i()), o = P([]),
|
|
1436
|
-
function
|
|
1450
|
+
function qt(e, t, n) {
|
|
1451
|
+
let r = Q(), i = () => _(), a = () => O(r, i()), o = t.trim(), s = P([]), c = P(!1), l = P(!1), u = P(!0), d = null, f;
|
|
1452
|
+
function p(e) {
|
|
1437
1453
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
1438
1454
|
}
|
|
1439
|
-
function
|
|
1440
|
-
|
|
1441
|
-
let t = new Map(
|
|
1455
|
+
function m() {
|
|
1456
|
+
f ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
|
|
1457
|
+
let t = new Map(s.value.map((e) => [e.id, e]));
|
|
1442
1458
|
for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
|
|
1443
1459
|
id: n.doc.id,
|
|
1444
1460
|
...n.doc.data()
|
|
1445
1461
|
});
|
|
1446
|
-
|
|
1462
|
+
s.value = p([...t.values()]);
|
|
1447
1463
|
}, (e) => {
|
|
1448
1464
|
console.error("[memi-board:replies] onSnapshot failed", e);
|
|
1449
1465
|
});
|
|
1450
1466
|
}
|
|
1451
|
-
async function
|
|
1452
|
-
if (!(
|
|
1453
|
-
|
|
1467
|
+
async function h() {
|
|
1468
|
+
if (!(c.value || !u.value)) {
|
|
1469
|
+
c.value = !0;
|
|
1454
1470
|
try {
|
|
1455
1471
|
let e = [
|
|
1472
|
+
Y("postId", "==", o),
|
|
1456
1473
|
Y("rootId", "==", n),
|
|
1457
1474
|
Y("isReply", "==", !0),
|
|
1458
1475
|
U("createdAt", "asc"),
|
|
1459
1476
|
U(L(), "asc"),
|
|
1460
|
-
...
|
|
1477
|
+
...d ? [q(d)] : [],
|
|
1461
1478
|
V(6)
|
|
1462
|
-
], t = await z(W(a(), ...e)), r = new Set(
|
|
1479
|
+
], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Ut), c = i.map((e) => ({
|
|
1463
1480
|
id: e.id,
|
|
1464
1481
|
...e.data()
|
|
1465
1482
|
})).filter((e) => !r.has(e.id));
|
|
1466
|
-
|
|
1483
|
+
s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Ut && (u.value = !1, m());
|
|
1467
1484
|
} finally {
|
|
1468
|
-
|
|
1485
|
+
c.value = !1;
|
|
1469
1486
|
}
|
|
1470
1487
|
}
|
|
1471
1488
|
}
|
|
1472
|
-
function
|
|
1473
|
-
|
|
1489
|
+
function g() {
|
|
1490
|
+
f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
|
|
1491
|
+
}
|
|
1492
|
+
async function v() {
|
|
1493
|
+
g(), await h();
|
|
1474
1494
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
hasMore: l,
|
|
1483
|
-
loadMore: m,
|
|
1484
|
-
refresh: g
|
|
1495
|
+
return N(() => f?.()), {
|
|
1496
|
+
replies: s,
|
|
1497
|
+
loading: c,
|
|
1498
|
+
loaded: l,
|
|
1499
|
+
hasMore: u,
|
|
1500
|
+
loadMore: h,
|
|
1501
|
+
refresh: v
|
|
1485
1502
|
};
|
|
1486
1503
|
}
|
|
1487
1504
|
//#endregion
|
|
1488
1505
|
//#region src/composables/useMemiBoardLikes.ts
|
|
1489
|
-
function
|
|
1506
|
+
function Jt(e, n) {
|
|
1490
1507
|
let r = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
|
|
1491
1508
|
function l(e) {
|
|
1492
1509
|
return j(i, r(), n, e);
|
|
@@ -1525,8 +1542,8 @@ function qt(e, n) {
|
|
|
1525
1542
|
toggleLike: u
|
|
1526
1543
|
};
|
|
1527
1544
|
}
|
|
1528
|
-
var
|
|
1529
|
-
function
|
|
1545
|
+
var Yt = Symbol.for("memi-board:comment-likes");
|
|
1546
|
+
function Xt(e, t) {
|
|
1530
1547
|
let n = () => _(), r = Q(), i = Z(), a = M(() => F(e)), o = M(() => F(t)), s = P(/* @__PURE__ */ new Set()), c = P(null), u;
|
|
1531
1548
|
I([i, o], ([e, t]) => {
|
|
1532
1549
|
u?.(), u = void 0, s.value = /* @__PURE__ */ new Set(), !(!e || !t) && (u = H(W(A(r, n()), Y("uid", "==", e.uid), Y("postId", "==", t), Y("target", "==", "comment")), (e) => {
|
|
@@ -1579,7 +1596,7 @@ function Yt(e, t) {
|
|
|
1579
1596
|
}
|
|
1580
1597
|
//#endregion
|
|
1581
1598
|
//#region src/composables/useMemiBoardReports.ts
|
|
1582
|
-
var
|
|
1599
|
+
var Zt = 200, Qt = [
|
|
1583
1600
|
{
|
|
1584
1601
|
value: "spam",
|
|
1585
1602
|
label: "스팸·광고"
|
|
@@ -1601,10 +1618,10 @@ var Xt = 200, Zt = [
|
|
|
1601
1618
|
label: "기타"
|
|
1602
1619
|
}
|
|
1603
1620
|
];
|
|
1604
|
-
function
|
|
1605
|
-
return
|
|
1621
|
+
function $t(e) {
|
|
1622
|
+
return Qt.find((t) => t.value === e)?.label || e || "신고";
|
|
1606
1623
|
}
|
|
1607
|
-
function
|
|
1624
|
+
function en(e, t) {
|
|
1608
1625
|
let n = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
|
|
1609
1626
|
I(a, (e) => {
|
|
1610
1627
|
if (c?.(), c = void 0, !e) {
|
|
@@ -1622,7 +1639,7 @@ function $t(e, t) {
|
|
|
1622
1639
|
if (!l) throw Error("로그인이 필요합니다.");
|
|
1623
1640
|
if (s.value) return;
|
|
1624
1641
|
if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
|
|
1625
|
-
let u =
|
|
1642
|
+
let u = Qt.some((e) => e.value === c.reason) ? c.reason : null;
|
|
1626
1643
|
if (!u) throw Error("신고 사유를 선택해 주세요.");
|
|
1627
1644
|
let d = (c.detail || "").trim();
|
|
1628
1645
|
if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
|
|
@@ -1651,9 +1668,9 @@ function $t(e, t) {
|
|
|
1651
1668
|
submitReport: l
|
|
1652
1669
|
};
|
|
1653
1670
|
}
|
|
1654
|
-
var
|
|
1655
|
-
function
|
|
1656
|
-
let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } =
|
|
1671
|
+
var tn = 20;
|
|
1672
|
+
function nn(e = {}) {
|
|
1673
|
+
let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } = tt(), s = e.pageSize ?? tn, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
|
|
1657
1674
|
function g(e) {
|
|
1658
1675
|
return {
|
|
1659
1676
|
id: e.id,
|
|
@@ -1707,7 +1724,7 @@ function tn(e = {}) {
|
|
|
1707
1724
|
}
|
|
1708
1725
|
//#endregion
|
|
1709
1726
|
//#region src/composables/useMemiBoardViews.ts
|
|
1710
|
-
function
|
|
1727
|
+
function rn() {
|
|
1711
1728
|
let e = () => _(), n = Q();
|
|
1712
1729
|
function r(t, n) {
|
|
1713
1730
|
let { postsCollection: r } = e();
|
|
@@ -1747,7 +1764,7 @@ function nn() {
|
|
|
1747
1764
|
}
|
|
1748
1765
|
//#endregion
|
|
1749
1766
|
//#region src/utils/moderation-prompt.ts
|
|
1750
|
-
var
|
|
1767
|
+
var an = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", on = {
|
|
1751
1768
|
abuse: "욕설·비속어·모욕",
|
|
1752
1769
|
spam: "스팸·광고",
|
|
1753
1770
|
adult: "선정적·성인 내용",
|
|
@@ -1755,17 +1772,17 @@ var rn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
1755
1772
|
other: "부적절한 내용",
|
|
1756
1773
|
none: ""
|
|
1757
1774
|
};
|
|
1758
|
-
function
|
|
1775
|
+
function sn(e) {
|
|
1759
1776
|
if (e.via === "local" && e.localHit) {
|
|
1760
1777
|
let t = e.localHit.trim();
|
|
1761
1778
|
return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
|
|
1762
1779
|
}
|
|
1763
1780
|
let t = (e.reason || "").trim();
|
|
1764
1781
|
if (t) return t;
|
|
1765
|
-
let n = e.category && e.category !== "none" ?
|
|
1782
|
+
let n = e.category && e.category !== "none" ? on[e.category] || e.category : "";
|
|
1766
1783
|
return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
|
|
1767
1784
|
}
|
|
1768
|
-
var
|
|
1785
|
+
var cn = [
|
|
1769
1786
|
"씨발",
|
|
1770
1787
|
"시발",
|
|
1771
1788
|
"시이발",
|
|
@@ -1791,10 +1808,10 @@ var sn = [
|
|
|
1791
1808
|
"bitch",
|
|
1792
1809
|
"asshole"
|
|
1793
1810
|
];
|
|
1794
|
-
function
|
|
1811
|
+
function ln(e) {
|
|
1795
1812
|
return new RegExp(e.join("|"), "i");
|
|
1796
1813
|
}
|
|
1797
|
-
var
|
|
1814
|
+
var un = /* @__PURE__ */ new Set([
|
|
1798
1815
|
"none",
|
|
1799
1816
|
"abuse",
|
|
1800
1817
|
"spam",
|
|
@@ -1802,7 +1819,7 @@ var ln = /* @__PURE__ */ new Set([
|
|
|
1802
1819
|
"violence",
|
|
1803
1820
|
"other"
|
|
1804
1821
|
]);
|
|
1805
|
-
function
|
|
1822
|
+
function dn(e) {
|
|
1806
1823
|
let t = e.trim();
|
|
1807
1824
|
if (!t) return null;
|
|
1808
1825
|
let n;
|
|
@@ -1820,7 +1837,7 @@ function un(e) {
|
|
|
1820
1837
|
if (typeof n != "object" || !n) return null;
|
|
1821
1838
|
let r = n;
|
|
1822
1839
|
if (typeof r.flagged != "boolean") return null;
|
|
1823
|
-
let i = typeof r.category == "string" &&
|
|
1840
|
+
let i = typeof r.category == "string" && un.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
1824
1841
|
return {
|
|
1825
1842
|
flagged: r.flagged,
|
|
1826
1843
|
category: i,
|
|
@@ -1829,20 +1846,20 @@ function un(e) {
|
|
|
1829
1846
|
}
|
|
1830
1847
|
//#endregion
|
|
1831
1848
|
//#region src/composables/useMemiBoardModeration.ts
|
|
1832
|
-
var
|
|
1849
|
+
var fn = {
|
|
1833
1850
|
flagged: !1,
|
|
1834
1851
|
category: "none",
|
|
1835
1852
|
reason: "",
|
|
1836
1853
|
via: "empty"
|
|
1837
1854
|
};
|
|
1838
|
-
function
|
|
1839
|
-
let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } =
|
|
1855
|
+
function pn() {
|
|
1856
|
+
let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = tt(), a = ln([...cn, ...e.localBlocklist ?? []]);
|
|
1840
1857
|
function o(e) {
|
|
1841
1858
|
let t = e.match(a);
|
|
1842
1859
|
return t ? {
|
|
1843
1860
|
flagged: !0,
|
|
1844
1861
|
category: "abuse",
|
|
1845
|
-
reason:
|
|
1862
|
+
reason: sn({
|
|
1846
1863
|
via: "local",
|
|
1847
1864
|
localHit: t[0] || "",
|
|
1848
1865
|
category: "abuse"
|
|
@@ -1857,7 +1874,7 @@ function fn() {
|
|
|
1857
1874
|
});
|
|
1858
1875
|
return Te(i, {
|
|
1859
1876
|
model: n,
|
|
1860
|
-
systemInstruction:
|
|
1877
|
+
systemInstruction: an,
|
|
1861
1878
|
generationConfig: {
|
|
1862
1879
|
temperature: 0,
|
|
1863
1880
|
maxOutputTokens: 256,
|
|
@@ -1886,7 +1903,7 @@ function fn() {
|
|
|
1886
1903
|
};
|
|
1887
1904
|
}
|
|
1888
1905
|
function l(e, t = "") {
|
|
1889
|
-
let n =
|
|
1906
|
+
let n = sn({
|
|
1890
1907
|
reason: e.reason,
|
|
1891
1908
|
category: e.category,
|
|
1892
1909
|
via: e.via
|
|
@@ -1914,22 +1931,22 @@ function fn() {
|
|
|
1914
1931
|
};
|
|
1915
1932
|
let i = t.trim();
|
|
1916
1933
|
if (!i) return {
|
|
1917
|
-
...
|
|
1934
|
+
...fn,
|
|
1918
1935
|
via: "empty"
|
|
1919
1936
|
};
|
|
1920
1937
|
let a = o(i);
|
|
1921
1938
|
if (a) return d(a);
|
|
1922
1939
|
if (e.enabled === !1) return {
|
|
1923
|
-
...
|
|
1940
|
+
...fn,
|
|
1924
1941
|
via: "disabled"
|
|
1925
1942
|
};
|
|
1926
1943
|
try {
|
|
1927
|
-
let e =
|
|
1944
|
+
let e = dn((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
|
|
1928
1945
|
if (!e) return c();
|
|
1929
1946
|
let t = {
|
|
1930
1947
|
flagged: e.flagged,
|
|
1931
1948
|
category: e.category,
|
|
1932
|
-
reason:
|
|
1949
|
+
reason: sn({
|
|
1933
1950
|
reason: e.reason,
|
|
1934
1951
|
category: e.category,
|
|
1935
1952
|
via: "ai"
|
|
@@ -1949,14 +1966,14 @@ function fn() {
|
|
|
1949
1966
|
via: "restricted"
|
|
1950
1967
|
};
|
|
1951
1968
|
if (e.enabled === !1 || !e.moderateImages) return {
|
|
1952
|
-
...
|
|
1969
|
+
...fn,
|
|
1953
1970
|
via: "disabled"
|
|
1954
1971
|
};
|
|
1955
1972
|
try {
|
|
1956
1973
|
let e = await new Promise((e, n) => {
|
|
1957
1974
|
let r = new FileReader();
|
|
1958
1975
|
r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
|
|
1959
|
-
}), n =
|
|
1976
|
+
}), n = dn((await s().generateContent([{ inlineData: {
|
|
1960
1977
|
mimeType: t.type || "image/jpeg",
|
|
1961
1978
|
data: e
|
|
1962
1979
|
} }, "이 이미지를 심사해 주세요."])).response.text());
|
|
@@ -1964,7 +1981,7 @@ function fn() {
|
|
|
1964
1981
|
let r = {
|
|
1965
1982
|
flagged: n.flagged,
|
|
1966
1983
|
category: n.category,
|
|
1967
|
-
reason:
|
|
1984
|
+
reason: sn({
|
|
1968
1985
|
reason: n.reason,
|
|
1969
1986
|
category: n.category,
|
|
1970
1987
|
via: "ai"
|
|
@@ -1983,7 +2000,7 @@ function fn() {
|
|
|
1983
2000
|
}
|
|
1984
2001
|
//#endregion
|
|
1985
2002
|
//#region src/composables/useMemiBoardUsers.ts
|
|
1986
|
-
var
|
|
2003
|
+
var mn = [
|
|
1987
2004
|
{
|
|
1988
2005
|
value: "admin",
|
|
1989
2006
|
label: "관리자",
|
|
@@ -2000,10 +2017,10 @@ var pn = [
|
|
|
2000
2017
|
description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
|
|
2001
2018
|
}
|
|
2002
2019
|
];
|
|
2003
|
-
function
|
|
2020
|
+
function hn(e = {}) {
|
|
2004
2021
|
let t = () => _(), n = Q(), r = M(() => F(e.enabled) ?? !0 ? h(n, t()) : null), i = se(r, { ssrKey: w(t(), "users-management") });
|
|
2005
2022
|
async function a(e, r) {
|
|
2006
|
-
if (!
|
|
2023
|
+
if (!mn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
|
|
2007
2024
|
await J(x(n, t(), e), {
|
|
2008
2025
|
role: r,
|
|
2009
2026
|
updatedAt: G()
|
|
@@ -2017,7 +2034,7 @@ function mn(e = {}) {
|
|
|
2017
2034
|
}
|
|
2018
2035
|
//#endregion
|
|
2019
2036
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
2020
|
-
function
|
|
2037
|
+
function gn(e) {
|
|
2021
2038
|
let t = Q(), n = () => _(), r = M(() => F(e)), i = M(() => {
|
|
2022
2039
|
let e = r.value;
|
|
2023
2040
|
return e ? x(t, n(), e) : null;
|
|
@@ -2029,7 +2046,7 @@ function hn(e) {
|
|
|
2029
2046
|
}
|
|
2030
2047
|
//#endregion
|
|
2031
2048
|
//#region src/utils/section.ts
|
|
2032
|
-
var
|
|
2049
|
+
var _n = [
|
|
2033
2050
|
3,
|
|
2034
2051
|
4,
|
|
2035
2052
|
5,
|
|
@@ -2038,7 +2055,7 @@ var gn = [
|
|
|
2038
2055
|
8,
|
|
2039
2056
|
9,
|
|
2040
2057
|
12
|
|
2041
|
-
],
|
|
2058
|
+
], vn = [
|
|
2042
2059
|
{
|
|
2043
2060
|
value: "list",
|
|
2044
2061
|
label: "게시목록"
|
|
@@ -2051,24 +2068,24 @@ var gn = [
|
|
|
2051
2068
|
value: "comments",
|
|
2052
2069
|
label: "댓글모음"
|
|
2053
2070
|
}
|
|
2054
|
-
],
|
|
2071
|
+
], yn = [{
|
|
2055
2072
|
value: "latest",
|
|
2056
2073
|
label: "최신순"
|
|
2057
2074
|
}, {
|
|
2058
2075
|
value: "likes",
|
|
2059
2076
|
label: "좋아요순"
|
|
2060
2077
|
}];
|
|
2061
|
-
function
|
|
2078
|
+
function bn(e) {
|
|
2062
2079
|
return e === "list" ? 5 : 4;
|
|
2063
2080
|
}
|
|
2064
|
-
function
|
|
2081
|
+
function xn(e, t) {
|
|
2065
2082
|
let n = Math.floor(Number(t));
|
|
2066
|
-
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) :
|
|
2083
|
+
return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : bn(e);
|
|
2067
2084
|
}
|
|
2068
|
-
function
|
|
2085
|
+
function Sn(e) {
|
|
2069
2086
|
return e === 12 ? null : 12 - e;
|
|
2070
2087
|
}
|
|
2071
|
-
function
|
|
2088
|
+
function Cn(e) {
|
|
2072
2089
|
return {
|
|
2073
2090
|
3: "col-span-12 md:col-span-3",
|
|
2074
2091
|
4: "col-span-12 md:col-span-4",
|
|
@@ -2080,36 +2097,36 @@ function Sn(e) {
|
|
|
2080
2097
|
12: "col-span-12"
|
|
2081
2098
|
}[e];
|
|
2082
2099
|
}
|
|
2083
|
-
function
|
|
2100
|
+
function wn(e) {
|
|
2084
2101
|
return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
|
|
2085
2102
|
}
|
|
2086
|
-
function
|
|
2103
|
+
function Tn(e) {
|
|
2087
2104
|
let t = e.boardId || e.category || "free";
|
|
2088
2105
|
return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
|
|
2089
2106
|
}
|
|
2090
|
-
function
|
|
2107
|
+
function En(e) {
|
|
2091
2108
|
return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
|
|
2092
2109
|
}
|
|
2093
|
-
function
|
|
2110
|
+
function Dn(e) {
|
|
2094
2111
|
if (!(!e.boardId || !e.postId)) return `/board/${e.boardId}/post/${e.postId}`;
|
|
2095
2112
|
}
|
|
2096
|
-
function
|
|
2113
|
+
function On(e) {
|
|
2097
2114
|
return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
2098
2115
|
}
|
|
2099
|
-
function
|
|
2116
|
+
function kn(e) {
|
|
2100
2117
|
return e.title?.trim() || e.summary || "게시물";
|
|
2101
2118
|
}
|
|
2102
|
-
function
|
|
2119
|
+
function An(e) {
|
|
2103
2120
|
return e === "image" || e === "video" ? "media" : "list";
|
|
2104
2121
|
}
|
|
2105
|
-
function
|
|
2106
|
-
return
|
|
2122
|
+
function jn(e) {
|
|
2123
|
+
return An(e) === "media" ? "미디어" : "목록";
|
|
2107
2124
|
}
|
|
2108
|
-
function
|
|
2125
|
+
function Mn(e, t) {
|
|
2109
2126
|
return e ? t(e)?.listView ?? "default" : "default";
|
|
2110
2127
|
}
|
|
2111
|
-
function
|
|
2112
|
-
let n = e.map((e) =>
|
|
2128
|
+
function Nn(e, t) {
|
|
2129
|
+
let n = e.map((e) => Mn(e.boardId, t));
|
|
2113
2130
|
if (!n.length) return "default";
|
|
2114
2131
|
if (n.every((e) => e === n[0])) return n[0];
|
|
2115
2132
|
let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
|
|
@@ -2117,27 +2134,27 @@ function Mn(e, t) {
|
|
|
2117
2134
|
}
|
|
2118
2135
|
//#endregion
|
|
2119
2136
|
//#region src/composables/useMemiBoardSections.ts
|
|
2120
|
-
var
|
|
2121
|
-
function
|
|
2137
|
+
var Pn = [];
|
|
2138
|
+
function Fn(e) {
|
|
2122
2139
|
let t = Number(e.cols);
|
|
2123
|
-
if (
|
|
2140
|
+
if (_n.includes(t)) return t;
|
|
2124
2141
|
let n = e.width;
|
|
2125
2142
|
return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
|
|
2126
2143
|
}
|
|
2127
|
-
function
|
|
2144
|
+
function In(e) {
|
|
2128
2145
|
let t = e.height ?? e.size;
|
|
2129
2146
|
return t === "sm" || t === "lg" ? t : "md";
|
|
2130
2147
|
}
|
|
2131
|
-
function
|
|
2148
|
+
function Ln(e) {
|
|
2132
2149
|
return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
|
|
2133
2150
|
}
|
|
2134
|
-
function
|
|
2151
|
+
function Rn(e) {
|
|
2135
2152
|
return e.sort === "likes" ? "likes" : "latest";
|
|
2136
2153
|
}
|
|
2137
|
-
function
|
|
2154
|
+
function zn(e) {
|
|
2138
2155
|
return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
|
|
2139
2156
|
}
|
|
2140
|
-
function
|
|
2157
|
+
function Bn(e, t) {
|
|
2141
2158
|
let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
|
|
2142
2159
|
for (let e of n) {
|
|
2143
2160
|
let n = String(e || "").trim();
|
|
@@ -2145,40 +2162,40 @@ function zn(e, t) {
|
|
|
2145
2162
|
}
|
|
2146
2163
|
return r;
|
|
2147
2164
|
}
|
|
2148
|
-
function
|
|
2149
|
-
let n =
|
|
2165
|
+
function Vn(e, t) {
|
|
2166
|
+
let n = Ln(e), r = xn(n, e.count);
|
|
2150
2167
|
return {
|
|
2151
2168
|
id: String(e.id || `box-${t + 1}`),
|
|
2152
2169
|
title: String(e.title || `섹션 ${t + 1}`),
|
|
2153
2170
|
showTitle: e.showTitle !== !1,
|
|
2154
2171
|
kind: n,
|
|
2155
|
-
boardId:
|
|
2172
|
+
boardId: zn(e),
|
|
2156
2173
|
count: r,
|
|
2157
|
-
postIds:
|
|
2158
|
-
sort:
|
|
2159
|
-
cols:
|
|
2160
|
-
height:
|
|
2174
|
+
postIds: Bn(e, r),
|
|
2175
|
+
sort: Rn(e),
|
|
2176
|
+
cols: Fn(e),
|
|
2177
|
+
height: In(e),
|
|
2161
2178
|
order: t
|
|
2162
2179
|
};
|
|
2163
2180
|
}
|
|
2164
|
-
function
|
|
2165
|
-
let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks :
|
|
2181
|
+
function Hn() {
|
|
2182
|
+
let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Pn).map((e, t) => Vn(e, t)));
|
|
2166
2183
|
async function a(e) {
|
|
2167
2184
|
await K(t, {
|
|
2168
2185
|
columns: 12,
|
|
2169
2186
|
blocks: e.map((e, t) => {
|
|
2170
|
-
let n = e, r =
|
|
2187
|
+
let n = e, r = Ln(n), i = xn(r, e.count);
|
|
2171
2188
|
return {
|
|
2172
2189
|
id: e.id,
|
|
2173
2190
|
title: e.title.trim() || `섹션 ${t + 1}`,
|
|
2174
2191
|
showTitle: e.showTitle !== !1,
|
|
2175
2192
|
kind: r,
|
|
2176
|
-
boardId:
|
|
2193
|
+
boardId: zn(n),
|
|
2177
2194
|
count: i,
|
|
2178
|
-
postIds:
|
|
2179
|
-
sort:
|
|
2180
|
-
cols:
|
|
2181
|
-
height:
|
|
2195
|
+
postIds: Bn(n, i),
|
|
2196
|
+
sort: Rn(n),
|
|
2197
|
+
cols: Fn(n),
|
|
2198
|
+
height: In(n),
|
|
2182
2199
|
order: t
|
|
2183
2200
|
};
|
|
2184
2201
|
}),
|
|
@@ -2197,7 +2214,7 @@ function Vn() {
|
|
|
2197
2214
|
}
|
|
2198
2215
|
//#endregion
|
|
2199
2216
|
//#region src/composables/useMemiBoardMiniPosts.ts
|
|
2200
|
-
function
|
|
2217
|
+
function Un(e, t) {
|
|
2201
2218
|
let n = Q(), r = () => _(), i = M(() => {
|
|
2202
2219
|
if (!import.meta.client) return null;
|
|
2203
2220
|
let i = Math.max(1, Math.floor(Number(F(t)) || 5)), a = F(e)?.trim() || "", o = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
|
|
@@ -2209,7 +2226,7 @@ function Hn(e, t) {
|
|
|
2209
2226
|
error: s
|
|
2210
2227
|
};
|
|
2211
2228
|
}
|
|
2212
|
-
function
|
|
2229
|
+
function Wn(e) {
|
|
2213
2230
|
let t = Q(), n = () => _(), r = M(() => [...new Set(F(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = M(() => !import.meta.client || !r.value.length ? null : W(c(t, n()), Y(L(), "in", r.value))), { data: a, pending: o, error: s } = se(i);
|
|
2214
2231
|
return {
|
|
2215
2232
|
posts: M(() => {
|
|
@@ -2226,7 +2243,13 @@ function Un(e) {
|
|
|
2226
2243
|
}
|
|
2227
2244
|
//#endregion
|
|
2228
2245
|
//#region src/data/versionHistory.ts
|
|
2229
|
-
var
|
|
2246
|
+
var Gn = [
|
|
2247
|
+
{
|
|
2248
|
+
version: "0.38.0",
|
|
2249
|
+
date: "2026-08-21",
|
|
2250
|
+
highlights: ["숨김 게시판에는 댓글을 거의 쓰지 않아요. 필요하면 담당 스태프만 남길 수 있어요", "숨김 게시판 댓글은 공개 댓글 모음에 나오지 않아요"],
|
|
2251
|
+
note: "호스트는 Firestore 규칙·댓글 listed 인덱스를 다시 배포해야 해요. 공개 보드는 한 번 저장하면 예전 댓글이 목록에 다시 채워져요."
|
|
2252
|
+
},
|
|
2230
2253
|
{
|
|
2231
2254
|
version: "0.36.0",
|
|
2232
2255
|
date: "2026-08-19",
|
|
@@ -2459,7 +2482,7 @@ var Wn = [
|
|
|
2459
2482
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
2460
2483
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
2461
2484
|
}
|
|
2462
|
-
],
|
|
2485
|
+
], Kn = /* @__PURE__ */ i(((e, t) => {
|
|
2463
2486
|
(function(n, r) {
|
|
2464
2487
|
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();
|
|
2465
2488
|
})(e, (function() {
|
|
@@ -2753,7 +2776,7 @@ var Wn = [
|
|
|
2753
2776
|
return w(1e3 * e);
|
|
2754
2777
|
}, w.en = b[y], w.Ls = b, w.p = {}, w;
|
|
2755
2778
|
}));
|
|
2756
|
-
})),
|
|
2779
|
+
})), qn = /* @__PURE__ */ i(((e, t) => {
|
|
2757
2780
|
(function(n, r) {
|
|
2758
2781
|
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();
|
|
2759
2782
|
})(e, (function() {
|
|
@@ -2857,9 +2880,9 @@ var Wn = [
|
|
|
2857
2880
|
};
|
|
2858
2881
|
};
|
|
2859
2882
|
}));
|
|
2860
|
-
})),
|
|
2883
|
+
})), Jn = /* @__PURE__ */ i(((e, t) => {
|
|
2861
2884
|
(function(n, r) {
|
|
2862
|
-
typeof e == "object" && t !== void 0 ? t.exports = r(
|
|
2885
|
+
typeof e == "object" && t !== void 0 ? t.exports = r(Kn()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
|
|
2863
2886
|
})(e, (function(e) {
|
|
2864
2887
|
function t(e) {
|
|
2865
2888
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
@@ -2907,24 +2930,24 @@ var Wn = [
|
|
|
2907
2930
|
};
|
|
2908
2931
|
return n.default.locale(r, null, !0), r;
|
|
2909
2932
|
}));
|
|
2910
|
-
})),
|
|
2911
|
-
|
|
2912
|
-
function
|
|
2933
|
+
})), Yn = /* @__PURE__ */ a(Kn(), 1), Xn = /* @__PURE__ */ a(qn(), 1);
|
|
2934
|
+
Jn(), Yn.default.extend(Xn.default), Yn.default.locale("ko");
|
|
2935
|
+
function Zn(e) {
|
|
2913
2936
|
return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
|
|
2914
2937
|
}
|
|
2915
|
-
function
|
|
2916
|
-
return e?.toDate ? (0,
|
|
2938
|
+
function Qn(e, t = Date.now()) {
|
|
2939
|
+
return e?.toDate ? (0, Yn.default)(e.toDate()).from((0, Yn.default)(t)) : "방금 전";
|
|
2917
2940
|
}
|
|
2918
|
-
function
|
|
2919
|
-
return e?.toDate ? (0,
|
|
2941
|
+
function $n(e) {
|
|
2942
|
+
return e?.toDate ? (0, Yn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
|
|
2920
2943
|
}
|
|
2921
|
-
function
|
|
2922
|
-
let n =
|
|
2923
|
-
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${
|
|
2944
|
+
function er(e, t) {
|
|
2945
|
+
let n = $n(e), r = e?.toMillis?.(), i = t?.toMillis?.();
|
|
2946
|
+
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${$n(t)}`];
|
|
2924
2947
|
}
|
|
2925
2948
|
//#endregion
|
|
2926
2949
|
//#region src/composables/useMemiBoardWritingAssistant.ts
|
|
2927
|
-
var
|
|
2950
|
+
var tr = 10, nr = 864e5, rr = {
|
|
2928
2951
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
2929
2952
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
2930
2953
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -2933,13 +2956,13 @@ var er = 10, tr = 864e5, nr = {
|
|
|
2933
2956
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
2934
2957
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
2935
2958
|
};
|
|
2936
|
-
function
|
|
2959
|
+
function ir(e) {
|
|
2937
2960
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
2938
2961
|
}
|
|
2939
|
-
function
|
|
2962
|
+
function ar(e) {
|
|
2940
2963
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
2941
2964
|
}
|
|
2942
|
-
function
|
|
2965
|
+
function or() {
|
|
2943
2966
|
let e = le(), t = Q(), n = Z(), r = u();
|
|
2944
2967
|
async function i() {
|
|
2945
2968
|
let e = n.value?.uid;
|
|
@@ -2947,9 +2970,9 @@ function ar() {
|
|
|
2947
2970
|
await oe(t, async (n) => {
|
|
2948
2971
|
let r = x(t, _(), e), i = await n.get(r);
|
|
2949
2972
|
if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
|
|
2950
|
-
let a = i.data(), o =
|
|
2973
|
+
let a = i.data(), o = ar(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < nr;
|
|
2951
2974
|
if (l && s >= 10) {
|
|
2952
|
-
let e = o +
|
|
2975
|
+
let e = o + nr, t = Math.max(1, Math.ceil((e - c) / 36e5));
|
|
2953
2976
|
throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
|
|
2954
2977
|
}
|
|
2955
2978
|
n.update(r, {
|
|
@@ -2978,10 +3001,10 @@ function ar() {
|
|
|
2978
3001
|
}), s = t.content.trim();
|
|
2979
3002
|
if (!s) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
|
|
2980
3003
|
let c = [
|
|
2981
|
-
`작업: ${
|
|
3004
|
+
`작업: ${rr[t.action]}`,
|
|
2982
3005
|
t.title ? `현재 제목: ${t.title}` : "",
|
|
2983
3006
|
`본문:\n${s.slice(0, 12e3)}`
|
|
2984
|
-
].filter(Boolean).join("\n\n"), l =
|
|
3007
|
+
].filter(Boolean).join("\n\n"), l = ir((await o.generateContent(c)).response.text());
|
|
2985
3008
|
if (!l) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
|
|
2986
3009
|
return l;
|
|
2987
3010
|
}
|
|
@@ -2989,14 +3012,14 @@ function ar() {
|
|
|
2989
3012
|
}
|
|
2990
3013
|
//#endregion
|
|
2991
3014
|
//#region src/utils/renderMarkdown.ts
|
|
2992
|
-
function
|
|
3015
|
+
function sr(e) {
|
|
2993
3016
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2994
3017
|
}
|
|
2995
|
-
function
|
|
3018
|
+
function cr(e) {
|
|
2996
3019
|
let t = e;
|
|
2997
3020
|
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;
|
|
2998
3021
|
}
|
|
2999
|
-
function
|
|
3022
|
+
function lr(e) {
|
|
3000
3023
|
if (!e?.trim()) return "";
|
|
3001
3024
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
3002
3025
|
function c() {
|
|
@@ -3005,7 +3028,7 @@ function cr(e) {
|
|
|
3005
3028
|
return;
|
|
3006
3029
|
}
|
|
3007
3030
|
let e = o;
|
|
3008
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
3031
|
+
n.push(`<${e}>${s.map((e) => `<li>${cr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
3009
3032
|
}
|
|
3010
3033
|
for (; r < t.length;) {
|
|
3011
3034
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -3014,7 +3037,7 @@ function cr(e) {
|
|
|
3014
3037
|
continue;
|
|
3015
3038
|
}
|
|
3016
3039
|
if (i) {
|
|
3017
|
-
a.push(
|
|
3040
|
+
a.push(sr(e)), r++;
|
|
3018
3041
|
continue;
|
|
3019
3042
|
}
|
|
3020
3043
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
|
|
@@ -3025,24 +3048,24 @@ function cr(e) {
|
|
|
3025
3048
|
if (u) {
|
|
3026
3049
|
c();
|
|
3027
3050
|
let e = u[1].length;
|
|
3028
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${sr(
|
|
3051
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${cr(sr(u[2]))}</h${e}>`), r++;
|
|
3029
3052
|
continue;
|
|
3030
3053
|
}
|
|
3031
3054
|
if (e.startsWith("> ")) {
|
|
3032
3055
|
c();
|
|
3033
3056
|
let e = [];
|
|
3034
|
-
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(
|
|
3035
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
3057
|
+
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(sr((t[r] ?? "").slice(2))), r++;
|
|
3058
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${cr(e.join("<br />"))}</blockquote>`);
|
|
3036
3059
|
continue;
|
|
3037
3060
|
}
|
|
3038
3061
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
3039
3062
|
if (d) {
|
|
3040
|
-
o && o !== "ul" && c(), o = "ul", s.push(
|
|
3063
|
+
o && o !== "ul" && c(), o = "ul", s.push(sr(d[1])), r++;
|
|
3041
3064
|
continue;
|
|
3042
3065
|
}
|
|
3043
3066
|
let f = e.match(/^\d+\.\s+(.+)$/);
|
|
3044
3067
|
if (f) {
|
|
3045
|
-
o && o !== "ol" && c(), o = "ol", s.push(
|
|
3068
|
+
o && o !== "ol" && c(), o = "ol", s.push(sr(f[1])), r++;
|
|
3046
3069
|
continue;
|
|
3047
3070
|
}
|
|
3048
3071
|
if (!e.trim()) {
|
|
@@ -3054,13 +3077,13 @@ function cr(e) {
|
|
|
3054
3077
|
for (; r < t.length;) {
|
|
3055
3078
|
let e = t[r] ?? "";
|
|
3056
3079
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
3057
|
-
p.push(
|
|
3080
|
+
p.push(sr(e)), r++;
|
|
3058
3081
|
}
|
|
3059
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
3082
|
+
n.push(`<p class="my-2 leading-relaxed">${cr(p.join("<br />"))}</p>`);
|
|
3060
3083
|
}
|
|
3061
3084
|
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");
|
|
3062
3085
|
}
|
|
3063
3086
|
//#endregion
|
|
3064
|
-
export {
|
|
3087
|
+
export { zt as AUTHOR_MEMO_MAX_LENGTH, _n as BOARD_SECTION_COLS, vn as BOARD_SECTION_KINDS, yn as BOARD_SECTION_SORTS, mn as BOARD_USER_ROLES, Wt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, St as DEFAULT_BOARDS, Ct as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Zt as REPORT_DETAIL_MAX_LENGTH, Qt as REPORT_REASONS, tr as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, xt as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, xn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, bn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Zn as formatDate, $n as formatFullDate, Qn as formatRelativeDate, Ze as formatRestrictedUntilLabel, er as formatTimestampDetails, gt as hasBodyImage, ht as hasBodyText, Be as isAssignedStaff, _t as isContentEmpty, Dt as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Yt as memiBoardCommentLikesKey, Mn as miniBoardView, Dn as miniCommentLink, En as miniMoreLink, On as miniPostImage, Tn as miniPostLink, kn as miniPostTitle, Nn as miniPostsView, An as miniViewGroup, jn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Vn as normalizeBoardSection, Sn as pairSectionCols, pt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, lr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, $t as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, Cn as sectionColClass, wn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, mt as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Vt as useMemiBoardAuthorMemo, Rt as useMemiBoardCommentFeed, Xt as useMemiBoardCommentLikes, Kt as useMemiBoardComments, u as useMemiBoardConfig, Jt as useMemiBoardLikes, Un as useMemiBoardMiniLatest, Wn as useMemiBoardMiniPicked, pn as useMemiBoardModeration, Mt as useMemiBoardPost, jt as useMemiBoardPostList, kt as useMemiBoardPosts, qt as useMemiBoardReplies, nn as useMemiBoardReportQueue, en as useMemiBoardReports, Hn as useMemiBoardSections, Et as useMemiBoardSettings, It as useMemiBoardUserComments, Pt as useMemiBoardUserPosts, gn as useMemiBoardUserProfile, hn as useMemiBoardUsers, rn as useMemiBoardViews, or as useMemiBoardWritingAssistant, Gn as versionHistory, bt as videoListCoverUrl, yt as youtubeId };
|
|
3065
3088
|
|
|
3066
3089
|
//# sourceMappingURL=index.js.map
|