diff --git a/lib/menu.ts b/lib/menu.ts index 9027a7a..444b1cc 100644 --- a/lib/menu.ts +++ b/lib/menu.ts @@ -5,7 +5,8 @@ import { sendNotification } from '@tauri-apps/plugin-notification' import { revealItemInDir } from '@tauri-apps/plugin-opener' import { exit } from '@tauri-apps/plugin-process' import { isDirectoryEmpty } from './fs' -import { deleteRemote, mountRemote, unmountRemote } from './rclone' +import { deleteRemote, mountRemote, unmountRemote } from './rclone/api' +import { dialogGetMountPlugin, needsMountPlugin } from './rclone/mount' import { usePersistedStore, useStore } from './store' import { getLoadingTray, getMainTray, rebuildTrayMenu } from './tray' import { lockWindows, openFullWindow, openTrayWindow, openWindow, unlockWindows } from './window' @@ -87,6 +88,16 @@ export async function buildMenu() { await getLoadingTray().then((t) => t?.setVisible(true)) + const needsPlugin = await needsMountPlugin() + if (needsPlugin) { + console.log('Mount plugin not installed') + await dialogGetMountPlugin() + await getLoadingTray().then((t) => t?.setVisible(false)) + await getMainTray().then((t) => t?.setVisible(true)) + return + } + console.log('Mount plugin installed') + try { await lockWindows() diff --git a/lib/rclone/mount.ts b/lib/rclone/mount.ts new file mode 100644 index 0000000..0277fb7 --- /dev/null +++ b/lib/rclone/mount.ts @@ -0,0 +1,74 @@ +import { downloadDir } from '@tauri-apps/api/path' +import { confirm } from '@tauri-apps/plugin-dialog' +import { exists, writeFile } from '@tauri-apps/plugin-fs' +import { fetch } from '@tauri-apps/plugin-http' +import { revealItemInDir } from '@tauri-apps/plugin-opener' +import { platform } from '@tauri-apps/plugin-os' +import { exit } from '@tauri-apps/plugin-process' + +export async function needsMountPlugin() { + const currentPlatform = platform() + if (currentPlatform === 'macos') { + // check fuse-t or osxfuse + const hasFuseT = await exists('/Library/Application Support/fuse-t') + console.log('hasFuseT', hasFuseT) + const hasOsxFuse = await exists('/Library/Filesystems/macfuse.fs') + console.log('hasOsxFuse', hasOsxFuse) + return !hasFuseT && !hasOsxFuse + } + if (currentPlatform === 'windows') { + // check winfsp + const hasWinFsp = + (await exists('C:\\Program Files\\WinFsp')) || + (await exists('C:\\Program Files (x86)\\WinFsp')) + console.log('hasWinFsp', hasWinFsp) + return !hasWinFsp + } + return false +} + +export async function dialogGetMountPlugin() { + const currentPlatform = platform() + if (currentPlatform === 'macos') { + // download fuse-t or osxfuse + const wantsDownload = await confirm( + "Fuse-t is required on macOS to mount remotes. You can open rclone UI again once you're done with the installation.", + { + title: 'Mount plugin not installed', + kind: 'warning', + okLabel: 'Download', + cancelLabel: 'Cancel', + } + ) + if (wantsDownload) { + const fuseInstallerUrl = + 'https://github.com/macos-fuse-t/fuse-t/releases/download/1.0.44/fuse-t-macos-installer-1.0.44.pkg' + const localPath = `${await downloadDir()}/fuse-t-installer.pkg` + const installer = await (await fetch(fuseInstallerUrl)).arrayBuffer() + await writeFile(localPath, new Uint8Array(installer)) + await revealItemInDir(localPath) + return await exit(0) + } + } + if (currentPlatform === 'windows') { + // download winfsp + const wantsDownload = await confirm( + "WinFsp is required on Windows to mount remotes. You can open rclone UI again once you're done with the installation.", + { + title: 'Mount plugin not installed', + kind: 'warning', + okLabel: 'Download', + cancelLabel: 'Cancel', + } + ) + if (wantsDownload) { + const winFspInstallerUrl = + 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi' + const localPath = `${await downloadDir()}/winfsp-installer.msi` + const installer = await (await fetch(winFspInstallerUrl)).arrayBuffer() + await writeFile(localPath, new Uint8Array(installer)) + await revealItemInDir(localPath) + return await exit(0) + } + } +}