import e, { promises as o } from "fs"
import t from "path"
import d from "child_process"
import {
select as n,
take as i,
takeLeading as r,
takeEvery as l,
fork as s,
put as c,
all as a,
call as y,
race as f,
delay as p,
} from "redux-saga/effects"
import m from "pear-user-dirs"
import {
addFileDownloadCmd as u,
resumeFileDownload as w,
updateFileDownloadName as h,
updateFileDownloadProgress as I,
updateFileDownloadSpeed as g,
removeFileDownload as k,
getFileDownload as D,
getFileDownloadsPaused as L,
setFileDownloadError as S,
getFileDownloadsOpen as $,
toggleFileDownloads as x,
getFileEntryById as E,
addFileListDownloadCmd as N,
getFileDownloadsNextId as b,
savePendingDownloadedFiles as A,
} from ".."
import {
NOTIFICATION_CODE as P,
userCustomNotification as B,
userWarnNotification as O,
} from "../../../notification"
import { getUIAppActive as W } from "../../../ui-desktop"
import {
getPreferencesNotificationsAlerts as _,
NOTIFICATIONS_ALERT_TYPES as j,
} from "../../../preferences"
import { openFileDownloadFolder as v } from "../file.action"
const C = 2097152,
F = 50,
H = (e) => (o) => w.match(o) && o?.payload === e
export function* checkAccess(e) {
try {
return (yield y([o, o.access], e, o.constants.W_OK), !0)
} catch {
return !1
}
}
function U(o, d) {
let n = d,
i = 0
for (; e.existsSync(t.join(o, n)); ) {
i += 1
const e = d.split(".")
if (e.length > 1) {
const o = e.pop()
n = `${e.join(".")} (${i}).${o}`
} else n = `${d} (${i})`
}
return n
}
export function* desktopFileDownloadsSaga() {
const o = {},
y = (yield m()).downloads
function* w({ downloadId: d, name: r, httpLink: l, byteLength: s }) {
let m, u
try {
if (!(yield checkAccess(y)))
return void (yield a([
c(S({ downloadId: d, error: "No access" })),
c(
O({
title: "No Access",
message: "Please check the write permissions on your Downloads folder",
}),
),
]))
const w = yield fetch(l)
m = yield w.body.getReader()
const k = U(y, r)
yield c(h({ downloadId: d, fileName: k }))
const E = t.join(y, k)
;((u = e.createWriteStream(E, { highWaterMark: C })),
u.on("error", (e) => {
throw (o[d]?.cancel(), new Error("Could not write to file", { cause: e }))
}))
let N = 0,
b = 0,
A = Date.now(),
v = 0
for (;;) {
;(yield n((e) => L(e, d))) && (yield c(g({ downloadId: d, speed: 0 })), yield i(H(d)))
const { result: e, timeout: o } = yield f({ result: m.read(), timeout: p(3e3) })
if (o) {
const { speed: e } = yield n((e) => D(e, d))
e > 0 && (yield c(g({ downloadId: d, speed: 0 })))
continue
}
const { value: t, done: r } = e || {}
if (r) {
const e = yield n(W),
o = yield n(_)
;((yield n($)) || (yield c(x(!0))),
yield c(g({ downloadId: d, speed: 0 })),
yield c(
B({
appActive: e,
code: P.FILE_DOWNLOAD_FINISHED,
fileName: k,
notifyByBanner: !1,
notifyByPush: o.includes(j.PUSH),
notifyBySound: o.includes(j.SOUND),
}),
))
break
}
;((N += t.length),
(v += 1),
u.write(t) ||
(yield new Promise((e) => {
u.once("drain", e)
})),
(v % F === 0 || N >= s) && (yield c(I({ downloadId: d, byteProgress: N }))))
const l = Date.now()
if (l - A >= 1e3) {
const e = (l - A) / 1e3,
o = N - b
;((A = l), (b = N))
const t = o / e
yield c(g({ downloadId: d, speed: t }))
}
}
} catch (e) {
;(console.error(e), yield c(S({ downloadId: d, error: e.message })))
} finally {
;(u?.errored && (yield c(S({ downloadId: d, error: "Could not write to file" }))),
m?.cancel(),
u?.end(),
delete o[d])
}
}
;(yield l([u, N], function* (e) {
let t = []
const d = yield n(b)
if (u.match(e)) t = [e.payload.fileId]
else {
if (!N.match(e)) return
t = e.payload.fileIds
}
if (0 === t.length) return
let i = d
const r = []
for (const e of t) {
const o = yield n(E, e)
o && (r.push({ fileId: e, fileEntry: o, downloadId: i }), (i += 1))
}
yield c(A({ files: r, roomId: e.payload.roomId, lastDownloadId: i }))
for (const e of r)
e.downloadId in o ||
(o[e.downloadId] = yield s(w, {
downloadId: e.downloadId,
name: e.fileEntry.name,
httpLink: e.fileEntry.httpLink,
byteLength: e.fileEntry.byteLength,
}))
}),
yield r(v, function* () {
try {
const e = { darwin: "open", win32: "explorer" }[process.platform] || "xdg-open"
yield d.execSync(`${e} "${y}"`)
} catch (e) {
console.error(`Can't open ${y}`, e)
}
}),
yield r(k, function ({ payload: e }) {
;(o[e]?.cancel(), delete o[e])
}))
}