diff --git a/svelte/src/filesystem/Toolbar.svelte b/svelte/src/filesystem/Toolbar.svelte index 699fb2b..3402800 100644 --- a/svelte/src/filesystem/Toolbar.svelte +++ b/svelte/src/filesystem/Toolbar.svelte @@ -4,8 +4,9 @@ import { copy_text } from "util/Util.svelte"; import FileStats from "./FileStats.svelte"; import type { FSNavigator } from "./FSNavigator"; import EditWindow from "./edit_window/EditWindow.svelte"; -import { fs_share_url } from "lib/FilesystemAPI"; +import { fs_share_url, path_is_shared } from "lib/FilesystemAPI"; import ShareDialog from "./ShareDialog.svelte"; +import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks"; let dispatch = createEventDispatcher() @@ -14,10 +15,9 @@ export let details_visible = false export let edit_window: EditWindow export let edit_visible = false let share_dialog: ShareDialog - -$: share_url = fs_share_url($nav.path) let link_copied = false export const copy_link = () => { + const share_url = fs_share_url($nav.path) if (share_url === "") { edit_window.edit(nav.base, true, "share") return @@ -50,7 +50,19 @@ export const copy_link = () => { Download - {#if share_url !== ""} + {#if is_bookmark($bookmarks_store, $nav.base.id)} + bookmark_del($nav.base.id)}> + bookmark_remove + Bookmark + + {:else} + bookmark_add($nav.base)}> + bookmark_add + Bookmark + + {/if} + + {#if path_is_shared($nav.path)} content_copy Copy link @@ -58,7 +70,7 @@ export const copy_link = () => { {/if} - {#if $nav.base.id !== "me" && (navigator.share !== undefined || $nav.permissions.write === true)} + {#if navigator.share !== undefined || $nav.permissions.write === true} share_dialog.open(e, nav.path)}> share Share diff --git a/svelte/src/filesystem/edit_window/EditWindow.svelte b/svelte/src/filesystem/edit_window/EditWindow.svelte index 10206ee..380466b 100644 --- a/svelte/src/filesystem/edit_window/EditWindow.svelte +++ b/svelte/src/filesystem/edit_window/EditWindow.svelte @@ -7,6 +7,7 @@ import FileOptions from "./FileOptions.svelte"; import SharingOptions from "./SharingOptions.svelte"; import AccessControl from "./AccessControl.svelte"; import type { FSNavigator } from "filesystem/FSNavigator"; +import { loading_finish, loading_start } from "lib/Loading"; export let nav: FSNavigator let file: FSNode = {} as FSNode @@ -64,7 +65,7 @@ const save = async (keep_editing = false) => { let new_file: FSNode try { - nav.set_loading(true) + loading_start() options.branding_enabled = JSON.stringify(branding_enabled) new_file = await fs_update(file.path, options) @@ -88,7 +89,7 @@ const save = async (keep_editing = false) => { } return } finally { - nav.set_loading(false) + loading_finish() } if (open_after_edit) { diff --git a/svelte/src/filesystem/edit_window/FileOptions.svelte b/svelte/src/filesystem/edit_window/FileOptions.svelte index e0fe186..badda40 100644 --- a/svelte/src/filesystem/edit_window/FileOptions.svelte +++ b/svelte/src/filesystem/edit_window/FileOptions.svelte @@ -3,6 +3,7 @@ import Button from "layout/Button.svelte"; import { fs_delete_all, type FSNode } from "lib/FilesystemAPI"; import PathLink from "filesystem/util/PathLink.svelte"; import type { FSNavigator } from "filesystem/FSNavigator"; +import { loading_finish, loading_start } from "lib/Loading"; export let nav: FSNavigator export let file: FSNode = {} as FSNode @@ -16,14 +17,14 @@ const delete_file = async (e: MouseEvent) => { e.preventDefault() try { - nav.set_loading(true) + loading_start() await fs_delete_all(file.path) } catch (err) { console.error(err) alert(err) return } finally { - nav.set_loading(false) + loading_finish() } if (open_after_edit) { diff --git a/svelte/src/filesystem/filemanager/FileManager.svelte b/svelte/src/filesystem/filemanager/FileManager.svelte index 291c309..d14b1a3 100644 --- a/svelte/src/filesystem/filemanager/FileManager.svelte +++ b/svelte/src/filesystem/filemanager/FileManager.svelte @@ -14,6 +14,7 @@ import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte"; import EditWindow from "filesystem/edit_window/EditWindow.svelte"; import { FileAction, type FileEvent } from "./FileManagerLib"; import FileMenu from "./FileMenu.svelte"; +import { loading_finish, loading_start } from "lib/Loading"; export let nav: FSNavigator export let upload_widget: FsUploadWidget @@ -117,9 +118,9 @@ const delete_selected = async () => { return } - nav.set_loading(true) - try { + loading_start() + // Save all promises with deletion requests in an array let promises = [] nav.children.forEach(child => { @@ -136,7 +137,7 @@ const delete_selected = async () => { alert("Delete failed: " + err.message + " ("+err.value+")") } finally { viewing_mode() - nav.reload() + loading_finish() } } @@ -264,11 +265,10 @@ const move_start = () => { } const move_here = async () => { - nav.set_loading(true) - - let target_dir = nav.base.path + "/" - + const target_dir = nav.base.path + "/" try { + loading_start() + let promises = [] moving_items.forEach(item => { console.log("moving", item.path, "to", target_dir + item.name) @@ -283,6 +283,7 @@ const move_here = async () => { } finally { viewing_mode() nav.reload() + loading_finish() } } @@ -424,6 +425,7 @@ onMount(() => { padding: 0; overflow: auto; display: block; + height: 100%; /* Used for drop target */ } .width_container { position: sticky; diff --git a/svelte/src/filesystem/filemanager/FileMenu.svelte b/svelte/src/filesystem/filemanager/FileMenu.svelte index 1117fbd..74a9b40 100644 --- a/svelte/src/filesystem/filemanager/FileMenu.svelte +++ b/svelte/src/filesystem/filemanager/FileMenu.svelte @@ -3,49 +3,31 @@ import EditWindow from "filesystem/edit_window/EditWindow.svelte"; import type { FSNavigator } from "filesystem/FSNavigator"; import Button from "layout/Button.svelte"; import Dialog from "layout/Dialog.svelte"; -import { bookmark_add, bookmark_del, bookmarks_store } from "lib/Bookmarks"; +import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks"; import { fs_download, type FSNode } from "lib/FilesystemAPI"; import { tick } from "svelte"; export let nav: FSNavigator export let edit_window: EditWindow let dialog: Dialog -let node: FSNode -let is_bookmark: boolean = false +let node: FSNode = null export const open = async (n: FSNode, target: EventTarget) => { node = n - is_bookmark = false - for (const bm of $bookmarks_store) { - console.log(bm) - if (bm.id === n.id) { - is_bookmark = true - break - } - } - // Wait for the view to update, so the dialog gets the proper measurements await tick() dialog.open((target as Element).closest("button").getBoundingClientRect()) } - -const bookmark = () => { - bookmark_add({ - id: node.id, - icon: "folder_shared", - label: node.name, - }) -} {dialog.close(); fs_download(node)}} icon="save" label="Download"/> - {#if is_bookmark} + {#if node !== null && is_bookmark($bookmarks_store, node.id)} {dialog.close(); bookmark_del(node.id)}} icon="bookmark_remove" label="Remove bookmark"/> {:else} - {dialog.close(); bookmark()}} icon="bookmark_add" label="Add bookmark"/> + {dialog.close(); bookmark_add(node)}} icon="bookmark_add" label="Add bookmark"/> {/if} {#if $nav.permissions.write} {dialog.close(); edit_window.edit(node, false, "file")}} icon="edit" label="Edit"/> diff --git a/svelte/src/lib/Bookmarks.ts b/svelte/src/lib/Bookmarks.ts index e72ec7d..2aafd9a 100644 --- a/svelte/src/lib/Bookmarks.ts +++ b/svelte/src/lib/Bookmarks.ts @@ -1,11 +1,13 @@ import { writable } from "svelte/store" -import { fs_check_response, fs_path_url } from "./FilesystemAPI" +import { fs_check_response, fs_path_url, type FSNode } from "./FilesystemAPI" import { loading_finish, loading_start } from "lib/Loading" +import { get_user } from "./PixeldrainAPI" const bookmarks_file = "/me/.fnx/bookmarks.json" export type Bookmark = { id: string, + path: string, icon: string, label: string, } @@ -22,52 +24,88 @@ export let bookmarks_store = writable( ) export const bookmarks_get = async (): Promise => { - let bookmarks: Bookmark[] = [] try { - bookmarks = await fs_check_response( + loading_start() + const user = await get_user() + if ( + user.username === undefined || + user.username === "" || + user.subscription.filesystem_access === undefined || + user.subscription.filesystem_access === false + ) { + return [] + } + + const bookmarks = await fs_check_response( await fetch(fs_path_url(bookmarks_file), { cache: "no-store" }) - ) + ) as Bookmark[] + + // Sanity checks + for (const bookmark of bookmarks) { + if (typeof bookmark.id !== "string") { bookmark.id = "" } + if (typeof bookmark.path !== "string") { bookmark.path = "" } + if (typeof bookmark.icon !== "string") { bookmark.icon = "" } + if (typeof bookmark.label !== "string") { bookmark.label = "" } + } + + console.debug("Fetched", bookmarks.length, "bookmarks:", bookmarks) + bookmarks_store.set(bookmarks) + return bookmarks } catch (err) { // If the bookmarks were not found when we return an empty bookmarks // list - if (err.value !== "path_not_found") { + if ( + err.value !== "path_not_found" && + err.value !== "forbidden" && + err.value !== "authentication_required" + ) { throw err + } else { + return [] } + } finally { + loading_finish() } - - console.debug("Fetched", bookmarks.length, "bookmarks:", bookmarks) - bookmarks_store.set(bookmarks) - return bookmarks } export const bookmarks_save = async (bookmarks: Bookmark[]) => { - await fs_check_response( - await fetch( - fs_path_url(bookmarks_file) + "?make_parents=true", - { method: "PUT", body: JSON.stringify(bookmarks) }, + try { + loading_start() + + await fs_check_response( + await fetch( + fs_path_url(bookmarks_file) + "?make_parents=true", + { method: "PUT", body: JSON.stringify(bookmarks) }, + ) ) - ) - console.debug("Saved", bookmarks.length, "bookmarks:", bookmarks) - bookmarks_store.set(bookmarks) + console.debug("Saved", bookmarks.length, "bookmarks:", bookmarks) + bookmarks_store.set(bookmarks) + } finally { + loading_finish() + } } -export const bookmark_add = async (bm: Bookmark): Promise => { - let bookmarks: Bookmark[] = [] +export const bookmark_add = async (node: FSNode): Promise => { try { loading_start() // Get bookmarks - bookmarks = await bookmarks_get() + const bookmarks = await bookmarks_get() // Add new bookmark - bookmarks.push(bm) + bookmarks.push({ + id: node.id, + path: node.path, + icon: "bookmark", + label: node.name, + }) // Save new bookmarks await bookmarks_save(bookmarks) + return bookmarks } finally { loading_finish() } - return bookmarks } export const bookmark_del = async (id: string): Promise => { @@ -94,3 +132,12 @@ export const bookmark_del = async (id: string): Promise => { } return bookmarks } + +export const is_bookmark = (bookmarks: Bookmark[], id: string): boolean => { + for (const bm of bookmarks) { + if (bm.id === id) { + return true + } + } + return false +} diff --git a/svelte/src/lib/DropTarget.ts b/svelte/src/lib/DropTarget.ts index 2477b64..253f082 100644 --- a/svelte/src/lib/DropTarget.ts +++ b/svelte/src/lib/DropTarget.ts @@ -55,6 +55,12 @@ export const drop_target = ( const entry: FileSystemEntry | null = e.dataTransfer.items[i].webkitGetAsEntry(); if (entry !== null) { read_dir_recursive(entry); + continue + } + const file: File | null = e.dataTransfer.items[i].getAsFile(); + if (file !== null) { + args.upload([file]); + continue } } } else if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) { diff --git a/svelte/src/lib/FilesystemAPI.ts b/svelte/src/lib/FilesystemAPI.ts index a64d61b..867b8b4 100644 --- a/svelte/src/lib/FilesystemAPI.ts +++ b/svelte/src/lib/FilesystemAPI.ts @@ -55,6 +55,14 @@ export const node_is_shared = (node: FSNode): boolean => { } return false } +export const path_is_shared = (path: FSNode[]): boolean => { + for (let i = 0; i < path.length; i++) { + if (node_is_shared(path[i])) { + return true + } + } + return false +} export type FSNodeProperties = { branding_enabled?: string, @@ -364,7 +372,7 @@ export const fs_share_path = (path: FSNode[]): string => { // Find the last node in the path that has a public ID for (let i = path.length - 1; i >= 0; i--) { - if (path[i].id !== undefined && path[i].id !== "me") { + if (node_is_shared(path[i])) { bucket_idx = i break } diff --git a/svelte/src/lib/PixeldrainAPI.ts b/svelte/src/lib/PixeldrainAPI.ts index ee4a7f8..bf34adf 100644 --- a/svelte/src/lib/PixeldrainAPI.ts +++ b/svelte/src/lib/PixeldrainAPI.ts @@ -132,6 +132,16 @@ export const put_user = async (data: Object) => { } } +export const logout_user = async (redirect_path: string) => { + check_response(await fetch( + get_endpoint() + "/user/session", + { method: "DELETE" }, + )) + + document.cookie = "pd_auth_key=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + window.location.pathname = redirect_path +} + export type VATRate = { name: string, vat: number, diff --git a/svelte/src/user_home/dashboard/CardAccount.svelte b/svelte/src/user_home/dashboard/CardAccount.svelte index e0125ed..ddd0425 100644 --- a/svelte/src/user_home/dashboard/CardAccount.svelte +++ b/svelte/src/user_home/dashboard/CardAccount.svelte @@ -1,3 +1,7 @@ + Username: {window.user.username} {#if window.user.email === ""} @@ -26,10 +30,10 @@ Filesystem {/if} - + logout_user("/")}> logout Log out - + diff --git a/svelte/src/user_home/dashboard/Dashboard.svelte b/svelte/src/user_home/dashboard/Dashboard.svelte index 419ec17..605cf52 100644 --- a/svelte/src/user_home/dashboard/Dashboard.svelte +++ b/svelte/src/user_home/dashboard/Dashboard.svelte @@ -7,7 +7,6 @@ import CardSubscription from "./CardSubscription.svelte"; import CardUsage from "./CardUsage.svelte"; import CardActivity from "./CardActivity.svelte"; import CardPrepaidTransactions from "./CardPrepaidTransactions.svelte"; -import CardFsHome from "./CardFSHome.svelte"; import AddressReputation from "home_page/AddressReputation.svelte"; import { flip } from "svelte/animate"; @@ -60,51 +59,37 @@ const swap_card = (idx1, idx2) => { } onMount(() => { - cards = [] - if (window.user.subscription.filesystem_access === true) { - cards.push({ - id: "filesystem_home", - elem: CardFsHome, - title: "Filesystem home", - link: "/d/me", - }) - } - cards.push({ - id: "account", - elem: CardAccount, - title: "Account", - link: "/user/settings", - }) - cards.push({ - id: "subscription", - elem: CardSubscription, - title: "Subscription", - link: "/user/subscription", - }) - if (window.user.subscription.type === "prepaid") { - cards.push({ + cards = [ + { + id: "account", + elem: CardAccount, + title: "Account", + link: "/user/settings", + },{ + id: "subscription", + elem: CardSubscription, + title: "Subscription", + link: "/user/subscription", + }, { id: "prepaid_transactions", elem: CardPrepaidTransactions, title: "Prepaid transactions", link: "/user/prepaid/transactions", - }) - } - cards.push({ - id: "usage", - elem: CardUsage, - title: "Usage", - }) - cards.push({ - id: "statistics", - elem: CardStatistics, - title: "Statistics", - }) - cards.push({ - id: "activity", - elem: CardActivity, - title: "Activity", - link: "/user/activity", - }) + }, { + id: "usage", + elem: CardUsage, + title: "Usage", + }, { + id: "statistics", + elem: CardStatistics, + title: "Statistics", + }, { + id: "activity", + elem: CardActivity, + title: "Activity", + link: "/user/activity", + } + ] // Apply the view settings from localstorage try { diff --git a/svelte/src/util/upload_widget/DropUpload.svelte b/svelte/src/util/upload_widget/DropUpload.svelte deleted file mode 100644 index b4efaba..0000000 --- a/svelte/src/util/upload_widget/DropUpload.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - - { dragging = true }} - on:dragenter|preventDefault|stopPropagation={() => { dragging = true }} - on:dragleave|preventDefault|stopPropagation={() => { dragging = false }} - on:drop={drop} - on:paste={paste} -/> - -{#if dragging} - - Drop files here to upload them - -{/if} - - diff --git a/svelte/src/util/upload_widget/UploadFunc.js b/svelte/src/util/upload_widget/UploadFunc.js deleted file mode 100644 index ddd3b60..0000000 --- a/svelte/src/util/upload_widget/UploadFunc.js +++ /dev/null @@ -1,68 +0,0 @@ -// Uploads a file to the logged in user's pixeldrain account. If no user is -// logged in the file is uploaded anonymously. -// -// on_progress reports progress on the file upload, parameter 1 is the uploaded -// file size and parameter 2 is the total file size -// -// on_success is called when the upload is done, the only parameter is the file -// ID -// -// on_error is called when the upload has failed. The parameters are the error -// code and an error message -export const upload_file = (file, name, on_progress, on_success, on_error) => { - // Check the file size limit. For free accounts it's 20 GB - if (window.user.subscription.file_size_limit === 0) { - window.user.subscription.file_size_limit = 20e9 - } - - if (file.size > window.user.subscription.file_size_limit) { - on_failure( - "file_too_large", - "This file is too large. Check out the Pro subscription to increase the file size limit" - ) - return - } - - let xhr = new XMLHttpRequest(); - xhr.open("PUT", window.api_endpoint + "/file/" + encodeURIComponent(name), true); - xhr.timeout = 86400000; // 24 hours, to account for slow connections - - xhr.upload.addEventListener("progress", evt => { - if (on_progress && evt.lengthComputable) { - on_progress(evt.loaded, evt.total) - } - }); - - xhr.onreadystatechange = () => { - // readystate 4 means the upload is done - if (xhr.readyState !== 4) { - return - } - - if (xhr.status >= 100 && xhr.status < 400) { - // Request is a success - on_success(JSON.parse(xhr.response).id) - } else if (xhr.status >= 400) { - // Request failed - console.log("Upload error. status: " + xhr.status + " response: " + xhr.response); - - let resp; - if (xhr.status === 429) { - resp = { - value: "too_many_requests", - message: "Too many requests. Please wait a few seconds", - } - } else { - resp = JSON.parse(xhr.response) - } - - on_error(resp.value, resp.message) - } else if (xhr.status === 0) { - on_error("request_failed", "Your request did not arrive, check your network connection") - } else { - on_error(xhr.responseText, xhr.responseText) - } - }; - - xhr.send(file); -} diff --git a/svelte/src/util/upload_widget/UploadProgress.svelte b/svelte/src/util/upload_widget/UploadProgress.svelte deleted file mode 100644 index 07bb51b..0000000 --- a/svelte/src/util/upload_widget/UploadProgress.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - - - {job.name} - {#if error_code !== ""} - {error_message} - {error_code} - {/if} - - - - diff --git a/svelte/src/util/upload_widget/UploadWidget.svelte b/svelte/src/util/upload_widget/UploadWidget.svelte deleted file mode 100644 index aebc37d..0000000 --- a/svelte/src/util/upload_widget/UploadWidget.svelte +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - -{#if visible} - - - {#if state === "idle"} - Waiting for files - {:else if state === "uploading"} - Uploading files... - {:else if state === "finished"} - Done - {/if} - - - {#each upload_queue as job} - {#if job.status !== "finished"} - - {/if} - {/each} - - -{/if} - -{#if drop_upload} - upload_files(e.detail)}/> -{/if} - - diff --git a/svelte/src/wrap/Bookmarks.svelte b/svelte/src/wrap/Bookmarks.svelte index ce5871f..a1ad36e 100644 --- a/svelte/src/wrap/Bookmarks.svelte +++ b/svelte/src/wrap/Bookmarks.svelte @@ -1,11 +1,12 @@ {#each $bookmarks_store as bookmark} - + {bookmark.icon} {bookmark.label} diff --git a/svelte/src/wrap/MainMenu.svelte b/svelte/src/wrap/MainMenu.svelte index cf65c71..fd3004d 100644 --- a/svelte/src/wrap/MainMenu.svelte +++ b/svelte/src/wrap/MainMenu.svelte @@ -10,9 +10,14 @@ import { fs_get_node } from "lib/FilesystemAPI"; import { css_from_path } from "filesystem/edit_window/Branding"; import { loading_run, loading_store } from "lib/Loading"; import Spinner from "util/Spinner.svelte"; +import { get_user, logout_user } from "lib/PixeldrainAPI"; onMount(async () => { await loading_run(async () => { + const user = await get_user() + if (user.username === undefined || user.username === "") { + return + } const root = await fs_get_node("/me") document.documentElement.style = css_from_path(root.path) }) @@ -21,10 +26,20 @@ onMount(async () => { + + home + Home + + {#if $user.username !== undefined && $user.username !== ""} + + {$user.username} + + + Subscription {$user.subscription.name} @@ -39,14 +54,28 @@ onMount(async () => { Transfer used {formatDataVolume($user.monthly_transfer_used, 3)} - - {/if} - - home - Home - - {#if !$user.username} + + + + folder + Filesystem + + + dashboard + Dashboard + + {#if $user.is_admin} + + admin_panel_settings + Admin Panel + + {/if} + logout_user("/")}> + logout + Log out + + {:else} login Login @@ -55,22 +84,10 @@ onMount(async () => { how_to_reg Register - {:else} - - dashboard - Dashboard - - - folder - Filesystem - - {#if $user.is_admin} - - admin_panel_settings - Admin Panel - - {/if} {/if} + + + speed Speedtest @@ -80,7 +97,8 @@ onMount(async () => { Themes - + +