OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-store / store / chat / loader / loader.saga.js

153 lines · javascript ref:
import {
  call as e,
  put as t,
  fork as o,
  select as l,
  spawn as a,
  take as n,
  join as r,
  delay as i,
  race as c,
} from "redux-saga/effects"
import { block as s } from "../../lib/effects"
import { first as d, isEmpty as y, last as m } from "../../lib/array"
import { logWarning as f, taskErrorSaga as u } from "../../error"
import {
  getChatMessageCount as h,
  fetchMessagesCmd as I,
  reloadMessagesCmd as p,
  subChatMessagesEvt as g,
  setChatMessagesFetching as T,
  getChatMessageSeqLoaded as w,
} from "../list-view"
import { isQueryValid as $, getQueryCount as L, hasQueryIntersection as k } from "../message"
import { createChatLoader as A } from "./loader"
import { createCtrlCtxRef as C } from "./loader-step"
import { LOADER_FAIL as N, PORTION_SIZE_MAX as O } from "./loader.constants"
import { isSeqInRange as v, noMessagesLoaded as x } from "../chat.model"
import { CHAT_WORKER as R } from "../chat.constant"
import { sharedCoreMessagesUpdate as B } from "../../api/data.action"
const q = 1e4,
  E = 3e4
export const isUpdateChatMessagesByWorker = (e) => (t) => B.match(t) && t.payload.worker === e
export function* fetchCtrl({ name: o, ctxRef: l }) {
  let a = 0
  try {
    ;((a = yield e(l.waitStart)),
      yield t(T({ name: o, status: !0, timestamp: a })),
      yield i(q),
      yield t(
        f({
          message: "chat loading slow",
          stack: `runFetchWorker: <${o}>: callCount=${l.callCount}`,
        }),
      ),
      yield s())
  } finally {
    a && (yield t(T({ name: o, status: !1, timestamp: Date.now(), callCount: l.callCount })))
  }
}
export function* chatCoreEventLoader(s, T) {
  const B = { [R.INITIAL]: null, [R.TOP]: null, [R.BOTTOM]: null }
  function q() {
    ;(B[R.INITIAL]?.cancel(), B[R.TOP]?.cancel(), B[R.BOTTOM]?.cancel())
  }
  let M = null
  function* W({ name: s, loader: d }) {
    B[s]?.isRunning() ||
      (B[s] = yield a(function* () {
        let a = null
        try {
          if (B[R.INITIAL]?.isRunning() && !(yield r(B[R.INITIAL]))) return
          const m = C()
          let u
          a = yield o(fetchCtrl, { name: s, ctxRef: m })
          do {
            u = M
            const o = yield l(w),
              a = { gte: o.top, lte: o.bottom },
              r = yield l(h),
              [I, p] = yield c([e(d.fetch[s], { query: u, loaded: a, count: r, ctxRef: m }), i(E)])
            p || I === N
              ? (p &&
                  (yield t(
                    f({ message: "chat loading cancelled", stack: `runFetchWorker: <${s}>` }),
                  )),
                (u = null))
              : y(I) ||
                (yield t(T, { worker: s, events: I }), yield n(isUpdateChatMessagesByWorker(s)))
          } while (u !== M)
        } catch (e) {
          yield u(`runFetchWorker <${s}>`, e)
        } finally {
          a?.cancel()
        }
      }))
  }
  const b = A({ roomId: s })
  try {
    for (;;) {
      const r = yield n([I, p, g]),
        i = yield l(w)
      if (I.match(r)) M = r.payload
      else if (p.match(r)) {
        const e = r.payload
        ;(e && (M = e), q())
      } else if (g.match(r)) {
        if (!Array.isArray(r.payload) || !r.payload.length) continue
        const e = d(r.payload),
          o = m(r.payload)
        if (!v(i, e?.seq) || !v(i, o?.seq)) continue
        if (B[R.INITIAL]?.isRunning() || x(i)) continue
        yield t(T, { worker: R.SUB, events: r.payload })
        continue
      }
      if (!$(M)) {
        M && (yield u("chatCoreEventLoader", `invalid query ${JSON.stringify(M)}`), (M = null))
        continue
      }
      if (L(M) > 3 * O) {
        const e = `loader: huge query ${JSON.stringify(M)}`
        yield t(f({ message: e, stack: "chatCoreEventLoader" }))
      }
      const c = { gte: i.top, lte: i.bottom },
        A = !k(M, c)
      ;(A && q(),
        (A || p.match(r)) &&
          (B[R.INITIAL] = yield a(function* () {
            let a = null
            try {
              const r = C()
              a = yield o(fetchCtrl, { name: R.INITIAL, ctxRef: r })
              const i = M,
                d = yield l(h)
              if (d <= 0) throw new Error("chat loader: count is not available")
              const m = yield e(b.fetch[R.INITIAL], { query: i, loaded: c, count: d, ctxRef: r })
              if (m === N)
                throw new Error(
                  `chat loader: initial messages FAIL roomId=${s} gte=${i?.gte} lte=${i?.lte} count=${d}`,
                )
              if (y(m))
                throw new Error(
                  `chat loader: empty initial messages: length=${m?.length} roomId=${s} gte=${i?.gte} lte=${i?.lte} count=${d}`,
                )
              return (
                yield t(T, { worker: R.INITIAL, events: m }),
                yield n(isUpdateChatMessagesByWorker(R.INITIAL)),
                !0
              )
            } catch (e) {
              yield u("loader:fetchInitial", e)
            } finally {
              a?.cancel()
            }
            return !1
          })),
        yield W({ name: R.TOP, loader: b }),
        yield W({ name: R.BOTTOM, loader: b }))
    }
  } finally {
    q()
  }
}