import { isFeatureSupported as e } from "@holepunchto/keet-core/features"
import { ERRORS as i } from "@holepunchto/keet-core/legacy-constants"
import {
all as t,
call as r,
delay as d,
fork as n,
getContext as a,
put as o,
race as y,
select as c,
take as s,
takeEvery as l,
takeLatest as m,
takeLeading as u,
} from "redux-saga/effects"
import { getCoreBackend as p } from "../../backend"
import { apiCall as f } from "../../api/core"
import { ENV_CONTEXT_KEY as g, PLATFORM_CONTEXT_KEY as I } from "../constants"
import { pairInvalidInviteError as b, taskErrorSaga as v } from "../error"
import { apiSubscribeSaga as E, apiTakeEvery as S, apiTakeMatch as h } from "../lib"
import {
IDENTITY_STATUS as D,
IDENTITY_USERNAME_ACTION_TYPE as U,
IDENTITY_USERNAME_SAVE_STATUS as L,
IDENTITY_USERNAME_STATUS as R,
IDENTITY_VERIFY_DELAY as x,
LINK_INVITATION as k,
QUERY_IDENTITY_DELAY as O,
QUERY_IDENTITY_LIMIT as A,
QUERY_IDENTITY_REQUEST_TIMEOUT as T,
QUERY_IDENTITY_SOURCE as C,
} from "./identity.constants"
import {
isValidIdentityUsername as N,
parseUserSearchItem as P,
parseUserSearchResponse as w,
} from "./identity.model"
import {
addIdentityInvitedDevice as V,
approveIdentityInvitedDevice as _,
cancelIdentityInvitationLink as F,
createIdentityInvitationLink as G,
createIdentityUsernameCmd as j,
getIdentityInvitationLink as q,
getIdentityInvitedDevices as H,
getIdentityStatus as M,
rejectIdentityInvitedDevice as $,
removeIdentityInvitedDevice as K,
setIdentity as B,
setIdentityAsked as W,
setIdentityInvitationLink as Y,
setSyncDeviceErrorMsg as X,
updateIdentityDeviceFailure as z,
updateIdentityDeviceRequest as J,
updateIdentityDeviceSuccess as Q,
updateIdentitySearchList as Z,
verifyIdentityUsernameCmd as ee,
updateIdentityVerifyStatus as ie,
resetIdentityVerifyStatus as te,
askIdentityLoad as re,
updatePeerIdentity as de,
getMyMemberId as ne,
createAndSubmitBackup as ae,
getIsIdentityConfirmed as oe,
submitBackupSuccess as ye,
queryIdentityRecords as ce,
editIdentityUsernameCmd as se,
fetchMyContactInvitationCmd as le,
getAccountRecoveryLoading as me,
setAccountRecoverySuccess as ue,
removeLinkedDevice as pe,
updateRemoveDeviceStatus as fe,
getCreateUsernameStatus as ge,
setIdentityUsernameActionType as Ie,
updateUsernameReservationInfo as be,
resetUsernameReservationInfo as ve,
identityLoggedOut as Ee,
updateUserSettingsCmd as Se,
fetchPrivateProfileStatsCmd as he,
setPrivateProfileStats as De,
getMyIdentity as Ue,
getIdentityState as Le,
} from "./identity.reducer"
import { identityUserProfileSaga as Re } from "./identity-profile.saga"
import { accountRecoverSaga as xe } from "./saga/account-recover.saga"
import { backupCreateSaga as ke } from "./saga/backup-create.saga"
import { syncDeviceSaga as Oe } from "./saga/sync-device.saga"
import { secretPhraseSaga as Ae } from "./saga/secret-phrase-saga"
import { blockMemberSaga as Te, unblockMemberSaga as Ce } from "./member/identity-member.saga"
import {
blockMemberCmd as Ne,
initBlockedMembersSubscription as Pe,
unblockMemberCmd as we,
} from "./member"
import {
NOTIFICATION_CODE as Ve,
userCustomNotification as _e,
userSuccessNotification as Fe,
} from "../notification"
import { migrateRecoveryPhraseSaga as Ge } from "./saga/migrate-recovery-phrase.saga"
import { fetchMyContactInvitationSaga as je } from "./saga/fetch-my-contact-invitation.saga"
export function* updateIdentityDeviceSaga({ payload: e }) {
try {
const { name: i, deviceId: t } = e,
r = p(),
d = i || t
;(yield f(r.updateIdentityDevice, { name: d }), yield o(Q({ name: d, deviceId: t })))
} catch (e) {
;(yield o(z({ error: e.message })), yield v("updateIdentityDeviceSaga", e))
}
}
export function* approveIdentityInvitedDeviceSaga({ payload: e }) {
try {
const i = (yield c(H)).find((i) => i.deviceId === e)
if (!i) throw new Error("wrong device to approve")
const t = p()
yield f(t.confirmPairingRequest, i, !0)
} catch (e) {
yield v("approveIdentityInvitedDeviceSaga", e)
} finally {
yield o(K(e))
}
}
export function* rejectIdentityInvitedDeviceSaga({ payload: e }) {
try {
const i = (yield c(H)).find((i) => i.deviceId === e)
if (!i) throw new Error("wrong device to reject")
const t = p()
yield f(t.confirmPairingRequest, i, !1)
} catch (e) {
yield v("rejectIdentityInvitedDeviceSaga", e)
} finally {
yield o(K(e))
}
}
export function* createIdentityInvitationLinkSaga() {
try {
if (yield c(q)) return
const e = p(),
[i, t] = yield y([s(F), f(e.createIdentityInvitation)])
if (!i && !t) throw new Error("unable to create invitation link")
if (!i) {
const {
config: { appProtocol: e },
} = yield a(I)
yield o(Y(`${e}://${k}/${t}`))
}
} catch (e) {
yield v("createIdentityInvitationLinkSaga", e)
}
}
export function* loadIdentitySync() {
try {
yield o(W())
const e = p(),
i = yield f(e.getIdentity)
yield o(B(i))
} catch (e) {
yield v("loadIdentitySync", e, { isCritical: !0 })
}
return !0
}
function* qe({ payload: e }) {
try {
if ((yield c(ge)) === L.RESERVING) return
yield o(Ie(U.UPDATE))
const i = p()
yield f(i.updateUsername, e)
} catch (e) {
e?.code
? yield o(be({ status: L.FAILED, error: e.code }))
: (yield v("editIdentityUsername", e), yield o(ve()))
}
}
function* He({ payload: e }) {
try {
yield o(Ie(U.CREATE))
const i = p()
yield f(i.registerUsername, e)
} catch (e) {
;(yield o(ve()),
e.code === Ve.OUTDATED_CLOCK
? yield o(_e({ code: Ve.OUTDATED_CLOCK }))
: yield v("createIdentityUsername", e))
}
}
function* Me({ payload: e }) {
yield d(O)
try {
const i = p(),
{ prefix: t, opts: r = {} } = e,
{ source: n = C.GLOBAL, limit: a = A } = r,
[c, s] = yield y([f(i.searchUsername, t, { limit: a }), d(T)])
if (s) return void (yield o(Z({ list: [], source: n })))
const l = w({ response: c })
yield o(Z({ list: l, source: n }))
} catch (e) {
yield v("queryIdentityRecordsSaga", e)
}
}
function* $e({ payload: e }) {
try {
if ((yield o(ie(R.CHECKING)), yield d(x), !e)) return void (yield o(te()))
if (!N(e)) return void (yield o(ie(R.INVALID_CHAR)))
const i = p(),
t = (yield f(i.checkUsername, e)) ? R.NOT_FOUND : R.EXISTS
yield o(ie(t))
} catch (e) {
yield v("verifyIdentityUsernameSaga", e)
}
}
function* Ke() {
try {
const { profileId: e } = yield c(Ue),
i = p(),
[r, d] = yield t([f(i.getRoomStats, e), f(i.getRoomInfo, e)])
yield o(
De({
stats: {
roomInfo: {
version: d?.digest?.abi.version,
experimental: d?.digest?.settings?.isExperimental,
},
...r,
},
}),
)
} catch (e) {
yield v("fetchRoomStatsSaga", e)
}
}
export function* identityErrorHandler() {
yield l(b, function* () {
yield o(X(i.INVALID_INVITATION_TYPE))
})
}
function* Be({ payload: { deviceId: e, message: i } }) {
try {
const t = p()
;(yield o(fe({ isLoading: !0 })),
yield f(t.removeIdentityDevice, e),
yield o(fe({ isLoading: !1, isSuccess: !0, isError: !1 })),
yield o(Fe({ message: i })))
} catch (e) {
yield o(fe({ isLoading: !1, isError: !0, isSuccess: !1 }))
}
}
export function identityFeatureGate(i, t, d = []) {
return function* () {
for (;;) {
const t = yield c(Le),
r = t.myIdentity?.privateProfileVersion
if (e(r, i)) break
yield s(B)
}
yield r(t, ...d)
}
}
export function* identityWorker(e) {
const { username: i } = yield a(g)
switch (e) {
case D.ANONYMOUS:
yield v("Anonymous identity is not supported")
break
case D.COMPLETE:
;(yield n(Ae),
yield n(Ge),
yield u(_, approveIdentityInvitedDeviceSaga),
yield u($, rejectIdentityInvitedDeviceSaga),
yield u(G, createIdentityInvitationLinkSaga),
yield u(J, updateIdentityDeviceSaga),
yield u(Ne, Te),
yield u(we, Ce),
yield u(le, je),
yield u(pe, Be),
yield u(he, Ke),
yield Pe(),
(i?.registerEnabled || i?.searchEnabled) && (yield m(ee, $e)),
i?.registerEnabled && (yield u(j, He)),
i?.searchEnabled && (yield m(ce, Me)),
yield n(Re),
yield S([p().subscribeIdentityPairingRequests], (e) => o(V(e))))
break
default:
yield v("identitySaga", "no identity data available")
}
}
export function* memberIdentityLoad() {
yield l(re, function* ({ payload: e }) {
const i = p(),
t = yield f(i.lookupUserByMemberId, e)
yield o(de({ memberId: e, identityRecord: P({ item: t }) }))
})
}
export function* identityOnboardingSaga() {
const { username: e } = yield a(g)
;(yield n(ke),
yield n(Oe),
yield n(xe),
yield u(J, updateIdentityDeviceSaga),
(e?.searchEnabled || e?.registerEnabled) && (yield m(ee, $e)))
}
export function* subscribeIdentityUsername() {
yield E(
[p().subscribeUsername],
function* (e) {
const { status: i, record: t, error: r } = e || {}
;(i?.isReserving && (yield o(be({ status: L.RESERVING }))),
i?.hasRecord &&
t &&
(yield o(be({ status: L.RESERVED, username: t.username, count: t.count }))),
i?.hasError && r && (yield o(be({ error: r, status: L.FAILED }))))
},
{ onError: (e) => v("subscribeUsername", e) },
)
}
export function* subscribeIdentityDataSaga(e) {
;(e.isLoggedOut && (yield o(Ee())), yield o(B(e)))
}
export function* updateUserSettings({ payload: e }) {
try {
const i = p()
yield f(i.updateUserSettings, e.key, e.value)
} catch (e) {
yield v("updateUserSettings", e)
}
}
export function* identitySaga() {
try {
const { username: e } = yield a(g),
i = yield c(M)
if (i !== D.COMPLETE) return void (yield v("identitySaga", `incorrect initial ID status=${i}`))
if (!(yield c(oe))) {
const e = yield n(identityOnboardingSaga),
i = yield c(ne),
[t, r] = yield y([
h([p().subscribeIdentity], {
matchFn: (e) => e.memberId !== i,
onError: (e) => v("Onboarding: subscribeIdentity", e),
}),
s(ae),
])
if (t) ((yield c(me)) && (yield o(ue())), yield o(B(t)))
else {
const { payload: e } = r,
i = p()
;(yield f(i.confirmRecoveryPhrase, { persist: !e }), yield o(ye()))
}
e.cancel()
}
;(yield n(identityWorker, i),
yield n(identityErrorHandler),
(e?.registerEnabled || e?.searchEnabled) && (yield n(memberIdentityLoad)),
e?.editEnabled && (yield m(se, qe)),
(e?.registerEnabled || e?.editEnabled) && (yield n(subscribeIdentityUsername)),
yield m(Se, updateUserSettings),
yield E([p().subscribeIdentity], subscribeIdentityDataSaga, {
onError: (e) => v("subscribeIdentity", e),
}))
} catch (e) {
yield v("identitySaga", e)
}
}