Use custom event for file manager events
This commit is contained in:
@@ -7,7 +7,7 @@ import Breadcrumbs from "./Breadcrumbs.svelte";
|
|||||||
import DetailsWindow from "./DetailsWindow.svelte";
|
import DetailsWindow from "./DetailsWindow.svelte";
|
||||||
import FilePreview from "./viewers/FilePreview.svelte";
|
import FilePreview from "./viewers/FilePreview.svelte";
|
||||||
import FSUploadWidget from "./upload_widget/FSUploadWidget.svelte";
|
import FSUploadWidget from "./upload_widget/FSUploadWidget.svelte";
|
||||||
import { fs_path_url, type FSPath } from "./FilesystemAPI";
|
import { fs_download, type FSPath } from "./FilesystemAPI";
|
||||||
import Menu from "./Menu.svelte";
|
import Menu from "./Menu.svelte";
|
||||||
import { FSNavigator } from "./FSNavigator"
|
import { FSNavigator } from "./FSNavigator"
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
@@ -68,7 +68,7 @@ const keydown = (e: KeyboardEvent) => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "s":
|
case "s":
|
||||||
download()
|
fs_download(nav.base)
|
||||||
break;
|
break;
|
||||||
case "r":
|
case "r":
|
||||||
nav.shuffle = !nav.shuffle
|
nav.shuffle = !nav.shuffle
|
||||||
@@ -125,21 +125,6 @@ const keydown = (e: KeyboardEvent) => {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const download = () => {
|
|
||||||
const a = document.createElement("a")
|
|
||||||
|
|
||||||
if (nav.base.type === "file") {
|
|
||||||
a.href = fs_path_url(nav.base.path) + "?attach"
|
|
||||||
a.download = nav.base.name
|
|
||||||
} else if (nav.base.type === "dir") {
|
|
||||||
a.href = fs_path_url(nav.base.path) + "?bulk_download"
|
|
||||||
a.download = nav.base.name+".zip"
|
|
||||||
}
|
|
||||||
|
|
||||||
a.click()
|
|
||||||
a.remove()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={keydown} />
|
<svelte:window on:keydown={keydown} />
|
||||||
@@ -159,7 +144,7 @@ const download = () => {
|
|||||||
bind:details_visible={details_visible}
|
bind:details_visible={details_visible}
|
||||||
edit_window={edit_window}
|
edit_window={edit_window}
|
||||||
bind:edit_visible={edit_visible}
|
bind:edit_visible={edit_visible}
|
||||||
on:download={download}
|
on:download={() => fs_download(nav.base)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="file_preview">
|
<div class="file_preview">
|
||||||
@@ -169,7 +154,7 @@ const download = () => {
|
|||||||
upload_widget={upload_widget}
|
upload_widget={upload_widget}
|
||||||
edit_window={edit_window}
|
edit_window={edit_window}
|
||||||
on:open_sibling={e => nav.open_sibling(e.detail)}
|
on:open_sibling={e => nav.open_sibling(e.detail)}
|
||||||
on:download={download}
|
on:download={() => fs_download(nav.base)}
|
||||||
on:details={() => details_visible = !details_visible}
|
on:details={() => details_visible = !details_visible}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -351,3 +351,18 @@ export const fs_share_path = (path: FSNode[]): string => {
|
|||||||
|
|
||||||
return share_url
|
return share_url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fs_download = (node: FSNode) => {
|
||||||
|
const a = document.createElement("a")
|
||||||
|
|
||||||
|
if (node.type === "file") {
|
||||||
|
a.href = fs_path_url(node.path) + "?attach"
|
||||||
|
a.download = node.name
|
||||||
|
} else if (node.type === "dir") {
|
||||||
|
a.href = fs_path_url(node.path) + "?bulk_download"
|
||||||
|
a.download = node.name + ".zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
a.click()
|
||||||
|
a.remove()
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
|
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
|
import { FileAction } from "./FileManagerLib";
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ export let hide_edit = false
|
|||||||
{#each $nav.children as child, index (child.path)}
|
{#each $nav.children as child, index (child.path)}
|
||||||
<a
|
<a
|
||||||
href={"/d"+fs_encode_path(child.path)}
|
href={"/d"+fs_encode_path(child.path)}
|
||||||
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
|
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||||
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
|
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||||
class="node"
|
class="node"
|
||||||
class:node_selected={child.fm_selected}
|
class:node_selected={child.fm_selected}
|
||||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||||
@@ -28,7 +29,7 @@ export let hide_edit = false
|
|||||||
{#if child.id}
|
{#if child.id}
|
||||||
<a
|
<a
|
||||||
href="/d/{child.id}"
|
href="/d/{child.id}"
|
||||||
on:click|preventDefault|stopPropagation={e => {dispatch("node_share_click", {index: index, original: e})}}
|
on:click={e => dispatch("file", {index: index, action: FileAction.Share, original: e})}
|
||||||
class="button flat action_button"
|
class="button flat action_button"
|
||||||
>
|
>
|
||||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||||
@@ -36,10 +37,20 @@ export let hide_edit = false
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $nav.permissions.write && !hide_edit}
|
{#if $nav.permissions.write && !hide_edit}
|
||||||
<button class="action_button flat" on:click|preventDefault|stopPropagation={e => dispatch("node_settings", {index: index, original: e})}>
|
<button
|
||||||
|
class="action_button flat"
|
||||||
|
on:click={e => dispatch("file", {index: index, action: FileAction.Edit, original: e})}
|
||||||
|
>
|
||||||
<i class="icon">edit</i>
|
<i class="icon">edit</i>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="action_button flat"
|
||||||
|
on:click={e => dispatch("file", {index: index, action: FileAction.Download, original: e})}
|
||||||
|
>
|
||||||
|
<i class="icon">save</i>
|
||||||
|
</button>
|
||||||
</a>
|
</a>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,11 +1,5 @@
|
|||||||
<script lang="ts" context="module">
|
|
||||||
export type FMNodeEvent = {
|
|
||||||
index: number,
|
|
||||||
original: MouseEvent,
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fs_delete_all, fs_rename, type FSNode } from "filesystem/FilesystemAPI"
|
import { fs_delete_all, fs_download, fs_rename, type FSNode } from "filesystem/FilesystemAPI"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import CreateDirectory from "./CreateDirectory.svelte"
|
import CreateDirectory from "./CreateDirectory.svelte"
|
||||||
import ListView from "./ListView.svelte"
|
import ListView from "./ListView.svelte"
|
||||||
@@ -19,6 +13,7 @@ import SearchBar from "./SearchBar.svelte";
|
|||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
|
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";
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
export let upload_widget: FsUploadWidget
|
export let upload_widget: FsUploadWidget
|
||||||
@@ -36,49 +31,62 @@ export const upload = (files: File[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Navigation functions
|
// Navigation functions
|
||||||
|
const file_event = (e: CustomEvent<FileEvent>) => {
|
||||||
const node_click = (e: CustomEvent<FMNodeEvent>) => {
|
|
||||||
const index = e.detail.index
|
const index = e.detail.index
|
||||||
|
|
||||||
creating_dir = false
|
switch (e.detail.action) {
|
||||||
|
case FileAction.Click:
|
||||||
// We prefix our custom state properties with fm_ to not interfere with
|
|
||||||
// other modules
|
|
||||||
if (mode === "viewing") {
|
|
||||||
nav.navigate(nav.children[index].path, true)
|
|
||||||
} else if (mode === "moving") {
|
|
||||||
// If we are moving files we can only enter directories, and only if
|
|
||||||
// they're not selected. That last requirement prevents people from
|
|
||||||
// moving a directory into itself
|
|
||||||
if (nav.children[index].type === "dir" && !nav.children[index].fm_selected) {
|
|
||||||
nav.navigate(nav.children[index].path, true)
|
|
||||||
}
|
|
||||||
} else if (mode === "selecting") {
|
|
||||||
select_node(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let node_context = (e: CustomEvent<FMNodeEvent>) => {
|
|
||||||
// If this is a touch event we will select the item
|
|
||||||
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
|
|
||||||
e.detail.original.preventDefault()
|
e.detail.original.preventDefault()
|
||||||
node_select(e)
|
e.detail.original.stopPropagation()
|
||||||
}
|
creating_dir = false
|
||||||
}
|
|
||||||
const node_share_click = (e: CustomEvent<FMNodeEvent>) => {
|
|
||||||
creating_dir = false
|
|
||||||
edit_window.edit(nav.children[e.detail.index], false, "share")
|
|
||||||
}
|
|
||||||
const node_select = (e: CustomEvent<FMNodeEvent>) => {
|
|
||||||
const index = e.detail.index
|
|
||||||
mode = "selecting"
|
|
||||||
nav.children[index].fm_selected = !nav.children[index].fm_selected
|
|
||||||
}
|
|
||||||
|
|
||||||
const node_settings = (e: CustomEvent<FMNodeEvent>) => {
|
if (mode === "viewing") {
|
||||||
edit_window.edit(nav.children[e.detail.index], false, "file")
|
nav.navigate(nav.children[index].path, true)
|
||||||
}
|
} else if (mode === "moving") {
|
||||||
const node_branding = (e: CustomEvent<FMNodeEvent>) => {
|
// If we are moving files we can only enter directories, and only if
|
||||||
edit_window.edit(nav.children[e.detail.index], false, "branding")
|
// they're not selected. That last requirement prevents people from
|
||||||
|
// moving a directory into itself
|
||||||
|
if (nav.children[index].type === "dir" && !nav.children[index].fm_selected) {
|
||||||
|
nav.navigate(nav.children[index].path, true)
|
||||||
|
}
|
||||||
|
} else if (mode === "selecting") {
|
||||||
|
select_node(index)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case FileAction.Context:
|
||||||
|
// If this is a touch event we will select the item
|
||||||
|
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
select_node(index)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case FileAction.Edit:
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
e.detail.original.stopPropagation()
|
||||||
|
edit_window.edit(nav.children[index], false, "file")
|
||||||
|
break
|
||||||
|
case FileAction.Share:
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
e.detail.original.stopPropagation()
|
||||||
|
creating_dir = false
|
||||||
|
edit_window.edit(nav.children[index], false, "share")
|
||||||
|
break
|
||||||
|
case FileAction.Branding:
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
e.detail.original.stopPropagation()
|
||||||
|
edit_window.edit(nav.children[index], false, "branding")
|
||||||
|
break
|
||||||
|
case FileAction.Select:
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
e.detail.original.stopPropagation()
|
||||||
|
select_node(index)
|
||||||
|
break
|
||||||
|
case FileAction.Download:
|
||||||
|
e.detail.original.preventDefault()
|
||||||
|
e.detail.original.stopPropagation()
|
||||||
|
fs_download(nav.children[index])
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigate_back = () => {
|
const navigate_back = () => {
|
||||||
@@ -195,6 +203,7 @@ const keypress = (e: KeyboardEvent) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const select_node = (index: number) => {
|
const select_node = (index: number) => {
|
||||||
|
mode = "selecting"
|
||||||
if (shift_pressed) {
|
if (shift_pressed) {
|
||||||
// If shift is pressed we do a range select. We select all files between
|
// If shift is pressed we do a range select. We select all files between
|
||||||
// the last selected file and the file that is being selected now
|
// the last selected file and the file that is being selected now
|
||||||
@@ -398,38 +407,11 @@ onMount(() => {
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
||||||
{#if directory_view === "list"}
|
{#if directory_view === "list"}
|
||||||
<ListView
|
<ListView nav={nav} show_hidden={show_hidden} large_icons={large_icons} on:file={file_event} />
|
||||||
nav={nav}
|
|
||||||
show_hidden={show_hidden}
|
|
||||||
large_icons={large_icons}
|
|
||||||
on:node_click={node_click}
|
|
||||||
on:node_context={node_context}
|
|
||||||
on:node_share_click={node_share_click}
|
|
||||||
on:node_settings={node_settings}
|
|
||||||
on:node_branding={node_branding}
|
|
||||||
on:node_select={node_select}
|
|
||||||
/>
|
|
||||||
{:else if directory_view === "gallery"}
|
{:else if directory_view === "gallery"}
|
||||||
<GalleryView
|
<GalleryView nav={nav} show_hidden={show_hidden} large_icons={large_icons} on:file={file_event} />
|
||||||
nav={nav}
|
|
||||||
show_hidden={show_hidden}
|
|
||||||
large_icons={large_icons}
|
|
||||||
on:node_click={node_click}
|
|
||||||
on:node_context={node_context}
|
|
||||||
on:node_settings={node_settings}
|
|
||||||
on:node_select={node_select}
|
|
||||||
/>
|
|
||||||
{:else if directory_view === "compact"}
|
{:else if directory_view === "compact"}
|
||||||
<CompactView
|
<CompactView nav={nav} show_hidden={show_hidden} large_icons={large_icons} on:file={file_event} />
|
||||||
nav={nav}
|
|
||||||
show_hidden={show_hidden}
|
|
||||||
large_icons={large_icons}
|
|
||||||
on:node_click={node_click}
|
|
||||||
on:node_context={node_context}
|
|
||||||
on:node_share_click={node_share_click}
|
|
||||||
on:node_settings={node_settings}
|
|
||||||
on:node_select={node_select}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
6
svelte/src/filesystem/filemanager/FileManagerLib.ts
Normal file
6
svelte/src/filesystem/filemanager/FileManagerLib.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export type FileEvent = {
|
||||||
|
index: number,
|
||||||
|
action: FileAction,
|
||||||
|
original: MouseEvent,
|
||||||
|
}
|
||||||
|
export enum FileAction { Click, Context, Edit, Share, Branding, Select, Download }
|
@@ -2,6 +2,7 @@
|
|||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { fs_node_icon, fs_node_type, fs_encode_path } from "filesystem/FilesystemAPI";
|
import { fs_node_icon, fs_node_type, fs_encode_path } from "filesystem/FilesystemAPI";
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
|
import { FileAction } from "./FileManagerLib";
|
||||||
let dispatch = createEventDispatcher()
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let nav: FSNavigator
|
export let nav: FSNavigator
|
||||||
@@ -13,8 +14,8 @@ export let large_icons = false
|
|||||||
{#each $nav.children as child, index (child.path)}
|
{#each $nav.children as child, index (child.path)}
|
||||||
<a class="file"
|
<a class="file"
|
||||||
href={"/d"+fs_encode_path(child.path)}
|
href={"/d"+fs_encode_path(child.path)}
|
||||||
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
|
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||||
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
|
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||||
class:selected={child.fm_selected}
|
class:selected={child.fm_selected}
|
||||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||||
class:large_icons
|
class:large_icons
|
||||||
|
@@ -4,6 +4,7 @@ import { formatDataVolume } from "util/Formatting";
|
|||||||
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
|
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
import SortButton from "layout/SortButton.svelte";
|
import SortButton from "layout/SortButton.svelte";
|
||||||
|
import { FileAction } from "./FileManagerLib";
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@@ -24,8 +25,8 @@ export let hide_branding = false
|
|||||||
{#each $nav.children as child, index (child.path)}
|
{#each $nav.children as child, index (child.path)}
|
||||||
<a
|
<a
|
||||||
href={"/d"+fs_encode_path(child.path)}
|
href={"/d"+fs_encode_path(child.path)}
|
||||||
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
|
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||||
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
|
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||||
class="node"
|
class="node"
|
||||||
class:node_selected={child.fm_selected}
|
class:node_selected={child.fm_selected}
|
||||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||||
@@ -48,22 +49,25 @@ export let hide_branding = false
|
|||||||
{:else if child.id}
|
{:else if child.id}
|
||||||
<a
|
<a
|
||||||
href="/d/{child.id}"
|
href="/d/{child.id}"
|
||||||
on:click|preventDefault|stopPropagation={e => {dispatch("node_share_click", {index: index, original: e})}}
|
on:click={e => dispatch("file", {index: index, action: FileAction.Share, original: e})}
|
||||||
class="button action_button"
|
class="button action_button"
|
||||||
>
|
>
|
||||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{#if child.properties !== undefined && child.properties.branding_enabled === "true" && !hide_branding}
|
{#if child.properties !== undefined && child.properties.branding_enabled === "true" && !hide_branding}
|
||||||
<button class="action_button" on:click|preventDefault|stopPropagation={e => dispatch("node_branding", {index: index, original: e})}>
|
<button class="action_button" on:click={e => dispatch("file", {index: index, action: FileAction.Branding, original: e})}>
|
||||||
<i class="icon">palette</i>
|
<i class="icon">palette</i>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $nav.permissions.write && !hide_edit}
|
{#if $nav.permissions.write && !hide_edit}
|
||||||
<button class="action_button" on:click|preventDefault|stopPropagation={e => dispatch("node_settings", {index: index, original: e})}>
|
<button class="action_button" on:click={e => dispatch("file", {index: index, action: FileAction.Edit, original: e})}>
|
||||||
<i class="icon">edit</i>
|
<i class="icon">edit</i>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
<button class="action_button" on:click={e => dispatch("file", {index: index, action: FileAction.Download, original: e})}>
|
||||||
|
<i class="icon">save</i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -117,6 +117,15 @@ const login = async (e?: SubmitEvent) => {
|
|||||||
only have one login link at a time. Login links stay active
|
only have one login link at a time. Login links stay active
|
||||||
for 15 minutes.`
|
for 15 minutes.`
|
||||||
}
|
}
|
||||||
|
} else if (err.value === "password_incorrect") {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `The entered password is not correct for this user. If
|
||||||
|
you have an e-mail address configured on your account you
|
||||||
|
can try logging in with only your e-mail address to get a
|
||||||
|
login link. If you have forgotten your password you can
|
||||||
|
change it from the account settings page after logging in.`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {success: false, message: undefined, error_json: err}
|
return {success: false, message: undefined, error_json: err}
|
||||||
|
Reference in New Issue
Block a user