const {
encodeChatMessage: e,
encodeReactionsDigest: t,
encodeMyReactions: s,
encodeCallRecord: n,
} = require("./encode"),
{ getMemberForDeviceKey: a } = require("../../core/contract/query"),
l = require("b4a"),
{
contract: {
hyperschema: { getEnum: o },
},
} = require("../../../schema/spec"),
i = o("@keet/label-type")
async function r(e, t, s, n, a, l) {
const [o, i] = await Promise.all([s.get("@local/rooms", { roomKey: t }), e()])
if (!i || !o) return []
const r = Array.isArray(i) ? i : [i],
c = []
for (const e of r) c.push(f(t, n, a, o, l, e))
return Promise.all(c)
}
async function c({ roomKey: e, local: n, snapshot: a, room: l, oplog: o, msgId: i }) {
const [r, c] = await Promise.all([
i ? { id: i } : a.get("@keet/chat-by-message-id", { oplog: o }),
l || n.get("@local/rooms", { roomKey: e }),
])
if (!r || !c) return null
const { selfMember: d } = c,
[m, f] = await Promise.all([
a.get("@keet/reaction-digests", { id: r.id }),
d
? a
.find("@keet/reactions", {
gte: { id: r.id, sender: d.key },
lte: { id: r.id, sender: d.key },
})
.toArray()
: [],
])
return { digest: t(m), mine: s(f) }
}
async function d({ snapshot: e, msgId: t, localRoom: s }) {
if (!t || !s) return null
const { selfMember: n } = s,
[a, l] = await Promise.all([
e.get("@keet/chat-label-digests", { id: t }),
!!n &&
e.findOne("@keet/chat-labels", {
gte: { id: t, sender: n.key, label: i.Flag },
lte: { id: t, sender: n.key, label: i.Flag },
}),
])
return {
flags: a?.labels.find((e) => e.label === i.Flag)?.count ?? 0,
flagged: !!l,
flagsClock: a?.flagsClock ?? 0,
editedAt: a?.editedAt ?? 0,
}
}
function m({ dm: e, readLengths: t, selfMember: s }, n) {
return (
!!(e?.length && n && t?.length && s) &&
!!t.find((e) => !l.equals(e.memberKey, s.key) && n.index < e.length)
)
}
async function f(t, s, n, l, o, i) {
const r = a(s, i.oplog.key),
{ filePromises: f, linkFilePromises: y } = g(t, s, n, o, i),
[u, h, b, k] = await Promise.all([
r,
Promise.all(f),
c({ snapshot: s, room: l, msgId: i.id }),
d({ snapshot: s, localRoom: l, msgId: i.id }),
Promise.all(y),
])
return (
(i.reactions = b),
(i.labels = k),
h.length && (i.message.files = h),
(i.read = m(l, i.id)),
e({ record: i, member: u, localRoom: l })
)
}
async function y(e, t, s, n, a) {
const l = await s.get("@keet/files", { id: a })
if (!l) return null
const o = await t.getFileEntryWithPreview(e, l.file, { loadBlob: (e) => n.get(e) })
return { ...l.file, entry: o }
}
function g(e, t, s, n, a) {
const l = [],
o = []
if (a.message?.files?.length > 0) for (const o of a.message.files) l.push(y(e, n, t, s, o))
const i = a.message?.display?.httpLink || []
for (const e of i) c(e)
const r = a.message?.display?.pearLink || []
for (const e of r) c(e)
return { filePromises: l, linkFilePromises: o }
function c(a) {
if ((a?.preview?.files || []).length) {
const l = a.preview.files.map(async (l) => {
a.preview.file = await y(e, n, t, s, l)
})
o.push(...l)
}
}
}
async function u(e, t) {
const s = b(e),
n = await e.findOne(t)
return n ? s(n) : null
}
async function h(e, t, s = !0) {
const n = b(e, s)
return k(e.find(t), n)
}
function b(e, t) {
return async function (e) {
const a = { ...e, members: await s(e) }
return t ? n(a) : a
}
async function s({ callKey: t }) {
return k(
e.find("@keet/call-members", { gte: { callKey: t }, lte: { callKey: t } }),
async (e) => ({ ...e, devices: await a(e) }),
)
}
async function a({ callKey: t, memberKey: s }) {
const n = { gte: { callKey: t, memberKey: s }, lte: { callKey: t, memberKey: s } }
return k(e.find("@keet/call-devices-by-member-key", n), async (e) => ({
...e,
streams: await l(e),
}))
}
async function l({ callKey: t, deviceKey: s }) {
const n = { gte: { callKey: t, deviceKey: s }, lte: { callKey: t, deviceKey: s } },
a = e.find("@keet/call-streams-by-device-key", n),
l = []
for await (const e of a) l.push(e)
return l
}
}
async function k(e, t) {
const s = []
for await (const n of e) s.push(t(n))
return Promise.all(s)
}
module.exports = {
getChatMessages: r,
getReactions: c,
getChatLabels: d,
getChatMessage: f,
getCalls: h,
getCall: u,
isDMRead: m,
}