const e = require("b4a"),
t = require("./registration"),
{ getMemberForDeviceKey: s, isMemberActive: i } = require("./query"),
{ KeetContractError: a } = require("./errors"),
{ insertMessageFiles: r, deleteMessageFiles: o, updateImageFile: n } = require("./avatars"),
{
contract: {
hyperschema: { getEnum: l },
},
} = require("../../../schema/spec"),
{ chatStats: d, fileStats: c } = require("./stats"),
g = l("@keet/room-type")
async function h(e, t, s, i) {
const a = await e.get("@keet/threads", { id: 0 }),
r = {
id: { thread: 0, index: a.count },
oplog: t,
clock: a.stats.chat.clock,
timestamp: i,
event: s,
status: { isEdited: !1, isPinned: !1, isDeleted: !1 },
}
;(await e.insert("@keet/chat", r), await d(e, a, r))
}
async function p({ message: e, header: t }, { db: o, blobs: n, writer: l }) {
let [c, h, p, w, k, m] = await Promise.all([
s(o, l.key),
o.get("@keet/threads", { id: 0 }),
e.parent ? o.get("@keet/threads-by-parent", { oplog: e.parent }) : null,
e.parent ? o.get("@keet/chat-by-message-id", { oplog: e.parent }) : null,
e.replyTo ? o.get("@keet/chat-by-message-id", { oplog: e.replyTo }) : null,
o.get("@keet/system-digest"),
])
if (!i(c)) throw a.INACTIVE_MEMBER()
if (c.status.isMuted) throw a.WRITER_IS_MUTED()
const y = m.settings.roomType === g.Broadcast || m.settings.isAppendingDisabled
if (!c.status.isModerator && y) throw a.MODERATORS_ONLY()
if (e.parent && !w) throw a.PARENT_MSG_INVALID()
if (e.replyTo && !k) throw a.REPLYTO_MSG_INVALID()
if ((e.parent || (p = h), e.event?.callMissed)) {
const e = await o.get("@keet/chat", { id: { thread: p.id, index: p.count - 1 } })
if (e && e.event.callMissed) throw a.NO_DUPLICATE_CALL_EVENTS()
}
if (m.settings.isAttachmentsDisabled && T(e)) throw a.FILE_ATTACHMENTS_DISABLED()
if (m.settings.isLinksDisabled && _(e)) throw a.LINKS_DISABLED()
const E = h.stats.files.clock
e.files = await r({ primaryThread: h, clock: E, message: e, blobs: n, db: o, oplog: l })
const f = {
id: { thread: p.id, index: p.count },
oplog: l,
clock: h.stats.chat.clock,
timestamp: t.timestamp,
message: e,
status: { isEdited: !1, isPinned: !1, isDeleted: !1 },
event: e.event,
}
;(await o.insert("@keet/chat", f), await d(o, h, f))
}
async function w({ header: t, oplog: n, message: l }, { db: c, blobs: g, writer: h }) {
const [p, w, k, m, y, E] = await Promise.all([
s(c, h.key),
s(c, n.key),
c.get("@keet/chat-by-message-id", { oplog: n }),
c.get("@keet/threads", { id: 0 }),
l.replyTo ? c.get("@keet/chat-by-message-id", { oplog: l.replyTo }) : null,
c.get("@keet/system-digest"),
])
if (!i(p)) throw a.INACTIVE_MEMBER()
if (!k) throw a.TARGET_MSG_INVALID()
if (!w) throw a.TARGET_MEMBER_NOT_FOUND()
if (!e.equals(p.key, w.key)) throw a.SELF_ONLY()
if (k.status.isDeleted) throw a.TARGET_MSG_IS_DELETED()
if (l.replyTo && !y) throw a.REPLYTO_MSG_INVALID()
if (E.settings.isAttachmentsDisabled && T(l)) throw a.FILE_ATTACHMENTS_DISABLED()
if (E.settings.isLinksDisabled && _(l)) throw a.LINKS_DISABLED()
const f = []
;(k.message?.files?.length && (f.push(...k.message.files), (k.message.files = [])),
(l.files = []),
await o({ primaryThread: m, message: k, db: c, blobs: g }))
const u = m.stats.files.clock
await r({ primaryThread: m, clock: u, message: l, blobs: g, db: c, oplog: h })
const b = {
...k,
message: { ...k.message, text: l.text, files: f, display: l.display },
status: { ...k.status, isEdited: !0 },
editedAt: t.timestamp,
}
;(await c.insert("@keet/chat", b), await d(c, m, b, k))
}
async function k({ oplog: t }, { db: r, blobs: n, writer: l }) {
const [c, g, h, p] = await Promise.all([
s(r, l.key),
s(r, t.key),
r.get("@keet/chat-by-message-id", { oplog: t }),
r.get("@keet/threads", { id: 0 }),
])
if (!i(c)) throw a.INACTIVE_MEMBER()
if (!h) throw a.TARGET_MSG_INVALID()
if (!c.status.isModerator && !e.equals(c.key, g.key)) throw a.MODERATORS_OR_SELF_ONLY()
await o({ primaryThread: p, message: h, db: r, blobs: n })
const w = { ...h, message: null, status: { ...h.status, isDeleted: !0, isPinned: !1 } }
;(await r.insert("@keet/chat", w), await d(r, p, w, h))
const k = await r.get("@keet/chat-label-digests", { id: h.id })
k && (await r.insert("@keet/chat-label-digests", { ...k, isDeleted: !0 }))
}
async function m({ oplog: e, isPinned: t }, { db: r, writer: o }) {
const [n, l, c] = await Promise.all([
s(r, o.key),
r.get("@keet/chat-by-message-id", { oplog: e }),
r.get("@keet/threads", { id: 0 }),
])
if (!i(n)) throw a.INACTIVE_MEMBER()
if (!l) throw a.TARGET_MSG_INVALID()
if (!n.status.isModerator) throw a.MODERATORS_ONLY()
const g = { ...l, status: { ...l.status, isPinned: t } }
;(await r.insert("@keet/chat", g), await d(r, c, g, l))
}
async function y(
{ oplog: t, index: r, preview: o, displayType: l },
{ db: d, blobs: g, writer: h },
) {
const [p, w, k, m] = await Promise.all([
s(d, h.key),
s(d, t.key),
d.get("@keet/chat-by-message-id", { oplog: t }),
d.get("@keet/threads", { id: 0 }),
])
if (!i(p)) throw a.INACTIVE_MEMBER()
if (!k) throw a.TARGET_MSG_INVALID()
if (!e.equals(p.key, w.key)) throw a.SELF_ONLY()
const y = l ? u(k, l, r) : k.message?.files,
E = l ? 0 : r
if (!y || y.length <= E) throw a.FILE_REFERENCE_INVALID("files is falsy or invalid index")
const f = await d.get("@keet/files", { id: y[E] })
if (!f) throw a.FILE_RECORD_INVALID("record is falsy")
;((f.file.preview = await n(g, null, o)), await d.insert("@keet/files", f), await c(d, m, f, f))
}
async function E({ length: e }, { db: t, writer: a }) {
const [r, o] = await Promise.all([t.get("@keet/system-digest"), s(t, a.key)])
i(o) &&
!o.status.isMuted &&
r.settings.roomType === g.DirectMessage &&
(await t.insert("@keet/read-lengths", { memberKey: o.key, length: e }))
}
function f(e) {
;(e.add("@keet/add-chat-message", p),
e.add("@keet/update-chat-message", w),
e.add("@keet/remove-chat-message", k),
e.add("@keet/pin-chat-message", m),
e.add("@keet/add-file-preview", y),
e.add("@keet/update-read-length", E))
}
function u(e, t, s) {
switch (t) {
case 1:
return e.message.display.httpLink[s].preview.files
case 2:
return e.message.display.pearLink[s].preview.files
}
}
function T(e) {
return (
e?.files?.length > 0 ||
e?.display?.httpLink?.some((e) => e.preview) ||
e?.display?.pearLink?.some((e) => e.preview)
)
}
function _(e) {
return e.display?.httpLink?.length > 0 || e?.display?.pearLink?.length > 0
}
module.exports = { addEvent: h, [t]: f }