OverviewFilesCommitsIssuesDocsReleasesPeers

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

278 lines · javascript ref:
import { channel as e } from "redux-saga"
import {
  call as o,
  put as t,
  fork as i,
  select as r,
  takeLatest as d,
  getContext as l,
  delay as a,
  takeLeading as s,
} from "redux-saga/effects"
import { FEATURES as m, isFeatureSupported as n } from "@holepunchto/keet-core/features"
import {
  ERRORS as c,
  DISPLAY_TYPES as y,
  isDisplaySupported as f,
} from "@holepunchto/keet-core/legacy-constants"
import { block as p, apiTakeEvery as I } from "../lib"
import { isEmpty as g } from "../lib/object"
import { first as h } from "../lib/array"
import { getCoreBackend as u } from "../../backend"
import { apiCall as S } from "../../api/core"
import { getPreferencesNoChatRateLimit as L } from "../preferences"
import { taskErrorSaga as M } from "../error"
import { userErrorNotification as C } from "../notification"
import {
  updateFileEntryClearedHandler as N,
  updateFileEntryCleared as O,
  updateFileEntryListCleared as T,
} from "../media/file"
import { chatInit as k, chatReset as D, getChatConfig as E } from "./init"
import { ENV_CONTEXT_KEY as _ } from "../constants"
import { isMessageTextEmpty as b } from "./chat.model"
import {
  getChatReplyToMessageById as v,
  chatReplyToMessageInViewSaga as B,
  chatReplyToMessagePreloadSaga as q,
  chatReplyMessageSaga as x,
} from "./reply-message"
import {
  clearEditingMessage as R,
  getChatMessageById as A,
  setEditingMessage as H,
  deleteChatMessageSubmit as w,
  getChatMessageSendLimited as K,
  setChatMessageSendLimited as G,
  chatSubscribeMentions as P,
  chatMessageSubscribeDevice as U,
  chatMessageTransformSyncWorker as F,
  chatSubscribeMessages as V,
  setChatMessagesInView as j,
  isChatListViewJump as J,
  createAnchorToMessage as W,
  chatListViewJump as Y,
  chatListViewJumpToEditingMessage as $,
  chatListInitialLoad as z,
  chatListInitialRender as Q,
  chatListViewChangeHandler as X,
  chatListViewAnchorSaga as Z,
  chatListViewClearAnchorSaga as ee,
  chatListViewCountChangeHandler as oe,
} from "./list-view"
import { chatCoreEventLoader as te } from "./loader"
import { chatPinMessagesSaga as ie } from "./pin-message"
import { CHAT_MESSAGE_MAX_LENGTH as re } from "./chat.constant"
import { draftMessageSaga as de } from "./draft-message"
import {
  chatReactionSaga as le,
  chatMessageReportHandler as ae,
  reportChatMessage as se,
  reportChatMember as me,
  chatMemberReportHandler as ne,
} from "./reaction"
import { toggleChatSearching as ce, chatSearchSaga as ye } from "./search"
import { getCoreId as fe, sendChatMessageSuccess as pe } from "./message"
import { sharedCoreMessagesUpdate as Ie } from "../api/data.action"
import {
  setChatMessageLastSeen as ge,
  chatSubscribeUnreadSaga as he,
  chatUpdateLastSeenMessage as ue,
  getChatUnreadLastSeenSeq as Se,
} from "./unread"
import { sendChatFiles as Le, updateDisplayFiles as Me, chatFileInViewSaga as Ce } from "./file"
import { chatLastMessageCountCheckDebugLog as Ne } from "./last-message"
import { chatCleanupSaga as Oe } from "./cleanup"
import { getForwardedMessageByRoomId as Te } from "../room/forward-message/forward-message.reducer"
import { forwardMessageCall as ke } from "../room/forward-message/forward-message.saga"
import {
  getRoomIsAttachmentsDisabled as De,
  getRoomIsLinksDisabled as Ee,
} from "../room/room.selector"
import { setChatInputIsClearRequested as _e, setChatInputIsProcessing as be } from "./input"
const ve = [c.UNKNOWN_ROOM_COMMAND, c.INVALID_ROOM_COMMAND_ARGUMENTS],
  Be = {
    [y.BOLD]: "bold",
    [y.ITALIC]: "italic",
    [y.STRIKE_THROUGH]: "strike",
    [y.CODE]: "code",
    [y.CODE_BLOCK]: "codeBlock",
    [y.MENTION]: "mentions",
    [y.EMOJI]: "emoji",
    [y.HTTP_LINK]: "httpLinks",
    [y.PEAR_LINK]: "pearLinks",
  }
