OverviewFilesCommitsIssuesDocsReleasesPeers

keet-cli / keet-core-4.14.4-pretty / lib / client / view.js

624 lines · javascript ref:
const e = require("bare-events"),
  s = require("b4a"),
  t = 200
class i {
  constructor(e) {
    ;((this.value = e), (this.prev = null), (this.next = null))
  }
}
class h {
  constructor(e) {
    ;((this.value = e), (this.refs = 0), (this.gc = null))
  }
}
class n {
  constructor() {
    ;((this.head = null), (this.tail = null), (this.size = 0))
  }
  add(e) {
    const s = new i(e)
    return (
      this.tail ? ((this.tail.next = s), (s.prev = this.tail)) : (this.head = s),
      (this.tail = s),
      this.size++,
      s
    )
  }
  remove(e) {
    ;(e.prev ? (e.prev.next = e.next) : (this.head = e.next),
      e.next ? (e.next.prev = e.prev) : (this.tail = e.prev),
      this.size--)
  }
  shift() {
    const e = this.head
    return e ? (this.remove(e), e.value) : null
  }
}
class o {
  constructor(e) {
    ;((this._maxSize = e), (this._m = new Map()), (this._gc = new n()))
  }
  get size() {
    return this._m.size
  }
  maybeEvict() {
    for (; this._m.size > this._maxSize && this._gc.size > 0; ) {
      const e = this._gc.shift()
      this._m.delete(e)
    }
  }
  set(e, s) {
    const t = this._m.get(e)
    if (t) return void (t.value = s)
    const i = new h(s)
    ;((i.gc = this._gc.add(e)), this._m.set(e, i))
  }
  get(e) {
    return this._m.get(e)?.value
  }
  ref(e) {
    const s = this._m.get(e)
    s && (s.refs++ > 0 || (this._gc.remove(s.gc), (s.gc = null)))
  }
  unref(e) {
    const s = this._m.get(e)
    s && 0 !== s.refs && (--s.refs > 0 || (s.gc = this._gc.add(e)))
  }
  forceGC(e) {
    const s = this._maxSize
    ;((this._maxSize = e), this.maybeEvict(), (this._maxSize = s))
  }
}
class a extends e {
  constructor({ size: e = 200 } = {}) {
    ;(super(), (this._cache = new o(e)))
  }
  get size() {
    return this._cache.size
  }
  id(e) {}
  cacheId(e) {}
  diff(e, s) {
    return !0
  }
  ref(e) {
    this._cache.ref(e)
  }
  unref(e) {
    this._cache.unref(e)
  }
  maybeGC() {
    this._cache.maybeEvict()
  }
  getRaw(e) {
    return this._cache.get(this.constructor.cacheId(e))
  }
  get(e) {
    return this._cache.get(e)
  }
  add(e, s) {
    const t = s?.cacheId || this.constructor.cacheId(this.id(e)),
      i = this._cache.get(t)
    return ((i && !this.diff(i, e)) || (this._cache.set(t, e), this.emit("update", t, e)), t)
  }
}
class c extends a {
  static cacheId(e) {
    return s.toString(e, "hex")
  }
  id(e) {
    return e.key
  }
  diff(e, s) {
    return e.profile.timestamp !== s.profile.timestamp
  }
}
class r extends a {
  static cacheId(e) {
    return s.toString(e, "hex")
  }
  id(e) {
    return e.key
  }
  diff(e, s) {
    return (
      e.status.isActive !== s.status.isActive ||
      e.status.isIndexing !== s.status.isIndexing ||
      e.profile.name !== s.profile.name
    )
  }
}
class d extends a {
  static cacheId(e) {
    return s.toString(e, "hex")
  }
  id(e) {
    return e.roomKey
  }
  diff(e, s) {
    return !0
  }
}
class m extends a {
  static cacheId({ key: e, seq: t }) {
    return `${s.toString(e, "hex")}-${t}`
  }
  id(e) {
    return e.id
  }
  diff(e, s) {
    return !0
  }
}
class g extends a {
  static cacheId({ key: e, seq: t }) {
    return `${s.toString(e, "hex")}-${t}`
  }
  id(e) {
    return e.id
  }
  diff(e, s) {
    return !0
  }
}
class u extends e {
  constructor(e) {
    ;(super(),
      (this.view = e),
      (this.status = null),
      (this.member = null),
      (this.devices = []),
      (this.memberKey = null),
      (this.deviceKeys = []),
      (this._clock = 0),
      (this._memberId = null),
      (this._deviceIds = []))
  }
  _unref() {
    this._memberId && this.view.members.unref(this._memberId)
    for (const e of this._deviceIds) this.view.devices.unref(e)
  }
  _ref() {
    this._memberId && this.view.members.ref(this._memberId)
    for (const e of this._deviceIds) this.view.devices.ref(e)
  }
  _inflate() {
    ;((this.member = this.view.members.get(this._memberId)),
      (this.devices = this._deviceIds.map((e) => this.view.devices.get(e))))
  }
  onMemberChanged() {
    ;(this._inflate(), this.emit("update:member"), this.emit("update"))
  }
  onDeviceChanged() {
    ;(this._inflate(), this.emit("update:device"), this.emit("update"))
  }
  onReset({ memberKey: e, deviceKeys: s, status: t }) {
    ;(this._unref(),
      (this.status = t),
      (this.memberKey = e),
      (this.deviceKeys = s),
      (this._memberId = c.cacheId(this.memberKey)),
      (this._deviceIds = s.map((e) => r.cacheId(e))),
      this._ref(),
      this._inflate(),
      this.emit("reset"))
  }
  onWant(e) {
    this._clock = e
  }
  onHave(e) {
    e === this._clock && (this.emit("have", e), this.emit("update"))
  }
  onCancel(e) {
    e === this._clock && (this._clock = 0)
  }
}
class l {
  constructor(e, s, t) {
    ;((this.id = e),
      (this.cacheId = s),
      (this.role = t.role),
      (this.digest = t.digest),
      (this.config = t.config),
      (this.message = t.message),
      (this.messageId = t.messageId),
      (this.messageCacheId = m.cacheId(this.messageId)))
  }
  with(e) {
    return new l(this.id, this.cacheId, { ...this, ...e })
  }
}
class f extends e {
  constructor(e) {
    ;(super(), (this.view = e))
  }
  ref(e) {
    ;(this.view.rooms.ref(e.cacheId), e.messageCacheId && this.view.messages.ref(e.messageCacheId))
  }
  unref(e) {
    ;(this.view.rooms.unref(e.cacheId),
      e.messageCacheId && this.view.messages.unref(e.messageCacheId))
  }
  onRoleChanged({ id: e, role: s }) {
    const t = this.view.rooms.get(e)
    if (!t) return
    const i = t.with({ role: s })
    ;(this.view.rooms.add(i, { cacheId: e }), this.emit("update:role", i))
  }
  onDigestChanged({ id: e, digest: s }) {
    const t = this.view.rooms.get(e)
    if (!t) return
    const i = t.with({ digest: s })
    ;(this.view.rooms.add(i, { cacheId: e }), this.emit("update:digest", i))
  }
  onConfigChanged({ id: e, config: s }) {
    const t = this.view.rooms.get(e)
    if (!t) return
    const i = t.with({ config: s })
    ;(this.view.rooms.add(i, { cacheId: e }), this.emit("update:config", i))
  }
  onLastMessageChanged({ id: e, messageId: s }) {
    const t = this.view.rooms.get(e)
    if (!t) return
    const i = m.cacheId(s),
      h = this.view.messages.get(i),
      n = t.with({ messageId: s, messageCacheId: i, message: h })
    ;(this.view.rooms.add(n, { cacheId: e }), this.emit("update:message", n))
  }
  onAdd(e, { cacheId: t = null } = {}) {
    t || (t = s.toString(e.id, "hex"))
    const i = m.cacheId(e.messageId),
      h =
        (this.view.messages.get(i),
        {
          role: e.role,
          digest: e.digest,
          config: e.config,
          messageId: e.messageId,
          messageCacheId: i,
          message: this.view.messages.get(i),
        })
    let n = this.view.rooms.get(t)
    return (
      (n = n ? n.with(h) : new l(e.id, t, h)),
      this.view.rooms.add(n, { cacheId: t }),
      this.emit("update:reset", n),
      n
    )
  }
}
class I {
  constructor(e, s, t) {
    ;((this.id = e),
      (this.cacheId = s),
      (this.role = t.role),
      (this.chat = t.chat),
      (this.member = t.member),
      (this.reactions = t.reactions),
      (this.memberKey = t.memberKey),
      (this.memberId = t.memberId))
  }
  with(e) {
    return new I(this.id, this.cacheId, { ...this, ...e })
  }
}
class _ extends e {
  constructor(e) {
    ;(super(), (this.view = e))
  }
  ref(e) {
    ;(this.view.messages.ref(e.cacheId), e.memberId && this.view.members.ref(e.memberId))
  }
  unref(e) {
    ;(this.view.messages.unref(e.cacheId), e.memberId && this.view.members.unref(e.memberId))
  }
  onChatChanged({ chat: e }) {
    const s = m.cacheId(e.id),
      t = this.view.messages.get(s)
    if (!t) return
    const i = t.with({ chat: e })
    ;(this.view.messages.add(i, { cacheId: s }),
      this.emit("update:chat", { chat: e, messages: [i] }))
  }
  onRoleChanged({ ids: e, role: s }) {
    const t = []
    for (const i of e) {
      const e = m.cacheId(i),
        h = this.view.messages.get(e)
      if (!h) continue
      const n = h.with({ role: s })
      ;(this.view.messages.add(n, { cacheId: e }), t.push(n))
    }
    t.length > 0 && this.emit("update:role", { ids: e, role: s, messages: t })
  }
  onMemberChanged({ ids: e, memberKey: s }) {
    const t = this.view.members.get(c.cacheId(s))
    if (!t) return
    const i = []
    for (const s of e) {
      const e = m.cacheId(s),
        h = this.view.messages.get(e)
      if (!h) continue
      const n = h.with({ member: t })
      ;(this.view.messages.add(n, { cacheId: e }), i.push(n))
    }
    i.length > 0 && this.emit("update:member", { ids: e, memberKey: s, messages: i })
  }
  onReactionsChanged({ id: e, reactions: s }) {
    const t = m.cacheId(e),
      i = this.view.messages.get(t)
    if (!i) return
    const h = i.with({ reactions: s })
    ;(this.view.messages.add(h, { cacheId: t }),
      this.emit("update:reactions", { id: e, reactions: s, messages: [h] }))
  }
  onAdd(e, { cacheId: s = null } = {}) {
    s || (s = m.cacheId(e.chat.id))
    const t = c.cacheId(e.memberKey),
      i = e.memberKey
    let h = this.view.messages.get(s)
    if (!h) {
      const n = {
        reactions: this.view.reactions.get(s),
        member: this.view.members.get(t),
        chat: e.chat,
        role: e.role,
        memberKey: i,
        memberId: t,
      }
      ;((h = new I(e.chat.id, s, n)), this.view.messages.add(h, { cacheId: s }))
    }
    return (this.emit("update:reset", h), h)
  }
}
class v extends e {
  constructor(e) {
    ;(super(),
      (this.view = e),
      (this.pendingInvitations = []),
      (this.identityJoinRequests = []),
      (this.clock = 0),
      (this._pending = !0),
      (this._invitationIds = new Set()),
      (this._joinRequestIds = new Set()))
  }
  onWant(e) {
    ;((this.clock = e), (this._pending = !0))
  }
  onHave(e) {
    e === this.clock && ((this._pending = !1), this.emit("have", e), this.emit("update"))
  }
  onCancel(e) {
    e === this.clock && ((this.clock = 0), (this._pending = !0))
  }
  onReset({ pendingInvitations: e, identityJoinRequests: t }) {
    const i = new Set(e.map((e) => s.toString(e.id, "hex"))),
      h = new Set(t.map((e) => s.toString(e.deviceKey, "hex"))),
      n = !b(this._invitationIds, i),
      o = !b(this._joinRequestIds, h)
    ;(n || o) &&
      ((this.pendingInvitations = e),
      (this.identityJoinRequests = t),
      (this._invitationIds = i),
      (this._joinRequestIds = h),
      this.emit("reset"),
      this._pending || this.emit("update"))
  }
}
class w extends e {
  constructor(e) {
    ;(super(),
      (this.view = e),
      (this.rooms = null),
      (this.keys = null),
      (this.start = 0),
      (this.end = 0),
      (this.total = 0),
      (this.clock = 0),
      (this._pending = !0),
      (this._ids = null),
      (this._cacheIdSet = null))
  }
  _unref() {
    if (this.rooms) for (const e of this.rooms) e && this.view.roomInfo.unref(e)
  }
  _ref() {
    if (this._ids)
      for (const e of this._ids) {
        this.view.rooms.ref(e)
        const s = this.view.rooms.get(e)
        s?.messageCacheId && this.view.messages.ref(s.messageCacheId)
      }
  }
  _refreshRooms() {
    this.rooms = this._ids.map((e) => this.view.rooms.get(e))
  }
  onRoomChanged(e, s) {
    this._cacheIdSet &&
      this._cacheIdSet.has(e.cacheId) &&
      (this._unref(),
      this._refreshRooms(),
      this._ref(),
      this.emit("update:" + s, e),
      this._pending || this.emit("update"))
  }
  onWant(e, s, t) {
    ;((this.clock = e), (this.start = s), (this.end = t), (this._pending = !0))
  }
  onHave(e) {
    e === this.clock && ((this._pending = !1), this.emit("have", e), this.emit("update"))
  }
  onCancel(e) {
    e === this.clock &&
      ((this.start = 0), (this.end = 0), (this._pending = !0), (this._cacheIdSet = null))
  }
  onReset({ keys: e, start: t, end: i, total: h }) {
    const n = this._ids
    if (
      ((this.keys = e),
      (this.start = t),
      (this.end = i),
      (this.total = h),
      (this._ids = e.map((e) => s.toString(e, "hex"))),
      (this._cacheIdSet = new Set(this._ids)),
      this._ref(),
      n)
    )
      for (const e of n) {
        const s = this.view.rooms.get(e)
        s && this.view.roomInfo.unref(s)
      }
    ;(this._refreshRooms(), this.emit("reset"), this._pending || this.emit("update"))
  }
}
class p extends e {
  constructor(e) {
    ;(super(),
      (this.view = e),
      (this.messages = null),
      (this.ids = null),
      (this.room = null),
      (this.roomKey = null),
      (this.start = 0),
      (this.end = 0),
      (this.total = 0),
      (this.isBottom = !1),
      (this.clock = 0),
      (this._pending = !0),
      (this._ids = null),
      (this._cacheIdSet = null),
      (this._roomCacheId = null))
  }
  _unref() {
    if (this._cacheIds)
      for (const e of this._cacheIds) {
        const s = this.view.messages.get(e)
        s && this.view.messageInfo.unref(s)
      }
  }
  _ref() {
    if (this._cacheIds)
      for (const e of this._cacheIds) {
        this.view.messages.ref(e)
        const s = this.view.messages.get(e)
        s?.memberId && this.view.members.ref(s.memberId)
      }
  }
  _refreshMessages() {
    this.messages = this._cacheIds.map((e) => this.view.messages.get(e))
  }
  _refreshRoom() {
    this.room = this._roomCacheId ? this.view.rooms.get(this._roomCacheId) : null
  }
  onRoomChanged(e, s) {
    this._roomCacheId &&
      this._roomCacheId === e.cacheId &&
      (this._refreshRoom(),
      this.emit("update:room:" + s, this.room),
      this.emit("update:room", this.room),
      this._pending || this.emit("update"))
  }
  onMessagesChanged(e, s) {
    const t = e.filter((e) => this._cacheIdSet?.has(e.cacheId))
    0 !== t.length &&
      (this._unref(),
      this._refreshMessages(),
      this._ref(),
      this.emit("update:" + s, t),
      this._pending || this.emit("update"))
  }
  onWant(e, s, t, i, h) {
    ;((this.clock = e),
      (this.roomKey = s),
      (this._roomCacheId = d.cacheId(s)),
      (this.start = t),
      (this.size = i),
      (this.isBottom = h),
      (this._pending = !0))
  }
  onHave(e) {
    e === this.clock && ((this._pending = !1), this.emit("have", e), this.emit("update"))
  }
  onCancel(e) {
    e === this.clock &&
      ((this.room = null),
      (this.roomKey = null),
      (this.start = 0),
      (this.end = 0),
      (this._pending = !0),
      (this._roomCacheId = null),
      (this._cacheIdSet = null))
  }
  onReset({ ids: e, roomKey: s, start: t, size: i, total: h, isBottom: n }) {
    const o = this._cacheIds
    if (
      ((this.ids = e),
      (this.roomKey = s),
      (this.start = t),
      (this.size = i),
      (this.total = h),
      (this.isBottom = n),
      (this._roomCacheId = d.cacheId(s)),
      (this._cacheIds = e.map((e) => m.cacheId(e))),
      (this._cacheIdSet = new Set(this._cacheIds)),
      this._ref(),
      o)
    )
      for (const e of o) {
        const s = this.view.messages.get(e)
        s && this.view.messageInfo.unref(s)
      }
    ;(this._refreshMessages(),
      this._refreshRoom(),
      this.emit("reset"),
      this._pending || this.emit("update"))
  }
}
function b(e, s) {
  if (e.size !== s.size) return !1
  for (const t of e) if (!s.has(t)) return !1
  return !0
}
module.exports = class {
  constructor() {
    ;((this.members = new c()),
      (this.devices = new r()),
      (this.reactions = new g()),
      (this.messages = new m()),
      (this.rooms = new d()),
      (this.identity = new u(this)),
      (this.pairing = new v(this)),
      (this.lobby = new w(this)),
      (this.chat = new p(this)),
      (this.roomInfo = new f(this)),
      (this.messageInfo = new _(this)),
      this.roomInfo.on("update:reset", (e) => {
        this.chat.onRoomChanged(e, "reset")
      }),
      this.roomInfo.on("update:role", (e) => {
        ;(this.lobby.onRoomChanged(e, "role"), this.chat.onRoomChanged(e, "role"))
      }),
      this.roomInfo.on("update:digest", (e) => {
        ;(this.lobby.onRoomChanged(e, "digest"), this.chat.onRoomChanged(e, "digest"))
      }),
      this.roomInfo.on("update:config", (e) => {
        ;(this.lobby.onRoomChanged(e, "config"), this.chat.onRoomChanged(e, "config"))
      }),
      this.roomInfo.on("update:message", (e) => {
        this.lobby.onRoomChanged(e, "message")
      }),
      this.messageInfo.on("update:chat", ({ messages: e }) =>
        this.chat.onMessagesChanged(e, "chat"),
      ),
      this.messageInfo.on("update:role", ({ messages: e }) =>
        this.chat.onMessagesChanged(e, "role"),
      ),
      this.messageInfo.on("update:member", ({ messages: e }) =>
        this.chat.onMessagesChanged(e, "member"),
      ),
      this.messageInfo.on("update:reactions", ({ messages: e }) =>
        this.chat.onMessagesChanged(e, "reactions"),
      ))
  }
}