Add path to bookmarks, add logout button
This commit is contained in:
@@ -4,8 +4,9 @@ import { copy_text } from "util/Util.svelte";
|
|||||||
import FileStats from "./FileStats.svelte";
|
import FileStats from "./FileStats.svelte";
|
||||||
import type { FSNavigator } from "./FSNavigator";
|
import type { FSNavigator } from "./FSNavigator";
|
||||||
import EditWindow from "./edit_window/EditWindow.svelte";
|
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 ShareDialog from "./ShareDialog.svelte";
|
||||||
|
import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks";
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@@ -14,10 +15,9 @@ export let details_visible = false
|
|||||||
export let edit_window: EditWindow
|
export let edit_window: EditWindow
|
||||||
export let edit_visible = false
|
export let edit_visible = false
|
||||||
let share_dialog: ShareDialog
|
let share_dialog: ShareDialog
|
||||||
|
|
||||||
$: share_url = fs_share_url($nav.path)
|
|
||||||
let link_copied = false
|
let link_copied = false
|
||||||
export const copy_link = () => {
|
export const copy_link = () => {
|
||||||
|
const share_url = fs_share_url($nav.path)
|
||||||
if (share_url === "") {
|
if (share_url === "") {
|
||||||
edit_window.edit(nav.base, true, "share")
|
edit_window.edit(nav.base, true, "share")
|
||||||
return
|
return
|
||||||
@@ -50,7 +50,19 @@ export const copy_link = () => {
|
|||||||
<span>Download</span>
|
<span>Download</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if share_url !== ""}
|
{#if is_bookmark($bookmarks_store, $nav.base.id)}
|
||||||
|
<button on:click={() => bookmark_del($nav.base.id)}>
|
||||||
|
<i class="icon">bookmark_remove</i>
|
||||||
|
<span>Bookmark</span>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button on:click={() => bookmark_add($nav.base)}>
|
||||||
|
<i class="icon">bookmark_add</i>
|
||||||
|
<span>Bookmark</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if path_is_shared($nav.path)}
|
||||||
<button on:click={copy_link} class:button_highlight={link_copied}>
|
<button on:click={copy_link} class:button_highlight={link_copied}>
|
||||||
<i class="icon">content_copy</i>
|
<i class="icon">content_copy</i>
|
||||||
<span><u>C</u>opy link</span>
|
<span><u>C</u>opy link</span>
|
||||||
@@ -58,7 +70,7 @@ export const copy_link = () => {
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Share button is enabled when: The browser has a sharing API, or the user can edit the file (to enable sharing)-->
|
<!-- Share button is enabled when: The browser has a sharing API, or the user can edit the file (to enable sharing)-->
|
||||||
{#if $nav.base.id !== "me" && (navigator.share !== undefined || $nav.permissions.write === true)}
|
{#if navigator.share !== undefined || $nav.permissions.write === true}
|
||||||
<button on:click={(e) => share_dialog.open(e, nav.path)}>
|
<button on:click={(e) => share_dialog.open(e, nav.path)}>
|
||||||
<i class="icon">share</i>
|
<i class="icon">share</i>
|
||||||
<span>Share</span>
|
<span>Share</span>
|
||||||
|
@@ -7,6 +7,7 @@ import FileOptions from "./FileOptions.svelte";
|
|||||||
import SharingOptions from "./SharingOptions.svelte";
|
import SharingOptions from "./SharingOptions.svelte";
|
||||||
import AccessControl from "./AccessControl.svelte";
|
import AccessControl from "./AccessControl.svelte";
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
|
import { loading_finish, loading_start } from "lib/Loading";
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
let file: FSNode = {} as FSNode
|
let file: FSNode = {} as FSNode
|
||||||
@@ -64,7 +65,7 @@ const save = async (keep_editing = false) => {
|
|||||||
|
|
||||||
let new_file: FSNode
|
let new_file: FSNode
|
||||||
try {
|
try {
|
||||||
nav.set_loading(true)
|
loading_start()
|
||||||
options.branding_enabled = JSON.stringify(branding_enabled)
|
options.branding_enabled = JSON.stringify(branding_enabled)
|
||||||
|
|
||||||
new_file = await fs_update(file.path, options)
|
new_file = await fs_update(file.path, options)
|
||||||
@@ -88,7 +89,7 @@ const save = async (keep_editing = false) => {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} finally {
|
} finally {
|
||||||
nav.set_loading(false)
|
loading_finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_after_edit) {
|
if (open_after_edit) {
|
||||||
|
@@ -3,6 +3,7 @@ import Button from "layout/Button.svelte";
|
|||||||
import { fs_delete_all, type FSNode } from "lib/FilesystemAPI";
|
import { fs_delete_all, type FSNode } from "lib/FilesystemAPI";
|
||||||
import PathLink from "filesystem/util/PathLink.svelte";
|
import PathLink from "filesystem/util/PathLink.svelte";
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
|
import { loading_finish, loading_start } from "lib/Loading";
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
export let file: FSNode = {} as FSNode
|
export let file: FSNode = {} as FSNode
|
||||||
@@ -16,14 +17,14 @@ const delete_file = async (e: MouseEvent) => {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
nav.set_loading(true)
|
loading_start()
|
||||||
await fs_delete_all(file.path)
|
await fs_delete_all(file.path)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
alert(err)
|
alert(err)
|
||||||
return
|
return
|
||||||
} finally {
|
} finally {
|
||||||
nav.set_loading(false)
|
loading_finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_after_edit) {
|
if (open_after_edit) {
|
||||||
|
@@ -14,6 +14,7 @@ import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
|
|||||||
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
|
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
|
||||||
import { FileAction, type FileEvent } from "./FileManagerLib";
|
import { FileAction, type FileEvent } from "./FileManagerLib";
|
||||||
import FileMenu from "./FileMenu.svelte";
|
import FileMenu from "./FileMenu.svelte";
|
||||||
|
import { loading_finish, loading_start } from "lib/Loading";
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
export let upload_widget: FsUploadWidget
|
export let upload_widget: FsUploadWidget
|
||||||
@@ -117,9 +118,9 @@ const delete_selected = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.set_loading(true)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
loading_start()
|
||||||
|
|
||||||
// Save all promises with deletion requests in an array
|
// Save all promises with deletion requests in an array
|
||||||
let promises = []
|
let promises = []
|
||||||
nav.children.forEach(child => {
|
nav.children.forEach(child => {
|
||||||
@@ -136,7 +137,7 @@ const delete_selected = async () => {
|
|||||||
alert("Delete failed: " + err.message + " ("+err.value+")")
|
alert("Delete failed: " + err.message + " ("+err.value+")")
|
||||||
} finally {
|
} finally {
|
||||||
viewing_mode()
|
viewing_mode()
|
||||||
nav.reload()
|
loading_finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,11 +265,10 @@ const move_start = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const move_here = async () => {
|
const move_here = async () => {
|
||||||
nav.set_loading(true)
|
const target_dir = nav.base.path + "/"
|
||||||
|
|
||||||
let target_dir = nav.base.path + "/"
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
loading_start()
|
||||||
|
|
||||||
let promises = []
|
let promises = []
|
||||||
moving_items.forEach(item => {
|
moving_items.forEach(item => {
|
||||||
console.log("moving", item.path, "to", target_dir + item.name)
|
console.log("moving", item.path, "to", target_dir + item.name)
|
||||||
@@ -283,6 +283,7 @@ const move_here = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
viewing_mode()
|
viewing_mode()
|
||||||
nav.reload()
|
nav.reload()
|
||||||
|
loading_finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +425,7 @@ onMount(() => {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: block;
|
display: block;
|
||||||
|
height: 100%; /* Used for drop target */
|
||||||
}
|
}
|
||||||
.width_container {
|
.width_container {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@@ -3,49 +3,31 @@ import EditWindow from "filesystem/edit_window/EditWindow.svelte";
|
|||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
import Button from "layout/Button.svelte";
|
import Button from "layout/Button.svelte";
|
||||||
import Dialog from "layout/Dialog.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 { fs_download, type FSNode } from "lib/FilesystemAPI";
|
||||||
import { tick } from "svelte";
|
import { tick } from "svelte";
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
export let edit_window: EditWindow
|
export let edit_window: EditWindow
|
||||||
let dialog: Dialog
|
let dialog: Dialog
|
||||||
let node: FSNode
|
let node: FSNode = null
|
||||||
let is_bookmark: boolean = false
|
|
||||||
|
|
||||||
export const open = async (n: FSNode, target: EventTarget) => {
|
export const open = async (n: FSNode, target: EventTarget) => {
|
||||||
node = n
|
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
|
// Wait for the view to update, so the dialog gets the proper measurements
|
||||||
await tick()
|
await tick()
|
||||||
dialog.open((target as Element).closest("button").getBoundingClientRect())
|
dialog.open((target as Element).closest("button").getBoundingClientRect())
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookmark = () => {
|
|
||||||
bookmark_add({
|
|
||||||
id: node.id,
|
|
||||||
icon: "folder_shared",
|
|
||||||
label: node.name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog bind:this={dialog}>
|
<Dialog bind:this={dialog}>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<Button click={() => {dialog.close(); fs_download(node)}} icon="save" label="Download"/>
|
<Button click={() => {dialog.close(); fs_download(node)}} icon="save" label="Download"/>
|
||||||
{#if is_bookmark}
|
{#if node !== null && is_bookmark($bookmarks_store, node.id)}
|
||||||
<Button click={() => {dialog.close(); bookmark_del(node.id)}} icon="bookmark_remove" label="Remove bookmark"/>
|
<Button click={() => {dialog.close(); bookmark_del(node.id)}} icon="bookmark_remove" label="Remove bookmark"/>
|
||||||
{:else}
|
{:else}
|
||||||
<Button click={() => {dialog.close(); bookmark()}} icon="bookmark_add" label="Add bookmark"/>
|
<Button click={() => {dialog.close(); bookmark_add(node)}} icon="bookmark_add" label="Add bookmark"/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $nav.permissions.write}
|
{#if $nav.permissions.write}
|
||||||
<Button click={() => {dialog.close(); edit_window.edit(node, false, "file")}} icon="edit" label="Edit"/>
|
<Button click={() => {dialog.close(); edit_window.edit(node, false, "file")}} icon="edit" label="Edit"/>
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
import { writable } from "svelte/store"
|
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 { loading_finish, loading_start } from "lib/Loading"
|
||||||
|
import { get_user } from "./PixeldrainAPI"
|
||||||
|
|
||||||
const bookmarks_file = "/me/.fnx/bookmarks.json"
|
const bookmarks_file = "/me/.fnx/bookmarks.json"
|
||||||
|
|
||||||
export type Bookmark = {
|
export type Bookmark = {
|
||||||
id: string,
|
id: string,
|
||||||
|
path: string,
|
||||||
icon: string,
|
icon: string,
|
||||||
label: string,
|
label: string,
|
||||||
}
|
}
|
||||||
@@ -22,52 +24,88 @@ export let bookmarks_store = writable<Bookmark[]>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export const bookmarks_get = async (): Promise<Bookmark[]> => {
|
export const bookmarks_get = async (): Promise<Bookmark[]> => {
|
||||||
let bookmarks: Bookmark[] = []
|
|
||||||
try {
|
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" })
|
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) {
|
} catch (err) {
|
||||||
// If the bookmarks were not found when we return an empty bookmarks
|
// If the bookmarks were not found when we return an empty bookmarks
|
||||||
// list
|
// list
|
||||||
if (err.value !== "path_not_found") {
|
if (
|
||||||
|
err.value !== "path_not_found" &&
|
||||||
|
err.value !== "forbidden" &&
|
||||||
|
err.value !== "authentication_required"
|
||||||
|
) {
|
||||||
throw err
|
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[]) => {
|
export const bookmarks_save = async (bookmarks: Bookmark[]) => {
|
||||||
await fs_check_response(
|
try {
|
||||||
await fetch(
|
loading_start()
|
||||||
fs_path_url(bookmarks_file) + "?make_parents=true",
|
|
||||||
{ method: "PUT", body: JSON.stringify(bookmarks) },
|
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)
|
||||||
console.debug("Saved", bookmarks.length, "bookmarks:", bookmarks)
|
bookmarks_store.set(bookmarks)
|
||||||
bookmarks_store.set(bookmarks)
|
} finally {
|
||||||
|
loading_finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bookmark_add = async (bm: Bookmark): Promise<Bookmark[]> => {
|
export const bookmark_add = async (node: FSNode): Promise<Bookmark[]> => {
|
||||||
let bookmarks: Bookmark[] = []
|
|
||||||
try {
|
try {
|
||||||
loading_start()
|
loading_start()
|
||||||
|
|
||||||
// Get bookmarks
|
// Get bookmarks
|
||||||
bookmarks = await bookmarks_get()
|
const bookmarks = await bookmarks_get()
|
||||||
|
|
||||||
// Add new bookmark
|
// Add new bookmark
|
||||||
bookmarks.push(bm)
|
bookmarks.push({
|
||||||
|
id: node.id,
|
||||||
|
path: node.path,
|
||||||
|
icon: "bookmark",
|
||||||
|
label: node.name,
|
||||||
|
})
|
||||||
|
|
||||||
// Save new bookmarks
|
// Save new bookmarks
|
||||||
await bookmarks_save(bookmarks)
|
await bookmarks_save(bookmarks)
|
||||||
|
return bookmarks
|
||||||
} finally {
|
} finally {
|
||||||
loading_finish()
|
loading_finish()
|
||||||
}
|
}
|
||||||
return bookmarks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bookmark_del = async (id: string): Promise<Bookmark[]> => {
|
export const bookmark_del = async (id: string): Promise<Bookmark[]> => {
|
||||||
@@ -94,3 +132,12 @@ export const bookmark_del = async (id: string): Promise<Bookmark[]> => {
|
|||||||
}
|
}
|
||||||
return bookmarks
|
return bookmarks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const is_bookmark = (bookmarks: Bookmark[], id: string): boolean => {
|
||||||
|
for (const bm of bookmarks) {
|
||||||
|
if (bm.id === id) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -55,6 +55,12 @@ export const drop_target = (
|
|||||||
const entry: FileSystemEntry | null = e.dataTransfer.items[i].webkitGetAsEntry();
|
const entry: FileSystemEntry | null = e.dataTransfer.items[i].webkitGetAsEntry();
|
||||||
if (entry !== null) {
|
if (entry !== null) {
|
||||||
read_dir_recursive(entry);
|
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) {
|
} else if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||||
|
@@ -55,6 +55,14 @@ export const node_is_shared = (node: FSNode): boolean => {
|
|||||||
}
|
}
|
||||||
return false
|
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 = {
|
export type FSNodeProperties = {
|
||||||
branding_enabled?: string,
|
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
|
// Find the last node in the path that has a public ID
|
||||||
for (let i = path.length - 1; i >= 0; i--) {
|
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
|
bucket_idx = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@@ -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 = {
|
export type VATRate = {
|
||||||
name: string,
|
name: string,
|
||||||
vat: number,
|
vat: number,
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Button from "layout/Button.svelte";
|
||||||
|
import { logout_user } from "lib/PixeldrainAPI";
|
||||||
|
</script>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Username: {window.user.username}</li>
|
<li>Username: {window.user.username}</li>
|
||||||
{#if window.user.email === ""}
|
{#if window.user.email === ""}
|
||||||
@@ -26,10 +30,10 @@
|
|||||||
Filesystem
|
Filesystem
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
<a href="/logout" class="button">
|
<button on:click={()=> logout_user("/")}>
|
||||||
<i class="icon">logout</i>
|
<i class="icon">logout</i>
|
||||||
Log out
|
Log out
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -37,7 +41,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.button_row > a {
|
.button_row > * {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
|
@@ -1,150 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { FSNavigator } from "filesystem/FSNavigator.ts"
|
|
||||||
import { fs_encode_path, fs_node_icon, node_is_shared } from "lib/FilesystemAPI";
|
|
||||||
import Button from "layout/Button.svelte";
|
|
||||||
import CreateDirectory from "filesystem/filemanager/CreateDirectory.svelte";
|
|
||||||
import FSUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
|
|
||||||
import { drop_target } from "lib/DropTarget.ts"
|
|
||||||
|
|
||||||
const nav = new FSNavigator(false)
|
|
||||||
let upload_widget
|
|
||||||
var show_hidden = false
|
|
||||||
var creating_dir = false
|
|
||||||
|
|
||||||
onMount(() => nav.navigate("/me", false))
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="wrapper" use:drop_target={{upload: (files) => upload_widget.upload_files(files)}}>
|
|
||||||
<div class="toolbar">
|
|
||||||
{#if $nav.permissions.write}
|
|
||||||
<Button
|
|
||||||
click={() => upload_widget.pick_files()}
|
|
||||||
icon="cloud_upload"
|
|
||||||
title="Upload files to this directory"
|
|
||||||
label="Upload files"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
click={() => {creating_dir = !creating_dir}}
|
|
||||||
highlight={creating_dir}
|
|
||||||
icon="create_new_folder"
|
|
||||||
title="Create folder"
|
|
||||||
label="Create folder"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="toolbar_spacer"></div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
click={() => {show_hidden = !show_hidden}}
|
|
||||||
highlight={show_hidden}
|
|
||||||
icon={show_hidden ? "visibility_off" : "visibility"}
|
|
||||||
title="Show hidden files and directories"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
click={() => nav.reload()}
|
|
||||||
icon="refresh"
|
|
||||||
title="Refresh directory listing"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if creating_dir}
|
|
||||||
<CreateDirectory nav={nav} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="directory">
|
|
||||||
{#each $nav.children as child (child.path)}
|
|
||||||
<a
|
|
||||||
href={"/d"+fs_encode_path(child.path)}
|
|
||||||
class="node"
|
|
||||||
class:node_selected={child.fm_selected}
|
|
||||||
class:hidden={child.name.startsWith(".") && show_hidden === false}
|
|
||||||
>
|
|
||||||
<img src={fs_node_icon(child, 64, 64)} class="node_icon" alt="icon"/>
|
|
||||||
|
|
||||||
<div class="node_name">
|
|
||||||
{child.name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if node_is_shared(child)}
|
|
||||||
<a href="/d/{child.id}" class="button action_button">
|
|
||||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FSUploadWidget nav={nav} bind:this={upload_widget} />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wrapper {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.toolbar > * { flex: 0 0 auto; }
|
|
||||||
.toolbar_spacer {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.directory {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 6px;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
.node {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
flex-direction: row;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--body_text-color);
|
|
||||||
gap: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 250px;
|
|
||||||
max-width: 100%;
|
|
||||||
border: 1px solid var(--input_background);
|
|
||||||
}
|
|
||||||
.node:hover {
|
|
||||||
background: var(--input_hover_background);
|
|
||||||
color: var(--input_text);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.node > * {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
.node_icon {
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
vertical-align: middle;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.node_name {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
word-break: break-all;
|
|
||||||
line-height: 1.2em;
|
|
||||||
}
|
|
||||||
.action_button {
|
|
||||||
margin: 0;
|
|
||||||
background: none;
|
|
||||||
color: var(--body_text_color);
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -7,7 +7,6 @@ import CardSubscription from "./CardSubscription.svelte";
|
|||||||
import CardUsage from "./CardUsage.svelte";
|
import CardUsage from "./CardUsage.svelte";
|
||||||
import CardActivity from "./CardActivity.svelte";
|
import CardActivity from "./CardActivity.svelte";
|
||||||
import CardPrepaidTransactions from "./CardPrepaidTransactions.svelte";
|
import CardPrepaidTransactions from "./CardPrepaidTransactions.svelte";
|
||||||
import CardFsHome from "./CardFSHome.svelte";
|
|
||||||
import AddressReputation from "home_page/AddressReputation.svelte";
|
import AddressReputation from "home_page/AddressReputation.svelte";
|
||||||
import { flip } from "svelte/animate";
|
import { flip } from "svelte/animate";
|
||||||
|
|
||||||
@@ -60,51 +59,37 @@ const swap_card = (idx1, idx2) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
cards = []
|
cards = [
|
||||||
if (window.user.subscription.filesystem_access === true) {
|
{
|
||||||
cards.push({
|
id: "account",
|
||||||
id: "filesystem_home",
|
elem: CardAccount,
|
||||||
elem: CardFsHome,
|
title: "Account",
|
||||||
title: "Filesystem home",
|
link: "/user/settings",
|
||||||
link: "/d/me",
|
},{
|
||||||
})
|
id: "subscription",
|
||||||
}
|
elem: CardSubscription,
|
||||||
cards.push({
|
title: "Subscription",
|
||||||
id: "account",
|
link: "/user/subscription",
|
||||||
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({
|
|
||||||
id: "prepaid_transactions",
|
id: "prepaid_transactions",
|
||||||
elem: CardPrepaidTransactions,
|
elem: CardPrepaidTransactions,
|
||||||
title: "Prepaid transactions",
|
title: "Prepaid transactions",
|
||||||
link: "/user/prepaid/transactions",
|
link: "/user/prepaid/transactions",
|
||||||
})
|
}, {
|
||||||
}
|
id: "usage",
|
||||||
cards.push({
|
elem: CardUsage,
|
||||||
id: "usage",
|
title: "Usage",
|
||||||
elem: CardUsage,
|
}, {
|
||||||
title: "Usage",
|
id: "statistics",
|
||||||
})
|
elem: CardStatistics,
|
||||||
cards.push({
|
title: "Statistics",
|
||||||
id: "statistics",
|
}, {
|
||||||
elem: CardStatistics,
|
id: "activity",
|
||||||
title: "Statistics",
|
elem: CardActivity,
|
||||||
})
|
title: "Activity",
|
||||||
cards.push({
|
link: "/user/activity",
|
||||||
id: "activity",
|
}
|
||||||
elem: CardActivity,
|
]
|
||||||
title: "Activity",
|
|
||||||
link: "/user/activity",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Apply the view settings from localstorage
|
// Apply the view settings from localstorage
|
||||||
try {
|
try {
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import { fade } from "svelte/transition";
|
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
|
||||||
let dragging = false
|
|
||||||
|
|
||||||
const drop = (e) => {
|
|
||||||
dragging = false;
|
|
||||||
if (e.dataTransfer && e.dataTransfer.items.length > 0) {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
dispatch("upload", e.dataTransfer.files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const paste = (e) => {
|
|
||||||
if (e.clipboardData.files.length !== 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
dispatch("upload", e.clipboardData.files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window
|
|
||||||
on:dragover|preventDefault|stopPropagation={() => { dragging = true }}
|
|
||||||
on:dragenter|preventDefault|stopPropagation={() => { dragging = true }}
|
|
||||||
on:dragleave|preventDefault|stopPropagation={() => { dragging = false }}
|
|
||||||
on:drop={drop}
|
|
||||||
on:paste={paste}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if dragging}
|
|
||||||
<div class="drag_target" transition:fade={{duration: 200}}>
|
|
||||||
Drop files here to upload them
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.drag_target {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
padding: 50px;
|
|
||||||
font-size: 2em;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 100px;
|
|
||||||
box-shadow: 0 0 10px 10px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -1,66 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import ProgressBar from "util/ProgressBar.svelte";
|
|
||||||
import { upload_file } from "util/upload_widget/UploadFunc.js";
|
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
|
||||||
export let job = {
|
|
||||||
file: null,
|
|
||||||
name: "",
|
|
||||||
id: "",
|
|
||||||
status: "",
|
|
||||||
}
|
|
||||||
export let total = 0
|
|
||||||
export let loaded = 0
|
|
||||||
let error_code = ""
|
|
||||||
let error_message = ""
|
|
||||||
|
|
||||||
export const start = () => {
|
|
||||||
upload_file(
|
|
||||||
job.file,
|
|
||||||
job.name,
|
|
||||||
(prog_loaded, prog_total) => {
|
|
||||||
loaded = prog_loaded
|
|
||||||
total = prog_total
|
|
||||||
},
|
|
||||||
async (id) => {
|
|
||||||
console.log("finsished", id)
|
|
||||||
job.status = "finished"
|
|
||||||
job.id = id
|
|
||||||
dispatch("finished")
|
|
||||||
},
|
|
||||||
(code, message) => {
|
|
||||||
console.log("error", code, message)
|
|
||||||
error_code = code
|
|
||||||
error_message = message
|
|
||||||
job.status = "error"
|
|
||||||
dispatch("finished")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
job.status = "uploading"
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="upload_progress" class:error={job.status === "error"}>
|
|
||||||
{job.name}<br/>
|
|
||||||
{#if error_code !== ""}
|
|
||||||
{error_message}<br/>
|
|
||||||
{error_code}<br/>
|
|
||||||
{/if}
|
|
||||||
<ProgressBar total={total} used={loaded}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.upload_progress {
|
|
||||||
display: block;
|
|
||||||
padding: 2px 4px 1px 4px;
|
|
||||||
margin: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
background: var(--danger_color);
|
|
||||||
color: var(--highlight_text_color);
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,183 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { createEventDispatcher, tick } from "svelte";
|
|
||||||
import { fade } from "svelte/transition";
|
|
||||||
import DropUpload from "./DropUpload.svelte";
|
|
||||||
import UploadProgress from "./UploadProgress.svelte";
|
|
||||||
let dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
|
|
||||||
let file_input_field;
|
|
||||||
let file_input_change = e => {
|
|
||||||
// Start uploading the files async
|
|
||||||
upload_files(e.target.files)
|
|
||||||
|
|
||||||
// This resets the file input field
|
|
||||||
file_input_field.nodeValue = ""
|
|
||||||
}
|
|
||||||
export const pick_files = () => {
|
|
||||||
file_input_field.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
export let drop_upload = false
|
|
||||||
let visible = false
|
|
||||||
let upload_queue = [];
|
|
||||||
let task_id_counter = 0
|
|
||||||
|
|
||||||
export const upload_files = async (files) => {
|
|
||||||
if (files.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add files to the queue
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
if (files[i].type === "" && files[i].size === 0) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
upload_queue.push({
|
|
||||||
task_id: task_id_counter,
|
|
||||||
file: files[i],
|
|
||||||
name: files[i].name,
|
|
||||||
component: null,
|
|
||||||
id: "",
|
|
||||||
status: "queued",
|
|
||||||
total_size: files[i].size,
|
|
||||||
loaded_size: 0,
|
|
||||||
})
|
|
||||||
task_id_counter++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reassign array and wait for tick to complete. After the tick is completed
|
|
||||||
// each upload progress bar will have bound itself to its array item
|
|
||||||
upload_queue = upload_queue
|
|
||||||
visible = true
|
|
||||||
await tick()
|
|
||||||
|
|
||||||
start_upload()
|
|
||||||
}
|
|
||||||
|
|
||||||
let active_uploads = 0
|
|
||||||
let state = "idle"
|
|
||||||
|
|
||||||
const start_upload = () => {
|
|
||||||
for (let i = 0; i < upload_queue.length && active_uploads < 3; i++) {
|
|
||||||
if (upload_queue[i]) {
|
|
||||||
if (upload_queue[i].status === "queued") {
|
|
||||||
active_uploads++
|
|
||||||
upload_queue[i].component.start()
|
|
||||||
upload_queue[i].status = "uploading"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active_uploads === 0) {
|
|
||||||
state = "finished"
|
|
||||||
|
|
||||||
let file_ids = []
|
|
||||||
upload_queue.forEach(job => {
|
|
||||||
if (job.status === "finished" && job.id !== "") {
|
|
||||||
file_ids.push(job.id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
dispatch("uploads_finished", file_ids)
|
|
||||||
|
|
||||||
upload_queue = []
|
|
||||||
visible = false
|
|
||||||
} else {
|
|
||||||
state = "uploading"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const finish_upload = (e) => {
|
|
||||||
active_uploads--
|
|
||||||
upload_queue = upload_queue
|
|
||||||
start_upload()
|
|
||||||
}
|
|
||||||
|
|
||||||
const leave_confirmation = e => {
|
|
||||||
if (state === "uploading") {
|
|
||||||
e.preventDefault()
|
|
||||||
e.returnValue = "If you close this page your files will stop uploading. Do you want to continue?"
|
|
||||||
return e.returnValue
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window on:beforeunload={leave_confirmation} />
|
|
||||||
|
|
||||||
<input
|
|
||||||
bind:this={file_input_field}
|
|
||||||
on:change={file_input_change}
|
|
||||||
class="upload_input" type="file" name="file" multiple="multiple"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if visible}
|
|
||||||
<div class="upload_widget" transition:fade={{duration: 200}}>
|
|
||||||
<div class="header">
|
|
||||||
{#if state === "idle"}
|
|
||||||
Waiting for files
|
|
||||||
{:else if state === "uploading"}
|
|
||||||
Uploading files...
|
|
||||||
{:else if state === "finished"}
|
|
||||||
Done
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
{#each upload_queue as job}
|
|
||||||
{#if job.status !== "finished"}
|
|
||||||
<UploadProgress bind:this={job.component} job={job} on:finished={finish_upload}/>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if drop_upload}
|
|
||||||
<DropUpload on:upload={e => upload_files(e.detail)}/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.upload_input {
|
|
||||||
visibility: hidden;
|
|
||||||
position: fixed;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
.upload_widget {
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 500px;
|
|
||||||
max-width: 80%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 50%;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
border-radius: 20px 20px 8px 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 1px 1px 8px var(--shadow_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
background: var(--background_color);
|
|
||||||
color: var(--background_text_color);
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.2em;
|
|
||||||
padding: 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.body {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
background: var(--body_color);
|
|
||||||
color: var(--body_text_color);
|
|
||||||
overflow-y: auto;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { bookmark_del, bookmarks_store } from "lib/Bookmarks";
|
import { bookmark_del, bookmarks_store } from "lib/Bookmarks";
|
||||||
|
import { fs_encode_path } from "lib/FilesystemAPI";
|
||||||
import { highlight_current_page } from "lib/HighlightCurrentPage";
|
import { highlight_current_page } from "lib/HighlightCurrentPage";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $bookmarks_store as bookmark}
|
{#each $bookmarks_store as bookmark}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a class="button" href="/d/{bookmark.id}" use:highlight_current_page>
|
<a class="button" href="/d{fs_encode_path(bookmark.path)}" use:highlight_current_page>
|
||||||
<i class="icon">{bookmark.icon}</i>
|
<i class="icon">{bookmark.icon}</i>
|
||||||
<span class="hide_small">{bookmark.label}</span>
|
<span class="hide_small">{bookmark.label}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -10,9 +10,14 @@ import { fs_get_node } from "lib/FilesystemAPI";
|
|||||||
import { css_from_path } from "filesystem/edit_window/Branding";
|
import { css_from_path } from "filesystem/edit_window/Branding";
|
||||||
import { loading_run, loading_store } from "lib/Loading";
|
import { loading_run, loading_store } from "lib/Loading";
|
||||||
import Spinner from "util/Spinner.svelte";
|
import Spinner from "util/Spinner.svelte";
|
||||||
|
import { get_user, logout_user } from "lib/PixeldrainAPI";
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await loading_run(async () => {
|
await loading_run(async () => {
|
||||||
|
const user = await get_user()
|
||||||
|
if (user.username === undefined || user.username === "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
const root = await fs_get_node("/me")
|
const root = await fs_get_node("/me")
|
||||||
document.documentElement.style = css_from_path(root.path)
|
document.documentElement.style = css_from_path(root.path)
|
||||||
})
|
})
|
||||||
@@ -21,10 +26,20 @@ onMount(async () => {
|
|||||||
|
|
||||||
<div class="nav_container">
|
<div class="nav_container">
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
|
<a class="button" href="/" use:highlight_current_page>
|
||||||
|
<i class="icon">home</i>
|
||||||
|
<span class="hide_small">Home</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
{#if $user.username !== undefined && $user.username !== ""}
|
{#if $user.username !== undefined && $user.username !== ""}
|
||||||
|
<div class="separator hide_small"></div>
|
||||||
|
|
||||||
<div class="username hide_small">
|
<div class="username hide_small">
|
||||||
{$user.username}
|
{$user.username}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
<div class="stats_table hide_small">
|
<div class="stats_table hide_small">
|
||||||
<div>Subscription</div>
|
<div>Subscription</div>
|
||||||
<div>{$user.subscription.name}</div>
|
<div>{$user.subscription.name}</div>
|
||||||
@@ -39,14 +54,28 @@ onMount(async () => {
|
|||||||
<div>Transfer used</div>
|
<div>Transfer used</div>
|
||||||
<div>{formatDataVolume($user.monthly_transfer_used, 3)}</div>
|
<div>{formatDataVolume($user.monthly_transfer_used, 3)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator hide_small"></div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<a class="button" href="/" use:highlight_current_page>
|
<div class="separator hide_small"></div>
|
||||||
<i class="icon">home</i>
|
|
||||||
<span class="hide_small">Home</span>
|
<a class="button" href="/d/me" use:highlight_current_page>
|
||||||
</a>
|
<i class="icon">folder</i>
|
||||||
{#if !$user.username}
|
<span class="hide_small">Filesystem</span>
|
||||||
|
</a>
|
||||||
|
<a class="button" href="/user" use:highlight_current_page>
|
||||||
|
<i class="icon">dashboard</i>
|
||||||
|
<span class="hide_small">Dashboard</span>
|
||||||
|
</a>
|
||||||
|
{#if $user.is_admin}
|
||||||
|
<a class="button" href="/admin" use:highlight_current_page>
|
||||||
|
<i class="icon">admin_panel_settings</i>
|
||||||
|
<span class="hide_small">Admin Panel</span>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
<button class="button" on:click={()=> logout_user("/")}>
|
||||||
|
<i class="icon">logout</i>
|
||||||
|
<span class="hide_small">Log out</span>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
<a class="button" href="/login" use:highlight_current_page>
|
<a class="button" href="/login" use:highlight_current_page>
|
||||||
<i class="icon">login</i>
|
<i class="icon">login</i>
|
||||||
<span class="hide_small">Login</span>
|
<span class="hide_small">Login</span>
|
||||||
@@ -55,22 +84,10 @@ onMount(async () => {
|
|||||||
<i class="icon">how_to_reg</i>
|
<i class="icon">how_to_reg</i>
|
||||||
<span class="hide_small">Register</span>
|
<span class="hide_small">Register</span>
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
|
||||||
<a class="button" href="/user" use:highlight_current_page>
|
|
||||||
<i class="icon">dashboard</i>
|
|
||||||
<span class="hide_small">Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a class="button" href="/d/me" use:highlight_current_page>
|
|
||||||
<i class="icon">folder</i>
|
|
||||||
<span class="hide_small">Filesystem</span>
|
|
||||||
</a>
|
|
||||||
{#if $user.is_admin}
|
|
||||||
<a class="button" href="/admin" use:highlight_current_page>
|
|
||||||
<i class="icon">admin_panel_settings</i>
|
|
||||||
<span class="hide_small">Admin Panel</span>
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
<a class="button" href="/speedtest" use:highlight_current_page>
|
<a class="button" href="/speedtest" use:highlight_current_page>
|
||||||
<i class="icon">speed</i>
|
<i class="icon">speed</i>
|
||||||
<span class="hide_small">Speedtest</span>
|
<span class="hide_small">Speedtest</span>
|
||||||
@@ -80,7 +97,8 @@ onMount(async () => {
|
|||||||
<span class="hide_small">Themes</span>
|
<span class="hide_small">Themes</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="separator hide_small"></div>
|
<div class="separator"></div>
|
||||||
|
|
||||||
<Bookmarks/>
|
<Bookmarks/>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user