function* qe(e, o, t, i) {
  const r = (o || []).filter((o) => f(e, o.type))
  yield Me(r, i)
  const d = r.reduce((e, o) => {
    const { type: t, ...i } = o,
      r = Be[t]
    return ((e[r] = e[r] || []), e[r].push(i), e)
  }, {})
  return (t && n(e, m.REPLY_TO) && (d.replyTo = t), g(d) ? void 0 : d)
}
let xe = []
export function* rateLimitSaga() {
  const e = yield r(L),
    o = yield r(K)
  if (e || o) return
  xe.push(Date.now())
  const { msgRateLimit: i, msgRateLimitInterval: d } = yield r(E)
  if (xe.length < i) return
  xe = xe.slice(-i)
  const l = h(xe) + d - Date.now()
  if (!(l <= 0))
    try {
      ;(yield t(G(!0)), yield a(l))
    } finally {
      yield t(G(!1))
    }
}
export function* chatDeleteMessageHandler(e, o) {
  try {
    const t = fe(o),
      i = u()
    yield S(i.removeChatMessage, e, t)
  } catch (e) {
    yield M("chatDeleteMessageHandler", e)
  }
}
export function* chatAddMessageHandler({
  roomId: e,
  roomVersion: o,
  text: i,
  files: d,
  display: l,
  replyTo: a,
  previewByLink: s,
  locationShared: c,
}) {
  if (yield r(K)) return
  const f = u()
  try {
    if ((yield t(be(!0)), i && i.length > re))
      return void (yield t(C({ message: "Message is too big" })))
    let p
    a && (p = yield r(v, a))
    const I = yield r(De(e)),
      g = yield r(Ee(e)),
      h = g ? l?.filter((e) => ![y.HTTP_LINK, y.PEAR_LINK].includes(e.type)) : l,
      u = I || g ? {} : s,
      L = yield qe(o, h, p?.coreId, u),
      M = n(o, m.MULTI_FILES),
      N = n(o, m.LOCATION_SHARED_EVENT),
      O = yield r((o) => Te(o, e))
    ;(c?.latitude && c?.longitude && N
      ? (yield S(f.addLocationSharedEvent, e, i, { locationShared: c }), yield t(pe({ roomId: e })))
      : !i ||
        (d?.length && M && !I) ||
        (yield S(f.addChatMessage, e, i, L), yield t(pe({ roomId: e }))),
      d?.length > 0 && !I && (yield t(Le({ files: d, text: i, opts: L, isMultiFileSupported: M }))),
      yield ke(O),
      yield rateLimitSaga(),
      yield t(_e(!0)))
  } catch (e) {
    ve.includes(e.message)
      ? yield t(C({ message: e.message }))
      : yield M("sendChatMessageSubmit", e)
  } finally {
    yield t(be(!1))
  }
}
export function* chatUpdateMessageHandler({
  roomId: e,
  roomVersion: o,
  messageId: i,
  text: d,
  display: l,
  previewByLink: a,
}) {
  const s = u()
  try {
    if (!b(d)) {
      const t = yield r(A, i)
      let m
      if (t.replyTo) {
        const e = yield r(v, t.replyTo)
        m = e ? e?.coreId : null
      }
      const n = yield qe(o, l, m, a)
      yield S(s.updateChatMessage, e, t.coreId, d, n)
    }
    yield t(R())
  } catch (e) {
    yield M("updateChatMessageSubmit", e)
  }
}
export function* chatGetInitialData({ roomId: e, messageId: o }) {
  const t = u()
  let i,
    r = null
  if (o) {
    const { deviceId: d, seq: l } = fe(o),
      a = yield S(t.getChatMessageById, e, { deviceId: d, seq: l })
    a && Number.isInteger(a.seq)
      ? ((i = a.seq), (r = W({ messageId: o, seq: i })))
      : yield M("chatGetInitialData", `anchor: invalid data: ${JSON.stringify(a)}`)
  }
  const d = yield Se({ roomId: e })
  return ((i = i ?? d), { lastSeenSeq: d, startSeq: i, anchor: r })
}
export function* chatSaga(
  {
    roomId: r,
    ignoreBlocked: a = !1,
    filterNames: m,
    messageId: n,
    fileStatsChannel: c,
    useLabelsFeature: y,
  },
  f = {},
) {
  try {
    const g = yield o(e)
    ;(yield i(te, r, g),
      yield i(F, { roomId: r, ignoreBlocked: a, coreEventChannel: g }),
      yield i(V, r),
      yield d([ge, Ie], ue, { roomId: r }),
      yield i(Z),
      yield i(U, { roomId: r }),
      yield i(q, { roomId: r, ignoreBlocked: a }),
      yield i(Ce, { roomId: r, fileStatsChannel: c }),
      yield i(P, { roomId: r }),
      yield i(le, { roomId: r }),
      yield i(ie, { roomId: r, ignoreBlocked: a }))
    const {
      lastSeenSeq: h,
      startSeq: S,
      anchor: L,
    } = yield o(chatGetInitialData, { roomId: r, messageId: n })
    yield t(k({ ...f, roomId: r, lastSeenSeq: h, startSeq: S, anchor: L, filterNames: m }))
    const M = yield z({ roomId: r, coreEventChannel: g })
    ;(yield Ne({ roomId: r }), M && (yield Q()), yield d(j, X), yield d([J, pe], Y, r))
    const { isDesktop: C } = yield l(_)
    ;(C && (yield d(H, $)),
      yield d([O, T], N, r),
      yield s(w, function* (e) {
        const { messageId: o } = e.payload
        yield chatDeleteMessageHandler(r, o)
      }),
      yield i(x, { roomId: r }),
      yield i(B, { roomId: r, ignoreBlocked: a, fileStatsChannel: c }),
      yield i(de, { roomId: r, ignoreBlocked: a }),
      yield i(ee),
      yield d(ce, ye, { roomId: r }),
      yield d(se, ae, { roomId: r, useLabelsFeature: y }),
      yield d(me, ne, { roomId: r }),
      yield I([u().subscribeChatLength, r, { reorders: !0 }], oe),
      yield i(he, { roomId: r, autoscroll: !1 }),
      yield i(Oe),
      yield p())
  } catch (e) {
    yield M("chat", e)
  } finally {
    yield t(D())
  }
}