Convert the whole filesystem UI to Typescript

This commit is contained in:
2025-03-28 14:16:20 +01:00
parent 8279b9646e
commit d5cd5b1db1
114 changed files with 601 additions and 670 deletions

View File

@@ -1,5 +1,5 @@
import svelte from 'rollup-plugin-svelte'; import svelte from 'rollup-plugin-svelte';
import resolve, { nodeResolve } from '@rollup/plugin-node-resolve'; import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs'; import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload'; import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser'; import terser from '@rollup/plugin-terser';
@@ -52,13 +52,10 @@ export default [
resolve({ resolve({
browser: true, browser: true,
exportConditions: ['svelte'], exportConditions: ['svelte'],
extensions: ['.svelte'], modulePaths: [process.cwd() + "/src", process.cwd() + "/node_modules"],
extensions: [".svelte", ".mjs", ".js", ".json", ".mts", ".ts"],
}), }),
commonjs(), commonjs(),
nodeResolve({
modulePaths: [process.cwd() + "/src"],
extensions: [".svelte", ".mjs", ".js", ".json"]
}),
typescript(), typescript(),
// Watch the `public` directory and refresh the browser on changes when // Watch the `public` directory and refresh the browser on changes when

View File

@@ -1,5 +1,5 @@
<script> <script>
import { formatDate, formatNumber } from "util/Formatting.svelte"; import { formatDate, formatNumber } from "util/Formatting";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Modal from "util/Modal.svelte" import Modal from "util/Modal.svelte"
import SortButton from "./SortButton.svelte"; import SortButton from "./SortButton.svelte";
import { flip } from "svelte/animate"; import { flip } from "svelte/animate";

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onDestroy, onMount } from "svelte"; import { onDestroy, onMount } from "svelte";
import { formatDataVolume, formatThousands, formatDate, formatNumber, formatDuration } from "util/Formatting.svelte"; import { formatDataVolume, formatThousands, formatDate, formatNumber, formatDuration } from "util/Formatting";
import Chart from "util/Chart.svelte"; import Chart from "util/Chart.svelte";
import { color_by_name } from "util/Util.svelte"; import { color_by_name } from "util/Util.svelte";
import ServerDiagnostics from "./ServerDiagnostics.svelte"; import ServerDiagnostics from "./ServerDiagnostics.svelte";

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import { mollie_proxy_call } from "./MollieAPI.js"; import { mollie_proxy_call } from "./MollieAPI";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";

View File

@@ -1,11 +1,11 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";
import MollieSettlement from "./MollieSettlement.svelte"; import MollieSettlement from "./MollieSettlement.svelte";
import { mollie_proxy_call } from "./MollieAPI.js"; import { mollie_proxy_call } from "./MollieAPI";
let loading = true let loading = true
let response = {} let response = {}

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";

View File

@@ -1,6 +1,6 @@
<script> <script>
import { flip } from "svelte/animate"; import { flip } from "svelte/animate";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import SortButton from "./SortButton.svelte"; import SortButton from "./SortButton.svelte";
export let peers = []; export let peers = [];

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import { formatDuration } from "util/Formatting.svelte"; import { formatDuration } from "util/Formatting";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let running_since = "" export let running_since = ""

View File

@@ -1,6 +1,6 @@
<script> <script>
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";
import { formatDataVolume, formatDate } from "util/Formatting.svelte"; import { formatDataVolume, formatDate } from "util/Formatting";
export let row = {} export let row = {}
</script> </script>

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import Button from "layout/Button.svelte" import Button from "layout/Button.svelte"

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import { formatDataVolume, formatDate } from "util/Formatting.svelte"; import { formatDataVolume, formatDate } from "util/Formatting";
import SortButton from "admin_panel/SortButton.svelte"; import SortButton from "admin_panel/SortButton.svelte";
export let user_id = "" export let user_id = ""

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
export let user_id = "" export let user_id = ""
let lists = [] let lists = []

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDataVolume, formatDate, formatThousands } from "util/Formatting.svelte" import { formatDataVolume, formatDate, formatThousands } from "util/Formatting"
import { color_by_name, domain_url } from "util/Util.svelte"; import { color_by_name, domain_url } from "util/Util.svelte";
import Chart from "util/Chart.svelte"; import Chart from "util/Chart.svelte";

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher, tick } from "svelte"; import { createEventDispatcher, tick } from "svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import DirectoryElement from "user_home/filemanager/DirectoryElement.svelte"; import DirectoryElement from "user_home/filemanager/DirectoryElement.svelte";
import Modal from "util/Modal.svelte"; import Modal from "util/Modal.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,6 +1,6 @@
<script> <script>
import { formatDataVolume, formatThousands } from "util/Formatting.svelte" import { formatDataVolume, formatThousands } from "util/Formatting"
import { set_file, stats } from "lib/StatsSocket.mjs" import { set_file, stats } from "lib/StatsSocket"
export let file = { export let file = {
id: "", id: "",

View File

@@ -3,7 +3,7 @@ import { createEventDispatcher } from "svelte"
import { flip } from "svelte/animate" import { flip } from "svelte/animate"
import FilePicker from "./FilePicker.svelte" import FilePicker from "./FilePicker.svelte"
import { file_type } from "./FileUtilities.svelte"; import { file_type } from "./FileUtilities.svelte";
import { get_video_position } from "lib/VideoPosition.mjs" import { get_video_position } from "lib/VideoPosition"
import ProgressBar from "util/ProgressBar.svelte" import ProgressBar from "util/ProgressBar.svelte"
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,5 +1,5 @@
<script> <script>
import { formatDataVolume, formatThousands } from "util/Formatting.svelte" import { formatDataVolume, formatThousands } from "util/Formatting"
export let list = { export let list = {
files: [], files: [],

View File

@@ -1,6 +1,6 @@
<script> <script>
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
let percent = 0 let percent = 0
let title = "" let title = ""

View File

@@ -1,8 +1,8 @@
<script> <script>
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
export let file = { export let file = {
size: 0, size: 0,

View File

@@ -3,7 +3,7 @@ import { createEventDispatcher } from "svelte";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import FileTitle from "layout/FileTitle.svelte"; import FileTitle from "layout/FileTitle.svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export const set_file = f => file = f export const set_file = f => file = f

View File

@@ -11,7 +11,7 @@ import Abuse from "./Abuse.svelte";
import { file_type } from "file_viewer/FileUtilities.svelte"; import { file_type } from "file_viewer/FileUtilities.svelte";
import RateLimit from "./RateLimit.svelte"; import RateLimit from "./RateLimit.svelte";
import Torrent from "./Torrent.svelte"; import Torrent from "./Torrent.svelte";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
import Zip from "./Zip.svelte"; import Zip from "./Zip.svelte";
import SlowDown from "layout/SlowDown.svelte"; import SlowDown from "layout/SlowDown.svelte";
import TextBlock from "layout/TextBlock.svelte"; import TextBlock from "layout/TextBlock.svelte";

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { swipe_nav } from "lib/SwipeNavigate.mjs"; import { swipe_nav } from "lib/SwipeNavigate";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export const set_file = f => { export const set_file = f => {
@@ -74,16 +74,19 @@ const on_load = () => dispatch("loading", false)
bind:this={container} bind:this={container}
class="container" class="container"
class:zoom class:zoom
use:swipe_nav={{enabled: !zoom && is_list, prev: true, next: true}} use:swipe_nav={{
on:prev enabled: !zoom && is_list,
on:next prev: true,
next: true,
on_prev: () => dispatch("prev"),
on_next: () => dispatch("prev"),
}}
> >
<!-- svelte-ignore a11y-no-noninteractive-element-interactions --> <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<img <img
on:load={on_load} on:load={on_load}
on:error={on_load} on:error={on_load}
on:dblclick={double_click} on:dblclick={double_click}
on:doubletap={double_click}
on:mousedown={mousedown} on:mousedown={mousedown}
class="image" class="image"
class:zoom class:zoom

View File

@@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import Magnet from "icons/Magnet.svelte"; import Magnet from "icons/Magnet.svelte";
import { formatDate } from "util/Formatting.svelte" import { formatDate } from "util/Formatting"
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte"; import TextBlock from "layout/TextBlock.svelte";
import TorrentItem from "./TorrentItem.svelte" import TorrentItem from "./TorrentItem.svelte"

View File

@@ -1,5 +1,5 @@
<script> <script>
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
export let item = { export let item = {
size: 0, size: 0,

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount, createEventDispatcher, tick } from "svelte"; import { onMount, createEventDispatcher, tick } from "svelte";
import { video_position } from "lib/VideoPosition.mjs"; import { video_position } from "lib/VideoPosition";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDate } from "util/Formatting.svelte" import { formatDataVolume, formatDate } from "util/Formatting"
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import ZipItem from "./ZipItem.svelte"; import ZipItem from "./ZipItem.svelte";

View File

@@ -1,11 +1,8 @@
<script> <script lang="ts">
import { formatDataVolume } from "util/Formatting.svelte"; import type { ZipEntry } from "filesystem/viewers/Zip.svelte";
import { formatDataVolume } from "util/Formatting";
export let item = { export let item: ZipEntry = {} as ZipEntry
download_url: "",
size: 0,
children: null,
}
</script> </script>
<!-- First get directories and render them as details collapsibles --> <!-- First get directories and render them as details collapsibles -->

View File

@@ -1,7 +1,8 @@
<script> <script lang="ts">
import { fs_encode_path } from "./FilesystemAPI.mjs"; import { fs_encode_path } from "./FilesystemAPI";
import type { FSNavigator } from "./FSNavigator";
export let nav export let nav: FSNavigator
</script> </script>
<div class="breadcrumbs"> <div class="breadcrumbs">

View File

@@ -1,14 +1,14 @@
<script> <script lang="ts">
import Chart from "util/Chart.svelte"; import Chart from "util/Chart.svelte";
import { formatDataVolume, formatDate, formatNumber, formatThousands } from "util/Formatting.svelte"; import { formatDataVolume, formatDate, formatThousands } from "util/Formatting";
import Modal from "util/Modal.svelte"; import Modal from "util/Modal.svelte";
import { fs_path_url, fs_timeseries } from "./FilesystemAPI.mjs"; import { fs_path_url, fs_share_path, fs_share_url, fs_timeseries, type FSNode } from "./FilesystemAPI";
import { generate_share_path, generate_share_url } from "./Sharebar.svelte";
import { color_by_name } from "util/Util.svelte"; import { color_by_name } from "util/Util.svelte";
import { tick } from "svelte"; import { tick } from "svelte";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";
import type { FSNavigator } from "./FSNavigator";
export let nav export let nav: FSNavigator
export let visible = false export let visible = false
export const toggle = () => visible = !visible export const toggle = () => visible = !visible
@@ -20,8 +20,8 @@ const visibility_change = visible => {
} }
$: direct_url = $nav.base.path ? window.location.origin+fs_path_url($nav.base.path) : "" $: direct_url = $nav.base.path ? window.location.origin+fs_path_url($nav.base.path) : ""
$: share_url = generate_share_url($nav.path) $: share_url = fs_share_url($nav.path)
$: direct_share_url = $nav.base.path ? window.location.origin+fs_path_url(generate_share_path($nav.path)) : "" $: direct_share_url = $nav.base.path ? window.location.origin+fs_path_url(fs_share_path($nav.path)) : ""
let chart let chart
let chart_timespan = 0 let chart_timespan = 0
@@ -40,7 +40,7 @@ let total_downloads = 0
let total_transfer = 0 let total_transfer = 0
$: update_chart($nav.base, chart_timespan, chart_interval) $: update_chart($nav.base, chart_timespan, chart_interval)
let update_chart = async (base, timespan, interval) => { let update_chart = async (base: FSNode, timespan: number, interval: number) => {
if (chart === undefined) { if (chart === undefined) {
// Wait for the chart element to render, if it's not rendered already // Wait for the chart element to render, if it's not rendered already
await tick() await tick()
@@ -94,7 +94,6 @@ let update_chart = async (base, timespan, interval) => {
}, },
} }
resp.downloads.timestamps.forEach((val, idx) => { resp.downloads.timestamps.forEach((val, idx) => {
let date = new Date(val); let date = new Date(val);
let str = ("00" + (date.getMonth() + 1)).slice(-2); let str = ("00" + (date.getMonth() + 1)).slice(-2);

View File

@@ -1,5 +1,5 @@
import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI.mjs"; import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI";
import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI.mjs"; import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI";
import type { Writable } from "svelte/store" import type { Writable } from "svelte/store"
export class FSNavigator { export class FSNavigator {

View File

@@ -1,9 +1,10 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDataVolume, formatThousands } from "util/Formatting.svelte" import { formatDataVolume, formatThousands } from "util/Formatting"
import { fs_path_url } from "./FilesystemAPI.mjs"; import { fs_path_url } from "./FilesystemAPI";
import type { FSNavigator } from "./FSNavigator";
export let nav export let nav: FSNavigator
let loading = true let loading = true
let downloads = 0 let downloads = 0
@@ -71,7 +72,7 @@ const update_base = async () => {
window.setTimeout(() => { window.setTimeout(() => {
if (socket === null) { if (socket === null) {
update_base(nav.base) update_base()
} }
}, 5000) }, 5000)
} }
@@ -119,12 +120,12 @@ const close_socket = () => {
<div class="group"> <div class="group">
<div class="label">Directories</div> <div class="label">Directories</div>
<div class="stat">{formatThousands(total_directories, 3)}</div> <div class="stat">{formatThousands(total_directories)}</div>
</div> </div>
<div class="group"> <div class="group">
<div class="label">Files</div> <div class="label">Files</div>
<div class="stat">{formatThousands(total_files, 3)}</div> <div class="stat">{formatThousands(total_files)}</div>
</div> </div>
<div class="group"> <div class="group">

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import EditWindow from "./edit_window/EditWindow.svelte"; import EditWindow from "./edit_window/EditWindow.svelte";
@@ -7,21 +7,21 @@ 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 } from "./FilesystemAPI.mjs"; import { fs_path_url, 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";
import TransferLimit from "file_viewer/TransferLimit.svelte"; import TransferLimit from "file_viewer/TransferLimit.svelte";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
import { css_from_path } from "filesystem/edit_window/Branding.js"; import { css_from_path } from "filesystem/edit_window/Branding";
import AffiliatePrompt from "user_home/AffiliatePrompt.svelte"; import AffiliatePrompt from "user_home/AffiliatePrompt.svelte";
let file_viewer let file_viewer: HTMLDivElement
let file_preview let file_preview: FilePreview
let toolbar let toolbar: Toolbar
let upload_widget let upload_widget: FSUploadWidget
let details_visible = false let details_visible = false
let edit_window let edit_window: EditWindow
let edit_visible = false let edit_visible = false
const loading = writable(true) const loading = writable(true)
@@ -29,7 +29,7 @@ const nav = new FSNavigator(true)
onMount(() => { onMount(() => {
nav.loading = loading nav.loading = loading
nav.open_node(window.initial_node, false) nav.open_node((window as any).initial_node as FSPath, false)
// Subscribe to navigation updates. This function returns a deconstructor // Subscribe to navigation updates. This function returns a deconstructor
// which we can conveniently return from our mount function as well // which we can conveniently return from our mount function as well
@@ -45,10 +45,10 @@ onMount(() => {
}) })
}) })
const keydown = e => { const keydown = (e: KeyboardEvent) => {
if (e.ctrlKey || e.altKey || e.metaKey) { if (e.ctrlKey || e.altKey || e.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts return // prevent custom shortcuts from interfering with system shortcuts
} else if (document.activeElement.type && document.activeElement.type === "text") { } else if ((document.activeElement as any).type !== undefined && (document.activeElement as any).type === "text") {
return // Prevent shortcuts from interfering with input fields return // Prevent shortcuts from interfering with input fields
} }
@@ -127,7 +127,7 @@ const keydown = e => {
}; };
const download = () => { const download = () => {
let a = document.createElement("a") const a = document.createElement("a")
if (nav.base.type === "file") { if (nav.base.type === "file") {
a.href = fs_path_url(nav.base.path) + "?attach" a.href = fs_path_url(nav.base.path) + "?attach"

View File

@@ -24,18 +24,38 @@ export type FSNode = {
mode_octal: string, mode_octal: string,
created_by: string, created_by: string,
abuse_type: string | undefined, abuse_type?: string,
abuse_report_time: string | undefined, abuse_report_time?: string,
file_size: number, file_size: number,
file_type: string, file_type: string,
sha256_sum: string, sha256_sum: string,
id: string | undefined, id?: string,
properties: {} | undefined, properties?: FSNodeProperties,
link_permissions: FSPermissions | undefined, link_permissions?: FSPermissions,
user_permissions: [string: FSPermissions] | undefined, user_permissions?: { [index: string]: FSPermissions },
password_permissions: [string: FSPermissions] | undefined, password_permissions?: { [index: string]: FSPermissions },
// Added by us
// Indicates whether the file is selected in the file manager
fm_selected?: boolean,
}
export type FSNodeProperties = {
branding_enabled?: string,
brand_input_color?: string,
brand_highlight_color?: string,
brand_danger_color?: string,
brand_background_color?: string,
brand_body_color?: string,
brand_card_color?: string,
brand_header_image?: string,
brand_header_link?: string,
brand_footer_image?: string,
brand_footer_link?: string,
brand_background_image?: string,
} }
export type FSPermissions = { export type FSPermissions = {
@@ -53,38 +73,26 @@ export type FSContext = {
// ============== // ==============
export type NodeOptions = { export type NodeOptions = {
mode: number | undefined, mode?: number,
created: string | undefined, created?: string,
modified: string | undefined, modified?: string,
shared: boolean | undefined, shared?: boolean,
// Permissions // Permissions
link_permissions: FSPermissions | undefined, link_permissions?: FSPermissions,
user_permissions: FSPermissions | undefined, user_permissions?: { [index: string]: FSPermissions },
password_permissions: FSPermissions | undefined, password_permissions?: { [index: string]: FSPermissions },
} & FSNodeProperties
// Branding
branding_enabled: boolean | undefined,
brand_input_color: string | undefined,
brand_highlight_color: string | undefined,
brand_danger_color: string | undefined,
brand_background_color: string | undefined,
brand_body_color: string | undefined,
brand_card_color: string | undefined,
brand_header_image: string | undefined,
brand_header_link: string | undefined,
brand_background_image: string | undefined,
}
// API methods // API methods
// =========== // ===========
// mkdir only supports the "mode" option // mkdir only supports the "mode" option
export const fs_mkdir = async (path: string, opts: NodeOptions) => { export const fs_mkdir = async (path: string, opts?: NodeOptions) => {
const form = new FormData() const form = new FormData()
form.append("action", "mkdir") form.append("action", "mkdir")
if (opts && opts.mode) { if (opts !== undefined && opts.mode !== undefined) {
form.append("mode", opts.mode.toFixed(0)) form.append("mode", opts.mode.toFixed(0))
} }
@@ -169,7 +177,17 @@ export const fs_search = async (path: string, term: string, limit = 10) => {
"?search=" + encodeURIComponent(term) + "?search=" + encodeURIComponent(term) +
"&limit=" + limit "&limit=" + limit
) )
) as Array<string> ) as string[]
}
export type TimeSeries = {
timestamps: string[],
amounts: number[],
}
export type NodeTimeSeries = {
downloads: TimeSeries,
transfer_free: TimeSeries,
transfer_paid: TimeSeries,
} }
export const fs_timeseries = async (path: string, start: Date, end: Date, interval = 60) => { export const fs_timeseries = async (path: string, start: Date, end: Date, interval = 60) => {
@@ -181,7 +199,7 @@ export const fs_timeseries = async (path: string, start: Date, end: Date, interv
"&end=" + end.toISOString() + "&end=" + end.toISOString() +
"&interval=" + interval "&interval=" + interval
) )
) ) as NodeTimeSeries
} }
export const fs_import = async (parent_dir_path = "", filelist: Array<string>) => { export const fs_import = async (parent_dir_path = "", filelist: Array<string>) => {
@@ -301,3 +319,35 @@ export const fs_node_icon = (node: FSNode, width = 64, height = 64) => {
export const fs_thumbnail_url = (path: string, width = 64, height = 64) => { export const fs_thumbnail_url = (path: string, width = 64, height = 64) => {
return fs_path_url(path) + "?thumbnail&width=" + width + "&height=" + height return fs_path_url(path) + "?thumbnail&width=" + width + "&height=" + height
} }
export const fs_share_url = (path: FSNode[]): string => {
let share_path = fs_share_path(path)
if (share_path !== "") {
share_path = window.location.protocol + "//" + window.location.host + "/d/" + fs_encode_path(share_path)
}
return share_path
}
export const fs_share_path = (path: FSNode[]): string => {
let share_url = ""
let bucket_idx = -1
// 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") {
bucket_idx = i
break
}
}
if (bucket_idx !== -1) {
share_url = path[bucket_idx].id
// Construct the path starting from the bucket
for (let i = bucket_idx + 1; i < path.length; i++) {
share_url += "/" + path[i].name
}
}
return share_url
}

View File

@@ -1,11 +1,12 @@
<script> <script lang="ts">
import PixeldrainLogo from "util/PixeldrainLogo.svelte"; import PixeldrainLogo from "util/PixeldrainLogo.svelte";
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import { user } from "lib/UserStore";
let button let button: HTMLButtonElement
let dialog let dialog: HTMLDialogElement
export let no_login_label = "Pixeldrain" export let no_login_label = "Pixeldrain"
@@ -40,7 +41,7 @@ const open = () => {
} }
// Close the dialog when the user clicks the background // Close the dialog when the user clicks the background
const click = e => { const click = (e: MouseEvent) => {
if (e.target === dialog) { if (e.target === dialog) {
dialog.close() dialog.close()
} }
@@ -53,7 +54,7 @@ const click = e => {
<PixeldrainLogo style="height: 1.6em; width: 1.6em;"/> <PixeldrainLogo style="height: 1.6em; width: 1.6em;"/>
{/if} {/if}
<span class="button_username" class:hide_name> <span class="button_username" class:hide_name>
{window.user.username === "" ? no_login_label : window.user.username} {$user.username === "" ? no_login_label : $user.username}
</span> </span>
</button> </button>
</div> </div>
@@ -62,28 +63,28 @@ const click = e => {
<!-- svelte-ignore a11y-no-noninteractive-element-interactions --> <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<dialog bind:this={dialog} on:click={click}> <dialog bind:this={dialog} on:click={click}>
<div class="menu"> <div class="menu">
{#if window.user.username !== ""} {#if $user.username !== undefined && $user.username !== ""}
<Button link_href="/user" link_target={target} icon="dashboard" label="Dashboard" /> <Button link_href="/user" link_target={target} icon="dashboard" label="Dashboard" />
<div class="separator"></div> <div class="separator"></div>
<div class="stats_table"> <div class="stats_table">
<div>Subscription</div> <div>Subscription</div>
<div>{window.user.subscription.name}</div> <div>{$user.subscription.name}</div>
{#if window.user.subscription.type === "prepaid"} {#if $user.subscription.type === "prepaid"}
<div>Credit</div> <div>Credit</div>
<div><Euro amount={window.user.balance_micro_eur}/></div> <div><Euro amount={$user.balance_micro_eur}/></div>
{/if} {/if}
<div>Storage used</div> <div>Storage used</div>
<div>{formatDataVolume(window.user.filesystem_storage_used, 3)}</div> <div>{formatDataVolume($user.filesystem_storage_used, 3)}</div>
<div>Transfer used</div> <div>Transfer used</div>
<div>{formatDataVolume(window.user.monthly_transfer_used, 3)}</div> <div>{formatDataVolume($user.monthly_transfer_used, 3)}</div>
</div> </div>
<div class="separator"></div> <div class="separator"></div>
{#if window.user.subscription.filesystem_access} {#if $user.subscription.filesystem_access}
<Button link_href="/d/me" link_target={target} icon="folder" label="My Filesystem"/> <Button link_href="/d/me" link_target={target} icon="folder" label="My Filesystem"/>
{:else} {:else}
<Button link_href="/#pro" link_target={target} icon="star" label="Get Premium"/> <Button link_href="/#pro" link_target={target} icon="star" label="Get Premium"/>
@@ -102,7 +103,7 @@ const click = e => {
<Button link_href="/user/subscription" link_target={target} icon="shopping_cart" label="Subscription"/> <Button link_href="/user/subscription" link_target={target} icon="shopping_cart" label="Subscription"/>
<Button link_href="/user/prepaid/transactions" link_target={target} icon="receipt" label="Transactions"/> <Button link_href="/user/prepaid/transactions" link_target={target} icon="receipt" label="Transactions"/>
{#if window.user.is_admin} {#if $user.is_admin}
<div class="separator"></div> <div class="separator"></div>
<Button link_href="/admin" link_target={target} icon="admin_panel_settings" label="Admin Panel"/> <Button link_href="/admin" link_target={target} icon="admin_panel_settings" label="Admin Panel"/>
{/if} {/if}

View File

@@ -1,157 +0,0 @@
<script context="module">
export const generate_share_url = path => {
let share_path = generate_share_path(path)
if (share_path !== "") {
share_path = window.location.protocol+"//"+window.location.host+"/d/"+fs_encode_path(share_path)
}
return share_path
}
export const generate_share_path = path => {
let share_url = ""
let bucket_idx = -1
// 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") {
bucket_idx = i
break
}
}
if (bucket_idx !== -1) {
share_url = path[bucket_idx].id
// Construct the path starting from the bucket
for (let i = bucket_idx+1; i < path.length; i++) {
share_url += "/" + path[i].name
}
}
return share_url
}
</script>
<script>
import { fs_update, fs_encode_path } from "./FilesystemAPI.mjs";
export let visible = false
export let share_url = ""
$: {
if (share_url === "") {
visible = false
}
}
const share = async () => {
console.debug("Making file sharable", state.base)
try {
await fs_update(state.base.path, {shared: true})
} catch (err) {
console.error(err)
alert(err)
return
}
fs_navigator.navigate(state.base.path, false)
}
export let state
export let fs_navigator
const share_email = () => {
window.open(
'mailto:please@set.address?subject=File%20on%20pixeldrain&body='+encodeURIComponent(share_url)
);
}
const share_reddit = () => {
window.open('https://www.reddit.com/submit?url='+encodeURIComponent(share_url));
}
const share_twitter = () => {
window.open('https://twitter.com/share?url='+encodeURIComponent(share_url));
}
const share_facebook = () => {
window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(share_url));
}
const share_tumblr = () => {
window.open('http://www.tumblr.com/share/link?url='+encodeURIComponent(share_url));
}
</script>
<div class="sharebar" class:visible>
{#if share_url !== ""}
Share on:<br/>
<button class="button_full_width" on:click={share_email}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M22 4H2v16h20V4zm-2 4l-8 5-8-5V6l8 5 8-5v2z"/>
</svg>
<br/>
E-Mail
</button>
<button class="button_full_width" on:click={share_reddit}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22,12.14C22,10.92 21,9.96 19.81,9.96C19.22,9.96 18.68,10.19 18.29,10.57C16.79,9.5 14.72,8.79 12.43,8.7L13.43,4L16.7,4.71C16.73,5.53 17.41,6.19 18.25,6.19C19.11,6.19 19.81,5.5 19.81,4.63C19.81,3.77 19.11,3.08 18.25,3.08C17.65,3.08 17.11,3.43 16.86,3.95L13.22,3.18C13.11,3.16 13,3.18 12.93,3.24C12.84,3.29 12.79,3.38 12.77,3.5L11.66,8.72C9.33,8.79 7.23,9.5 5.71,10.58C5.32,10.21 4.78,10 4.19,10C2.97,10 2,10.96 2,12.16C2,13.06 2.54,13.81 3.29,14.15C3.25,14.37 3.24,14.58 3.24,14.81C3.24,18.18 7.16,20.93 12,20.93C16.84,20.93 20.76,18.2 20.76,14.81C20.76,14.6 20.75,14.37 20.71,14.15C21.46,13.81 22,13.04 22,12.14M7,13.7C7,12.84 7.68,12.14 8.54,12.14C9.4,12.14 10.1,12.84 10.1,13.7A1.56,1.56 0 0,1 8.54,15.26C7.68,15.28 7,14.56 7,13.7M15.71,17.84C14.63,18.92 12.59,19 12,19C11.39,19 9.35,18.9 8.29,17.84C8.13,17.68 8.13,17.43 8.29,17.27C8.45,17.11 8.7,17.11 8.86,17.27C9.54,17.95 11,18.18 12,18.18C13,18.18 14.47,17.95 15.14,17.27C15.3,17.11 15.55,17.11 15.71,17.27C15.85,17.43 15.85,17.68 15.71,17.84M15.42,15.28C14.56,15.28 13.86,14.58 13.86,13.72A1.56,1.56 0 0,1 15.42,12.16C16.28,12.16 17,12.86 17,13.72C17,14.56 16.28,15.28 15.42,15.28Z" />
</svg>
<br/>
Reddit
</button>
<button class="button_full_width" on:click={share_twitter}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
</svg>
<br/>
Twitter
</button>
<button class="button_full_width" on:click={share_facebook}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M18,5H15.5A3.5,3.5 0 0,0 12,8.5V11H10V14H12V21H15V14H18V11H15V9A1,1 0 0,1 16,8H18V5Z" />
</svg>
<br/>
Facebook
</button>
<button class="button_full_width" on:click={share_tumblr}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M17,11H13V15.5C13,16.44 13.28,17 14.5,17H17V21C17,21 15.54,21.05 14.17,21.05C10.8,21.05 9.5,19 9.5,16.75V11H7V7C10.07,6.74 10.27,4.5 10.5,3H13V7H17" />
</svg>
<br/>
Tumblr
</button>
{:else}
This file or directory is not currently shared. Would you like to make it sharable?
<button on:click={share}>
<i class="icon">share</i>
Make sharable
</button>
{/if}
</div>
<style>
.sharebar{
position: absolute;
width: 7em;
left: -8em;
bottom: 0;
top: 0;
overflow-y: scroll;
overflow-x: hidden;
float: left;
background: var(--shaded_background);
backdrop-filter: blur(4px);
text-align: center;
overflow: hidden;
opacity: 0;
transition: left 0.3s, opacity 0.3s;
border-top-left-radius: 16px;
border-bottom-left-radius: 16px;
}
.visible {
left: 8em;
opacity: 1;
}
.button_full_width {
width: calc(100% - 6px);
}
.button_full_width > svg {
height: 3em;
width: 3em;
fill: currentColor;
}
</style>

View File

@@ -1,19 +1,22 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { generate_share_url } from "./Sharebar.svelte";
import { copy_text } from "util/Util.svelte"; import { copy_text } from "util/Util.svelte";
import FileStats from "./FileStats.svelte"; import FileStats from "./FileStats.svelte";
import type { FSNavigator } from "./FSNavigator";
import EditWindow from "./edit_window/EditWindow.svelte";
import FilePreview from "./viewers/FilePreview.svelte";
import { fs_share_url } from "./FilesystemAPI";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
export let details_visible = false export let details_visible = false
export let edit_window export let edit_window: EditWindow
export let edit_visible = false export let edit_visible = false
export let file_viewer export let file_viewer: HTMLDivElement
export let file_preview export let file_preview: FilePreview
$: share_url = generate_share_url($nav.path) $: share_url = fs_share_url($nav.path)
let link_copied = false let link_copied = false
export const copy_link = () => { export const copy_link = () => {
if (share_url === "") { if (share_url === "") {
@@ -60,7 +63,7 @@ export const toggle_fullscreen = () => {
} }
let expanded = true let expanded = true
let expand = e => { let expand = (e: Event) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
expanded = !expanded expanded = !expanded

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import type { FSNode, FSPermissions } from "filesystem/FilesystemAPI.mjs"; import type { FSNode, FSPermissions } from "filesystem/FilesystemAPI";
import PermissionButton from "./PermissionButton.svelte"; import PermissionButton from "./PermissionButton.svelte";
export let file: FSNode export let file: FSNode
@@ -10,7 +10,7 @@ let new_user_perms = <FSPermissions>{read: true}
const add_user = (e: SubmitEvent) => { const add_user = (e: SubmitEvent) => {
e.preventDefault() e.preventDefault()
if (file.user_permissions === undefined) { if (file.user_permissions === undefined) {
file.user_permissions = <[string: FSPermissions]>{} file.user_permissions = {} as {[index: string]: FSPermissions}
} }
file.user_permissions[new_user_id] = structuredClone(new_user_perms) file.user_permissions[new_user_id] = structuredClone(new_user_perms)
} }
@@ -24,7 +24,7 @@ let new_password_perms = <FSPermissions>{read: true}
const add_password = (e: SubmitEvent) => { const add_password = (e: SubmitEvent) => {
e.preventDefault() e.preventDefault()
if (file.password_permissions === undefined) { if (file.password_permissions === undefined) {
file.password_permissions = <[string: FSPermissions]>{} file.password_permissions = {} as {[index: string]: FSPermissions}
} }
file.password_permissions[new_password] = structuredClone(new_password_perms) file.password_permissions[new_password] = structuredClone(new_password_perms)
} }

View File

@@ -1,4 +1,4 @@
import parse from "pure-color/parse" import parse from "pure-color/parse";
import rgb2hsl from "pure-color/convert/rgb2hsl"; import rgb2hsl from "pure-color/convert/rgb2hsl";
import hsl2rgb from "pure-color/convert/hsl2rgb"; import hsl2rgb from "pure-color/convert/hsl2rgb";
import rgb2hex from "pure-color/convert/rgb2hex"; import rgb2hex from "pure-color/convert/rgb2hex";

View File

@@ -1,33 +1,17 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import ThemePresets from "./ThemePresets.svelte"; import ThemePresets from "./ThemePresets.svelte";
import FilePicker from "file_viewer/FilePicker.svelte"; import { fs_update, fs_node_type, type FSNode } from "filesystem/FilesystemAPI";
import { fs_update, fs_node_type } from "filesystem/FilesystemAPI.mjs";
import CustomBanner from "filesystem/viewers/CustomBanner.svelte"; import CustomBanner from "filesystem/viewers/CustomBanner.svelte";
import HelpButton from "layout/HelpButton.svelte"; import HelpButton from "layout/HelpButton.svelte";
import FilePicker from "filesystem/filemanager/FilePicker.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let file = { export let file: FSNode
properties: {
branding_enabled: "",
brand_input_color: "",
brand_highlight_color: "",
brand_danger_color: "",
brand_background_color: "",
brand_body_color: "",
brand_card_color: "",
brand_header_image: "",
brand_header_link: "",
brand_footer_image: "",
brand_footer_link: "",
brand_background_image: "",
},
}
export let enabled = false export let enabled = false
$: update_colors(file) $: update_colors(file)
const update_colors = file => { const update_colors = (file: FSNode) => {
if (enabled) { if (enabled) {
file.properties.branding_enabled = "true" file.properties.branding_enabled = "true"
dispatch("style_change") dispatch("style_change")
@@ -36,13 +20,13 @@ const update_colors = file => {
} }
} }
let picker let picker: FilePicker
let picking = "" let picking = ""
const pick_image = type => { const pick_image = (type: string) => {
picking = type picking = type
picker.open(file.path) picker.open(file.path)
} }
const handle_picker = async e => { const handle_picker = async (e: CustomEvent<FSNode[]>) => {
if (e.detail.length !== 1) { if (e.detail.length !== 1) {
alert("Please select one file") alert("Please select one file")
return return

View File

@@ -1,29 +1,24 @@
<script> <script lang="ts">
import { fs_rename, fs_update } from "filesystem/FilesystemAPI.mjs"; import { fs_rename, fs_update, type FSNode, type FSNodeProperties, type NodeOptions } from "filesystem/FilesystemAPI";
import Modal from "util/Modal.svelte"; import Modal from "util/Modal.svelte";
import BrandingOptions from "./BrandingOptions.svelte"; import BrandingOptions from "./BrandingOptions.svelte";
import { branding_from_node } from "./Branding"; import { branding_from_node } from "./Branding";
import FileOptions from "./FileOptions.svelte"; 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";
export let nav export let nav: FSNavigator
let file = { let file: FSNode = {} as FSNode
path: "",
name: "",
id: "",
mode_octal: "",
properties: {},
};
let custom_css = "" let custom_css = ""
export let visible export let visible: boolean
// Open the edit window. Argument 1 is the file to edit, 2 is whether the file // Open the edit window. Argument 1 is the file to edit, 2 is whether the file
// should be opened after the user finishes editing and 3 is the default tab // should be opened after the user finishes editing and 3 is the default tab
// that should be open when the window shows // that should be open when the window shows
export const edit = (f, oae = false, open_tab = "") => { export const edit = (f: FSNode, oae = false, open_tab = "") => {
file = f file = f
open_after_edit = oae open_after_edit = oae
if (open_tab !== "") { if (open_tab !== "") {
@@ -38,12 +33,12 @@ export const edit = (f, oae = false, open_tab = "") => {
shared = !(file.id === undefined || file.id === "") shared = !(file.id === undefined || file.id === "")
if (file.properties === undefined) { if (file.properties === undefined) {
file.properties = {} file.properties = {} as FSNodeProperties
} }
if (shared && file.link_permissions === undefined) { if (shared && file.link_permissions === undefined) {
// Default to read-only for public links // Default to read-only for public links
file.link_permissions = {read: true, write: false, delete: false} file.link_permissions = { owner: false, read: true, write: false, delete: false}
} }
branding_enabled = file.properties.branding_enabled === "true" branding_enabled = file.properties.branding_enabled === "true"
@@ -59,51 +54,31 @@ export const edit = (f, oae = false, open_tab = "") => {
let tab = "file" let tab = "file"
let open_after_edit = false let open_after_edit = false
let shared = false
let new_name = "" let new_name = ""
let shared = false
let branding_enabled = false let branding_enabled = false
let branding_colors
let branding_fields = [
"brand_input_color",
"brand_highlight_color",
"brand_danger_color",
"brand_background_color",
"brand_body_color",
"brand_card_color",
"brand_header_image",
"brand_header_link",
"brand_background_image",
]
const save = async (keep_editing = false) => { const save = async (keep_editing = false) => {
console.debug("Saving file", file.path) console.debug("Saving file", file.path)
let new_file let new_file: FSNode
try { try {
nav.set_loading(true) nav.set_loading(true)
let opts = { let opts = {
shared: shared, shared: shared,
} branding_enabled: JSON.stringify(branding_enabled)
} as NodeOptions
opts.branding_enabled = branding_enabled ? "true" : "" if (branding_enabled && file.properties !== undefined) {
for (let field of Object.keys(file.properties)) {
if (branding_enabled && file.properties) {
for (let field of branding_fields) {
if (file.properties[field] !== undefined) {
console.log("setting", field, "to", file.properties[field]) console.log("setting", field, "to", file.properties[field])
opts[field] = file.properties[field] opts[field] = file.properties[field]
} }
} }
}
if (shared && file.link_permissions !== undefined) { if (shared) {
opts.link_permissions = file.link_permissions opts.link_permissions = file.link_permissions
}
if (shared && file.user_permissions !== undefined) {
opts.user_permissions = file.user_permissions opts.user_permissions = file.user_permissions
}
if (shared && file.password_permissions !== undefined) {
opts.password_permissions = file.password_permissions opts.password_permissions = file.password_permissions
} }
@@ -179,11 +154,10 @@ const save = async (keep_editing = false) => {
{:else if tab === "share"} {:else if tab === "share"}
<SharingOptions bind:file bind:shared on:save={() => save(true)} /> <SharingOptions bind:file bind:shared on:save={() => save(true)} />
{:else if tab === "access"} {:else if tab === "access"}
<AccessControl bind:file bind:shared /> <AccessControl bind:file />
{:else if tab === "branding"} {:else if tab === "branding"}
<BrandingOptions <BrandingOptions
bind:enabled={branding_enabled} bind:enabled={branding_enabled}
bind:colors={branding_colors}
bind:file bind:file
on:style_change={e => custom_css = branding_from_node(file)} on:style_change={e => custom_css = branding_from_node(file)}
/> />

View File

@@ -1,17 +1,18 @@
<script> <script lang="ts">
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import { fs_delete_all } from "filesystem/FilesystemAPI.mjs"; import { fs_delete_all, type FSNode } from "filesystem/FilesystemAPI";
import PathLink from "filesystem/util/PathLink.svelte"; import PathLink from "filesystem/util/PathLink.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
export let file = {} export let file: FSNode = {} as FSNode
export let new_name export let new_name: string
export let visible export let visible: boolean
export let open_after_edit export let open_after_edit: boolean
$: is_root_dir = file.path === "/"+file.id $: is_root_dir = file.path === "/"+file.id
const delete_file = async e => { const delete_file = async (e: MouseEvent) => {
e.preventDefault() e.preventDefault()
try { try {

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import ToggleButton from "layout/ToggleButton.svelte"; import ToggleButton from "layout/ToggleButton.svelte";
import type { FSPermissions } from "filesystem/FilesystemAPI.mjs"; import type { FSPermissions } from "filesystem/FilesystemAPI";
export let permissions = <FSPermissions>{} export let permissions = <FSPermissions>{}
</script> </script>

View File

@@ -1,20 +1,21 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { domain_url } from "util/Util.svelte"; import { domain_url } from "util/Util.svelte";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import type { FSNode } from "filesystem/FilesystemAPI";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let shared export let shared: boolean
export let file export let file: FSNode = {} as FSNode
let embed_html let embed_html: string
let preview_area let preview_area: HTMLDivElement
$: is_shared = file.id !== undefined && file.id !== "" $: is_shared = file.id !== undefined && file.id !== ""
$: share_link = window.location.protocol+"//"+window.location.host+"/d/"+file.id $: share_link = window.location.protocol+"//"+window.location.host+"/d/"+file.id
$: embed_iframe(file) $: embed_iframe(file)
let embed_iframe = file => { let embed_iframe = (file: FSNode) => {
if (!is_shared) { if (!is_shared) {
example = false example = false
embed_html = "File is not shared, can't generate embed code" embed_html = "File is not shared, can't generate embed code"
@@ -41,7 +42,7 @@ const toggle_example = () => {
} }
} }
const update_shared = e => { const update_shared = () => {
// If sharing is enabled we automatically save the file so the user can copy // If sharing is enabled we automatically save the file so the user can copy
// the sharing link. But if the user disables sharing we don't automatically // the sharing link. But if the user disables sharing we don't automatically
// save so that the user can't accidentally discard a sharing link that's in // save so that the user can't accidentally discard a sharing link that's in

View File

@@ -1,17 +1,15 @@
<script> <script lang="ts">
export let properties = { import type { FSNodeProperties } from "filesystem/FilesystemAPI";
brand_input_color: "",
brand_highlight_color: "", export let properties: FSNodeProperties = {} as FSNodeProperties
brand_danger_color: "",
brand_background_color: "",
brand_body_color: "",
brand_card_color: ""
}
let current_theme = -1 let current_theme = -1
const set_theme = index => { const set_theme = (index: number) => {
current_theme = index current_theme = index
// Copy all the properties of the theme to the file, except for the theme
// name
Object.keys(themes[index]).forEach(key => { Object.keys(themes[index]).forEach(key => {
if (key !== "name") { if (key !== "name") {
properties[key] = themes[index][key] properties[key] = themes[index][key]

View File

@@ -1,10 +1,11 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI.mjs" import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
export let show_hidden = false export let show_hidden = false
export let large_icons = false export let large_icons = false
</script> </script>
@@ -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 <a
href={"/d"+fs_encode_path(child.path)} href={"/d"+fs_encode_path(child.path)}
on:click|preventDefault={() => dispatch("node_click", index)} on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})} on:contextmenu={e => dispatch("node_context", {index: index, 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}
@@ -26,7 +27,7 @@ export let large_icons = false
{#if child.id} {#if child.id}
<a <a
href="/d/{child.id}" href="/d/{child.id}"
on:click|preventDefault|stopPropagation={() => {dispatch("node_share_click", index)}} on:click|preventDefault|stopPropagation={e => {dispatch("node_share_click", {index: index, 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>

View File

@@ -1,11 +1,12 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { fs_mkdir } from "filesystem/FilesystemAPI.mjs"; import { fs_mkdir } from "filesystem/FilesystemAPI";
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav; export let nav: FSNavigator
let name_input; let name_input: HTMLInputElement;
let new_dir_name = "" let new_dir_name = ""
let error_msg = "" let error_msg = ""
let create_dir = async () => { let create_dir = async () => {

View File

@@ -1,13 +1,15 @@
<script> <script lang="ts">
import FilePicker from "file_viewer/FilePicker.svelte"; import FilePicker from "file_viewer/FilePicker.svelte";
import { fs_import } from "filesystem/FilesystemAPI.mjs"; import { fs_import } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
let file_picker let file_picker: FilePicker
export const open = () => file_picker.open() export const open = () => file_picker.open()
const import_files = async files => { // TODO: Give files a proper type
const import_files = async (files: any) => {
nav.set_loading(true) nav.set_loading(true)
let fileids = [] let fileids = []

View File

@@ -1,5 +1,11 @@
<script> <script lang="ts" context="module">
import { fs_delete_all, fs_rename } from "filesystem/FilesystemAPI.mjs" export type FMNodeEvent = {
index: number,
original: MouseEvent,
}
</script>
<script lang="ts">
import { fs_delete_all, 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"
@@ -7,29 +13,32 @@ import GalleryView from "./GalleryView.svelte"
import CompactView from "./CompactView.svelte" import CompactView from "./CompactView.svelte"
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import FileImporter from "./FileImporter.svelte"; import FileImporter from "./FileImporter.svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import { drop_target } from "lib/DropTarget.ts" import { drop_target } from "lib/DropTarget"
import SearchBar from "./SearchBar.svelte"; import SearchBar from "./SearchBar.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
export let nav export let nav: FSNavigator
export let upload_widget export let upload_widget: FsUploadWidget
export let edit_window export let edit_window: EditWindow
export let directory_view = "" export let directory_view = ""
let large_icons = false let large_icons = false
let uploader let uploader: FsUploadWidget
let mode = "viewing" let mode = "viewing"
let creating_dir = false let creating_dir = false
let show_hidden = false let show_hidden = false
let file_importer let file_importer: FileImporter
export const upload = files => { export const upload = (files: File[]) => {
return uploader.upload(files) return uploader.upload(files)
} }
// Navigation functions // Navigation functions
const node_click = e => { const node_click = (e: CustomEvent<FMNodeEvent>) => {
let index = e.detail const index = e.detail.index
creating_dir = false creating_dir = false
@@ -48,27 +57,29 @@ const node_click = e => {
select_node(index) select_node(index)
} }
} }
let node_context = e => { let node_context = (e: CustomEvent<FMNodeEvent>) => {
// If this is a touch event we will select the item // If this is a touch event we will select the item
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) { if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
e.detail.event.preventDefault() e.detail.original.preventDefault()
node_select({detail: e.detail.index}) node_select(e)
} }
} }
const node_share_click = e => { const node_share_click = (e: CustomEvent<FMNodeEvent>) => {
let index = e.detail
creating_dir = false creating_dir = false
nav.navigate(nav.children[index].id, true) nav.navigate(nav.children[e.detail.index].id, true)
} }
const node_select = e => { const node_select = (e: CustomEvent<FMNodeEvent>) => {
let index = e.detail const index = e.detail.index
mode = "selecting" mode = "selecting"
nav.children[index].fm_selected = !nav.children[index].fm_selected nav.children[index].fm_selected = !nav.children[index].fm_selected
} }
const node_settings = e => edit_window.edit(nav.children[e.detail], false, "file") const node_settings = (e: CustomEvent<FMNodeEvent>) => {
const node_branding = e => edit_window.edit(nav.children[e.detail], false, "branding") edit_window.edit(nav.children[e.detail.index], false, "file")
}
const node_branding = (e: CustomEvent<FMNodeEvent>) => {
edit_window.edit(nav.children[e.detail.index], false, "branding")
}
const navigate_back = () => { const navigate_back = () => {
creating_dir = false creating_dir = false
@@ -147,7 +158,7 @@ const toggle_view = () => {
} }
const toggle_large_icons = () => { const toggle_large_icons = () => {
large_icons = !large_icons large_icons = !large_icons
localStorage.setItem("large_icons", large_icons) localStorage.setItem("large_icons", JSON.stringify(large_icons))
} }
// Moving functions // Moving functions
@@ -157,7 +168,7 @@ let moving_items = []
// We need to detect if shift is pressed so we can select multiple items // We need to detect if shift is pressed so we can select multiple items
let shift_pressed = false let shift_pressed = false
let last_selected_node = -1 let last_selected_node = -1
const keypress = e => { const keypress = (e: KeyboardEvent) => {
if (e.key === "Shift") { if (e.key === "Shift") {
shift_pressed = e.type === "keydown" shift_pressed = e.type === "keydown"
} else if (e.type === "keydown" && e.key === "a" && e.ctrlKey) { } else if (e.type === "keydown" && e.key === "a" && e.ctrlKey) {
@@ -176,7 +187,7 @@ const keypress = e => {
} }
} }
const select_node = index => { const select_node = (index: number) => {
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
@@ -199,7 +210,7 @@ const select_node = index => {
// function watches the children array for changes and updates the selection // function watches the children array for changes and updates the selection
// when it changes // when it changes
$: update($nav.children) $: update($nav.children)
const update = (children) => { const update = (children: FSNode[]) => {
creating_dir = false creating_dir = false
// Highlight the files which were previously selected // Highlight the files which were previously selected
@@ -327,11 +338,7 @@ onMount(() => {
<i class="icon">move_to_inbox</i> <i class="icon">move_to_inbox</i>
</button> </button>
<button <button on:click={selecting_mode} title="Select and delete files">
on:click={selecting_mode}
class:button_highlight={mode === "selecting"}
title="Select and delete files"
>
<i class="icon">select_all</i> <i class="icon">select_all</i>
</button> </button>
{/if} {/if}

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
import { createEventDispatcher, onMount } from "svelte" import { createEventDispatcher, onMount } from "svelte"
import ListView from "./ListView.svelte" import ListView from "./ListView.svelte"
import GalleryView from "./GalleryView.svelte" import GalleryView from "./GalleryView.svelte"
@@ -6,19 +6,20 @@ import CompactView from "./CompactView.svelte"
import Modal from "util/Modal.svelte"; import Modal from "util/Modal.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import Breadcrumbs from "filesystem/Breadcrumbs.svelte" import Breadcrumbs from "filesystem/Breadcrumbs.svelte"
import { FSNavigator } from "filesystem/FSNavigator.js"; import { FSNavigator } from "filesystem/FSNavigator";
import type { FMNodeEvent } from "./FileManager.svelte";
import type { FSNode } from "filesystem/FilesystemAPI";
let nav = new FSNavigator(false) let nav = new FSNavigator(false)
let modal let modal: Modal
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
let directory_view = "" let directory_view = ""
let loading = false let loading = false
const loading_evt = e => loading = e.detail
let large_icons = false let large_icons = false
let show_hidden = false let show_hidden = false
export let select_multiple = false export let select_multiple = false
export const open = path => { export const open = (path: string) => {
modal.show() modal.show()
nav.navigate(path, false) nav.navigate(path, false)
} }
@@ -32,8 +33,8 @@ $: selected_files = $nav.children.reduce((acc, file) => {
// Navigation functions // Navigation functions
const node_click = e => { const node_click = (e: CustomEvent<FMNodeEvent>) => {
let index = e.detail const index = e.detail.index
if (nav.children[index].type === "dir") { if (nav.children[index].type === "dir") {
nav.navigate(nav.children[index].path, true) nav.navigate(nav.children[index].path, true)
@@ -41,16 +42,15 @@ const node_click = e => {
select_node(index) select_node(index)
} }
} }
let node_context = e => { let node_context = (e: CustomEvent<FMNodeEvent>) => {
// If this is a touch event we will select the item // If this is a touch event we will select the item
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) { if (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
e.detail.event.preventDefault() e.detail.original.preventDefault()
node_select({detail: e.detail.index}) node_select(e)
} }
} }
const node_select = e => { const node_select = (e: CustomEvent<FMNodeEvent>) => {
let index = e.detail const index = e.detail.index
mode = "selecting"
nav.children[index].fm_selected = !nav.children[index].fm_selected nav.children[index].fm_selected = !nav.children[index].fm_selected
} }
@@ -69,13 +69,13 @@ const toggle_view = () => {
// We need to detect if shift is pressed so we can select multiple items // We need to detect if shift is pressed so we can select multiple items
let shift_pressed = false let shift_pressed = false
let last_selected_node = -1 let last_selected_node = -1
const detect_shift = (e) => { const detect_shift = (e: KeyboardEvent) => {
if (e.key === "Shift") { if (e.key === "Shift") {
shift_pressed = e.type === "keydown" shift_pressed = e.type === "keydown"
} }
} }
const select_node = index => { const select_node = (index: number) => {
if (select_multiple && shift_pressed) { if (select_multiple && 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
@@ -101,7 +101,7 @@ const select_node = index => {
} }
let done = () => { let done = () => {
let selected_files = [] let selected_files: FSNode[] = []
for (let i = 0; i < nav.children.length; i++) { for (let i = 0; i < nav.children.length; i++) {
if (nav.children[i].fm_selected) { if (nav.children[i].fm_selected) {
selected_files.push(nav.children[i]) selected_files.push(nav.children[i])

View File

@@ -1,9 +1,10 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { fs_node_icon, fs_node_type, fs_encode_path } from "filesystem/FilesystemAPI.mjs"; import { fs_node_icon, fs_node_type, fs_encode_path } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
export let show_hidden = false export let show_hidden = false
export let large_icons = false export let large_icons = false
</script> </script>
@@ -12,8 +13,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={() => dispatch("node_click", index)} on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})} on:contextmenu={e => dispatch("node_context", {index: index, 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

View File

@@ -1,11 +1,12 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI.mjs" import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
export let show_hidden = false export let show_hidden = false
export let large_icons = false export let large_icons = false
export let hide_edit = false export let hide_edit = false
@@ -22,8 +23,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={() => dispatch("node_click", index)} on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})} on:contextmenu={e => dispatch("node_context", {index: index, 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}
@@ -46,19 +47,19 @@ 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={() => {dispatch("node_share_click", index)}} on:click|preventDefault|stopPropagation={e => {dispatch("node_share_click", {index: index, 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 && child.properties.branding_enabled && !hide_branding} {#if child.properties !== undefined && child.properties.branding_enabled !== undefined && !hide_branding}
<button class="action_button" on:click|preventDefault|stopPropagation={() => dispatch("node_branding", index)}> <button class="action_button" on:click|preventDefault|stopPropagation={e => dispatch("node_branding", {index: index, 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={() => dispatch("node_settings", index)}> <button class="action_button" on:click|preventDefault|stopPropagation={e => dispatch("node_settings", {index: index, original: e})}>
<i class="icon">edit</i> <i class="icon">edit</i>
</button> </button>
{/if} {/if}

View File

@@ -1,13 +1,14 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { fs_search, fs_encode_path, fs_thumbnail_url } from "filesystem/FilesystemAPI.mjs"; import { fs_search, fs_encode_path, fs_thumbnail_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
let search_bar let search_bar: HTMLInputElement
let error = "" let error = ""
let search_term = "" let search_term = ""
let search_results = [] let search_results: string[] = []
let selected_result = 0 let selected_result = 0
let searching = false let searching = false
let last_searched_term = "" let last_searched_term = ""
@@ -71,7 +72,7 @@ const search = async (limit = 10) => {
} }
} }
const clear_search = (blur) => { const clear_search = (blur: boolean) => {
error = "" error = ""
search_term = "" search_term = ""
search_results = [] search_results = []
@@ -89,12 +90,12 @@ const clear_search = (blur) => {
// Cursor navigation events can only be prevented with keydown. But we want to // Cursor navigation events can only be prevented with keydown. But we want to
// use keyup for searching, so we use two listeners here // use keyup for searching, so we use two listeners here
const input_keydown = e => { const input_keydown = (e: KeyboardEvent) => {
if (e.key === "Escape" || e.key === "ArrowUp" || e.key === "ArrowDown") { if (e.key === "Escape" || e.key === "ArrowUp" || e.key === "ArrowDown") {
e.preventDefault() e.preventDefault()
} }
} }
const input_keyup = e => { const input_keyup = (e: KeyboardEvent) => {
if (e.key === "Escape") { if (e.key === "Escape") {
clear_search(true) clear_search(true)
} else if (e.key === "ArrowUp") { } else if (e.key === "ArrowUp") {
@@ -117,15 +118,15 @@ const submit_search = () => {
} }
} }
const open_result = index => { const open_result = (index: number) => {
nav.navigate(search_results[index], true) nav.navigate(search_results[index], true)
clear_search(false) clear_search(false)
} }
const window_keydown = (e) => { const window_keydown = (e: KeyboardEvent) => {
if (e.ctrlKey || e.altKey || e.metaKey) { if (e.ctrlKey || e.altKey || e.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts return // prevent custom shortcuts from interfering with system shortcuts
} else if (document.activeElement.type && document.activeElement.type === "text") { } else if ((document.activeElement as any).type !== undefined && (document.activeElement as any).type === "text") {
return // Prevent shortcuts from interfering with input fields return // Prevent shortcuts from interfering with input fields
} }
@@ -171,7 +172,7 @@ const window_keydown = (e) => {
/> />
{#if search_term !== ""} {#if search_term !== ""}
<!-- Button needs to be of button type in order to not submit the form --> <!-- Button needs to be of button type in order to not submit the form -->
<button on:click={clear_search} type="button"> <button on:click={() => clear_search(false)} type="button">
<i class="icon">close</i> <i class="icon">close</i>
</button> </button>
{/if} {/if}

View File

@@ -1,16 +1,28 @@
<script> <script lang="ts" context="module">
export type UploadJob = {
task_id: number,
file: File,
path: string,
component: UploadProgress,
status: string,
total_size: number,
loaded_size: number,
};
</script>
<script lang="ts">
import { tick } from "svelte"; import { tick } from "svelte";
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import UploadProgress from "./UploadProgress.svelte"; import UploadProgress from "./UploadProgress.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
const max_concurrent_uploads = 5 const max_concurrent_uploads = 5
let file_input_field let file_input_field: HTMLInputElement
let file_input_change = (e) => { let file_input_change = (e: Event) => {
// Start uploading the files async // Start uploading the files async
upload_files(e.target.files) upload_files((e.target as HTMLInputElement).files)
// This resets the file input field // This resets the file input field
file_input_field.nodeValue = "" file_input_field.nodeValue = ""
@@ -20,10 +32,10 @@ export const pick_files = () => {
} }
let visible = false let visible = false
let upload_queue = []; let upload_queue: UploadJob[] = [];
let task_id_counter = 0 let task_id_counter = 0
export const upload_files = async files => { export const upload_files = async (files: File[]|FileList) => {
if (files.length === 0) { if (files.length === 0) {
return return
} else if (nav.base.type !== "dir") { } else if (nav.base.type !== "dir") {
@@ -37,7 +49,7 @@ export const upload_files = async files => {
} }
} }
export const upload_file = async file => { export const upload_file = async (file: File) => {
if (nav.base.type !== "dir") { if (nav.base.type !== "dir") {
alert("Can only upload to directory") alert("Can only upload to directory")
return return
@@ -122,7 +134,7 @@ const finish_upload = () => {
start_upload() start_upload()
} }
const leave_confirmation = (e) => { const leave_confirmation = (e: BeforeUnloadEvent) => {
if (state === "uploading") { if (state === "uploading") {
e.preventDefault() e.preventDefault()
return "If you close this page your files will stop uploading. Do you want to continue?" return "If you close this page your files will stop uploading. Do you want to continue?"

View File

@@ -9,7 +9,7 @@
// //
// on_error is called when the upload has failed. The parameters are the error // on_error is called when the upload has failed. The parameters are the error
import { fs_path_url, type GenericResponse } from "filesystem/FilesystemAPI.mjs" import { fs_path_url, type GenericResponse } from "filesystem/FilesystemAPI"
// code and an error message // code and an error message
export const upload_file = ( export const upload_file = (

View File

@@ -1,22 +1,18 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import { upload_file } from "./UploadFunc"; import { upload_file } from "./UploadFunc";
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
import Button from "layout/Button.svelte" import Button from "layout/Button.svelte"
import type { UploadJob } from "./FSUploadWidget.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let job = { export let job: UploadJob
file: null,
path: "",
name: "",
status: "",
}
export let total = 0 export let total = 0
export let loaded = 0 export let loaded = 0
let error_code = "" let error_code = ""
let error_message = "" let error_message = ""
let xhr = null let xhr: XMLHttpRequest = null
export const start = () => { export const start = () => {
xhr = upload_file( xhr = upload_file(

View File

@@ -1,5 +1,7 @@
<script> <script lang="ts">
export let nav import type { FSNavigator } from "filesystem/FSNavigator";
export let nav: FSNavigator
export let path = "" export let path = ""
</script> </script>

View File

@@ -1,11 +1,12 @@
<script> <script lang="ts">
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { fs_path_url, fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI.mjs" import { fs_path_url, fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
import FileTitle from "layout/FileTitle.svelte"; import FileTitle from "layout/FileTitle.svelte";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import type { FSNavigator } from 'filesystem/FSNavigator';
export let nav export let nav: FSNavigator
let player let player: HTMLAudioElement
let playing = false let playing = false
let media_session = false let media_session = false
let siblings = [] let siblings = []
@@ -13,7 +14,7 @@ let siblings = []
export const toggle_playback = () => playing ? player.pause() : player.play() export const toggle_playback = () => playing ? player.pause() : player.play()
export const toggle_mute = () => player.muted = !player.muted export const toggle_mute = () => player.muted = !player.muted
export const seek = delta => { export const seek = (delta: number) => {
// fastseek can be pretty imprecise, so we don't use it for small seeks // fastseek can be pretty imprecise, so we don't use it for small seeks
// below 5 seconds // below 5 seconds
if (player.fastSeek && delta > 5) { if (player.fastSeek && delta > 5) {
@@ -40,7 +41,7 @@ onMount(() => {
media_session = true media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play()); navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause()); navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.stop()); navigator.mediaSession.setActionHandler('stop', () => player.pause());
navigator.mediaSession.setActionHandler('previoustrack', () => nav.open_sibling(-1)); navigator.mediaSession.setActionHandler('previoustrack', () => nav.open_sibling(-1));
navigator.mediaSession.setActionHandler('nexttrack', () => nav.open_sibling(1)); navigator.mediaSession.setActionHandler('nexttrack', () => nav.open_sibling(1));
} }
@@ -56,8 +57,8 @@ onMount(() => {
bind:this={player} bind:this={player}
class="player" class="player"
src={fs_path_url($nav.base.path)} src={fs_path_url($nav.base.path)}
autoplay="autoplay" autoplay
controls="controls" controls
on:pause={() => playing = false } on:pause={() => playing = false }
on:play={() => playing = true } on:play={() => playing = true }
on:ended={() => nav.open_sibling(1) }> on:ended={() => nav.open_sibling(1) }>

View File

@@ -1,8 +1,8 @@
<script> <script lang="ts">
export let path = [] export let path = []
let image_uri let image_uri: string
let image_link let image_link: string
$: update_links(path) $: update_links(path)
const update_links = (path) => { const update_links = (path) => {
image_uri = null image_uri = null

View File

@@ -1,13 +1,14 @@
<script> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import { fs_thumbnail_url } from "filesystem/FilesystemAPI.mjs"; import { fs_thumbnail_url } from "filesystem/FilesystemAPI";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import { formatDataVolume, formatDate } from "util/Formatting.svelte"; import { formatDataVolume, formatDate } from "util/Formatting";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
</script> </script>
<slot></slot> <slot></slot>

View File

@@ -1,7 +1,7 @@
<script> <script lang="ts">
import { onMount, tick } from "svelte"; import { onMount, tick } from "svelte";
import Spinner from "util/Spinner.svelte"; import Spinner from "util/Spinner.svelte";
import { fs_node_type, fs_thumbnail_url } from "filesystem/FilesystemAPI.mjs"; import { fs_node_type, fs_thumbnail_url } from "filesystem/FilesystemAPI";
import FileManager from "filesystem/filemanager/FileManager.svelte"; import FileManager from "filesystem/filemanager/FileManager.svelte";
import Audio from "./Audio.svelte"; import Audio from "./Audio.svelte";
import File from "./File.svelte"; import File from "./File.svelte";
@@ -12,14 +12,17 @@ import Video from "./Video.svelte";
import Torrent from "./Torrent.svelte"; import Torrent from "./Torrent.svelte";
import Zip from "./Zip.svelte"; import Zip from "./Zip.svelte";
import CustomBanner from "./CustomBanner.svelte"; import CustomBanner from "./CustomBanner.svelte";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
import SlowDown from "layout/SlowDown.svelte"; import SlowDown from "layout/SlowDown.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
export let nav export let nav: FSNavigator
export let upload_widget export let upload_widget: FsUploadWidget
export let edit_window export let edit_window: EditWindow
let viewer let viewer: any
let viewer_type = "" let viewer_type = ""
let last_path = "" let last_path = ""
@@ -64,7 +67,7 @@ export const toggle_fullscreen = () => {
} }
return false return false
} }
export const seek = delta => { export const seek = (delta: number) => {
if (viewer && viewer.seek) { if (viewer && viewer.seek) {
viewer.seek(delta) viewer.seek(delta)
} }

View File

@@ -1,12 +1,13 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { swipe_nav } from "lib/SwipeNavigate.mjs"; import { swipe_nav } from "lib/SwipeNavigate";
import { fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_path_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher(); let dispatch = createEventDispatcher();
export let nav export let nav: FSNavigator
let container let container: HTMLDivElement
let zoom = false let zoom = false
let x = 0, y = 0 let x = 0, y = 0
let dragging = false let dragging = false
@@ -30,7 +31,7 @@ export const update = async () => {
const on_load = () => dispatch("loading", false) const on_load = () => dispatch("loading", false)
const mousedown = (e) => { const mousedown = (e: MouseEvent) => {
if (!dragging && e.which === 1 && zoom) { if (!dragging && e.which === 1 && zoom) {
x = e.pageX x = e.pageX
y = e.pageY y = e.pageY
@@ -41,7 +42,7 @@ const mousedown = (e) => {
return false return false
} }
} }
const mousemove = (e) => { const mousemove = (e: MouseEvent) => {
if (dragging) { if (dragging) {
container.scrollLeft = container.scrollLeft - (e.pageX - x) container.scrollLeft = container.scrollLeft - (e.pageX - x)
container.scrollTop = container.scrollTop - (e.pageY - y) container.scrollTop = container.scrollTop - (e.pageY - y)
@@ -54,7 +55,7 @@ const mousemove = (e) => {
return false return false
} }
} }
const mouseup = (e) => { const mouseup = (e: MouseEvent) => {
if (dragging) { if (dragging) {
dragging = false dragging = false
@@ -71,9 +72,13 @@ const mouseup = (e) => {
bind:this={container} bind:this={container}
class="container" class="container"
class:zoom class:zoom
use:swipe_nav={{enabled: !zoom, prev: swipe_prev, next: swipe_next}} use:swipe_nav={{
on:prev={() => nav.open_sibling(-1)} enabled: !zoom,
on:next={() => nav.open_sibling(1)} prev: swipe_prev,
next: swipe_next,
on_prev: () => nav.open_sibling(-1),
on_next: () => nav.open_sibling(1),
}}
> >
<!-- svelte-ignore a11y-no-noninteractive-element-interactions --> <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<img <img

View File

@@ -1,7 +1,8 @@
<script> <script lang="ts">
import { fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_path_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
</script> </script>
<iframe <iframe

View File

@@ -1,14 +1,15 @@
<script> <script lang="ts">
import { tick } from "svelte"; import { tick } from "svelte";
import { fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_path_url, type FSNode } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav export let nav: FSNavigator
let text_type = "text" let text_type = "text"
export const update = () => { export const update = () => {
console.debug("Loading text file", nav.base.name) console.debug("Loading text file", nav.base.name)
if (nav.base.size > 1 << 21) { // File larger than 2 MiB if (nav.base.file_size > 1 << 21) { // File larger than 2 MiB
text_pre.innerText = "File is too large to view online.\nPlease download and view it locally." text_pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return return
} }
@@ -24,8 +25,8 @@ export const update = () => {
} }
} }
let text_pre let text_pre: HTMLPreElement
const text = async file => { const text = async (file: FSNode) => {
text_type = "text" text_type = "text"
await tick() await tick()
@@ -41,8 +42,8 @@ const text = async file => {
}) })
} }
let md_container let md_container: HTMLElement
const markdown = async file => { const markdown = async (file: FSNode) => {
text_type = "markdown" text_type = "markdown"
await tick() await tick()

View File

@@ -1,16 +1,31 @@
<script> <script lang="ts" context="module">
export type TorrentInfo = {
trackers: string[]
comment: string,
created_by: string,
created_at: string,
info_hash: string,
files: TorrentFile
};
export type TorrentFile = {
size: number,
children?: {[index: string]: TorrentFile}
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import Magnet from "icons/Magnet.svelte"; import Magnet from "icons/Magnet.svelte";
import { formatDate } from "util/Formatting.svelte" import { formatDate } from "util/Formatting"
import TorrentItem from "./TorrentItem.svelte" import TorrentItem from "./TorrentItem.svelte"
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_node_icon, fs_path_url } from "filesystem/FilesystemAPI";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
let status = "loading" let status = "loading"
@@ -48,15 +63,7 @@ export const update = async () => {
} }
} }
let torrent = { let torrent: TorrentInfo = {} as TorrentInfo
trackers: [],
comment: "",
created_by: "",
created_at: "",
info_hash: "",
files: null,
}
let magnet = "" let magnet = ""
</script> </script>

View File

@@ -1,10 +1,8 @@
<script> <script lang="ts">
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import type { TorrentFile } from "./Torrent.svelte";
export let item = { export let item: TorrentFile = {} as TorrentFile
size: 0,
children: null,
}
</script> </script>
<ul class="list_open"> <ul class="list_open">

View File

@@ -1,16 +1,17 @@
<script> <script lang="ts">
import { onMount, createEventDispatcher, tick } from "svelte"; import { onMount, createEventDispatcher, tick } from "svelte";
import { video_position } from "lib/VideoPosition.mjs"; import { video_position } from "lib/VideoPosition";
import { fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_path_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
// Used to detect when the file path changes // Used to detect when the file path changes
let last_path = "" let last_path = ""
let loaded = false let loaded = false
let player let player: HTMLVideoElement
let playing = false let playing = false
let media_session = false let media_session = false
let loop = false let loop = false
@@ -64,13 +65,13 @@ onMount(() => {
media_session = true media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play()); navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause()); navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.stop()); navigator.mediaSession.setActionHandler('stop', () => player.pause());
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("open_sibling", -1)); navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("open_sibling", -1));
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("open_sibling", 1)); navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("open_sibling", 1));
} }
}) })
const video_keydown = e => { const video_keydown = (e: KeyboardEvent) => {
if (e.key === " ") { if (e.key === " ") {
// Prevent spacebar from pausing playback in Chromium. This conflicts // Prevent spacebar from pausing playback in Chromium. This conflicts
// with our own global key handler, causing the video to immediately // with our own global key handler, causing the video to immediately
@@ -78,7 +79,6 @@ const video_keydown = e => {
e.stopPropagation() e.stopPropagation()
} }
} }
</script> </script>
<div class="container"> <div class="container">

View File

@@ -1,21 +1,28 @@
<script> <script lang="ts" context="module">
export type ZipEntry = {
size: number,
children?: {[index: string]: ZipEntry},
properties?: string[],
download_url?: string, // Added by us
details_open?: boolean, // Added by us
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDate } from "util/Formatting.svelte" import { formatDataVolume, formatDate } from "util/Formatting"
import ZipItem from "file_viewer/viewers/ZipItem.svelte"; import ZipItem from "file_viewer/viewers/ZipItem.svelte";
import IconBlock from "layout/IconBlock.svelte"; import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte" import TextBlock from "layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "filesystem/FilesystemAPI.mjs"; import { fs_node_icon, fs_path_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav: FSNavigator
let status = "loading" let status = "loading"
let zip = { let zip: ZipEntry = {size: 0} as ZipEntry
size: 0,
children: [],
}
let uncomp_size = 0 let uncomp_size = 0
let comp_ratio = 0 let comp_ratio = 0
let archive_type = "" let archive_type = ""
@@ -38,7 +45,7 @@ export const update = async () => {
return return
} }
zip = await resp.json() zip = await resp.json() as ZipEntry
// Check if the zip has the property which allows separate files to be // Check if the zip has the property which allows separate files to be
// downloaded. If so then we set the download URL for each file // downloaded. If so then we set the download URL for each file
@@ -61,25 +68,23 @@ export const update = async () => {
} }
} }
const recursive_set_url = (parent_path, file) => { const recursive_set_url = (parent_path: string, file: ZipEntry) => {
file.download_url = parent_path file.download_url = parent_path
if (file.children) { if (file.children !== undefined) {
Object.entries(file.children).forEach(child => { for (const [name, child] of Object.entries(file.children)) {
recursive_set_url(file.download_url + "/" + child[0], child[1]) recursive_set_url(file.download_url + "/" + name, child)
}); }
} }
} }
const recursive_size = (file) => { const recursive_size = (file: ZipEntry) => {
let size = file.size let size = file.size
// If the file has children (array is iterable) we call this function on all if (file.children !== undefined) {
// the children and add the size to our size accumulator for (const v of Object.values(file.children)) {
if (file.children.forEach) { size += recursive_size(v)
file.children.forEach(child => { }
size += recursive_size(child)
});
} }
// Return the total size of this file and all its children // Return the total size of this file and all its children

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import Expandable from "util/Expandable.svelte"; import Expandable from "util/Expandable.svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
let result = null; let result = null;

View File

@@ -1,6 +1,6 @@
<script> <script>
import { add_upload_history, domain_url } from "util/Util.svelte" import { add_upload_history, domain_url } from "util/Util.svelte"
import { formatDataVolume, formatDuration } from "util/Formatting.svelte" import { formatDataVolume, formatDuration } from "util/Formatting"
import Spinner from "util/Spinner.svelte"; import Spinner from "util/Spinner.svelte";
export let job = {} export let job = {}

View File

@@ -1,5 +1,5 @@
<script> <script>
import { formatDataVolume, formatDuration } from "util/Formatting.svelte"; import { formatDataVolume, formatDuration } from "util/Formatting";
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
export let upload_queue = [] export let upload_queue = []

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
export let highlight = false; export let highlight = false;
export let highlight_on_click = false export let highlight_on_click = false
export let red = false; export let red = false;
@@ -11,12 +11,12 @@ export let label = ""
export let title = null export let title = null
export let link_href = "" export let link_href = ""
export let link_target = "_self" export let link_target = "_self"
export let click = e => {} export let click: (e?: MouseEvent) => void = null
export let style = null export let style = null
export let type = null export let type = null
export let form = null export let form = null
let click_int = e => { let click_int = (e: MouseEvent) => {
if (highlight_on_click) { if (highlight_on_click) {
try { try {
click(e) click(e)
@@ -25,7 +25,7 @@ let click_int = e => {
red = true red = true
throw err throw err
} }
} else { } else if (click !== null) {
click(e) click(e)
} }
} }
@@ -43,7 +43,7 @@ let click_int = e => {
style={style} style={style}
type={type} type={type}
form={form} form={form}
disabled={disabled ? "disabled":null} disabled={disabled ? true:null}
> >
{#if icon !== ""} {#if icon !== ""}
<i class="icon" class:small={icon_small}>{icon}</i> <i class="icon" class:small={icon_small}>{icon}</i>
@@ -63,7 +63,6 @@ let click_int = e => {
class:flat class:flat
title={title} title={title}
style={style} style={style}
disabled={disabled ? "disabled":null}
> >
{#if icon !== ""} {#if icon !== ""}
<i class="icon" class:small={icon_small}>{icon}</i> <i class="icon" class:small={icon_small}>{icon}</i>

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
import { copy_text } from "util/Util.svelte"; import { copy_text } from "util/Util.svelte";
export let text = "" export let text = ""

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
export let title = "" export let title = ""
</script> </script>

View File

@@ -1,11 +1,12 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import Discord from "icons/Discord.svelte"; import Discord from "icons/Discord.svelte";
import Github from "icons/Github.svelte"; import Github from "icons/Github.svelte";
import Mastodon from "icons/Mastodon.svelte"; import Mastodon from "icons/Mastodon.svelte";
import Patreon from "icons/Patreon.svelte"; import Patreon from "icons/Patreon.svelte";
import Reddit from "icons/Reddit.svelte"; import Reddit from "icons/Reddit.svelte";
import { formatDataVolumeBits } from "util/Formatting.svelte"; import { formatDataVolumeBits } from "util/Formatting";
import { get_endpoint, get_hostname } from "lib/PixeldrainAPI";
export let nobg = false export let nobg = false
let server_tx = 0 let server_tx = 0
@@ -13,7 +14,7 @@ let cache_tx = 0
let storage_tx = 0 let storage_tx = 0
onMount(async () => { onMount(async () => {
try { try {
const resp = await fetch(window.api_endpoint+"/misc/cluster_speed") const resp = await fetch(get_endpoint()+"/misc/cluster_speed")
if (resp.status >= 400) { if (resp.status >= 400) {
throw Error(await resp.text()) throw Error(await resp.text())
} }
@@ -59,7 +60,7 @@ onMount(async () => {
</div> </div>
<br/> <br/>
<span class="small_footer_text" style="font-size: .75em; line-height: .75em;"> <span class="small_footer_text" style="font-size: .75em; line-height: .75em;">
page rendered by {window.server_hostname} page rendered by {get_hostname()}
</span> </span>
</div> </div>
</footer> </footer>

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
export let toggle = false; export let toggle = false;
</script> </script>

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
export let icon_href = "" export let icon_href = ""
export let width = "750px" export let width = "750px"
</script> </script>

View File

@@ -1,5 +1,7 @@
<script> <script lang="ts">
let nav; import { user } from "lib/UserStore";
let nav: HTMLElement;
export const toggle = () => { export const toggle = () => {
var body = document.getElementById("page_body"); var body = document.getElementById("page_body");
@@ -24,11 +26,11 @@ export const reset = () => {
<a href="/#">Home</a> <a href="/#">Home</a>
<a href="/#prepaid">For Creators</a> <a href="/#prepaid">For Creators</a>
<hr /> <hr />
{#if window.user !== ""} {#if $user.username !== ""}
<a href="/user">{window.user.username}</a> <a href="/user">{$user.username}</a>
<a href="/user/filemanager#files">My Files</a> <a href="/user/filemanager#files">My Files</a>
<a href="/user/filemanager#lists">My Albums</a> <a href="/user/filemanager#lists">My Albums</a>
{#if window.user.is_admin} {#if $user.is_admin}
<a href="/user/buckets">Buckets</a> <a href="/user/buckets">Buckets</a>
<a href="/admin">Admin Panel</a> <a href="/admin">Admin Panel</a>
{/if} {/if}

View File

@@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDuration } from "util/Formatting.svelte"; import { formatDataVolume, formatDuration } from "util/Formatting";
import { stats } from "lib/StatsSocket.mjs" import { stats } from "lib/StatsSocket"
import TextBlock from "./TextBlock.svelte" import TextBlock from "./TextBlock.svelte"
import IconBlock from "./IconBlock.svelte"; import IconBlock from "./IconBlock.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -48,12 +48,18 @@ export const get_endpoint = () => {
if ((window as any).api_endpoint !== undefined) { if ((window as any).api_endpoint !== undefined) {
return (window as any).api_endpoint as string return (window as any).api_endpoint as string
} }
console.warn("api_endpoint property is not defined on window") console.warn("api_endpoint property is not defined on window")
return "/api" return "/api"
} }
export const get_hostname = () => {
if ((window as any).server_hostname !== undefined) {
return (window as any).server_hostname as string
}
console.warn("server_hostname property is not defined on window")
return "undefined"
}
export const check_response = async (resp: Response) => { export const check_response = async (resp: Response) => {
let text = await resp.text() let text = await resp.text()
if (resp.status >= 400) { if (resp.status >= 400) {

View File

@@ -3,7 +3,16 @@ const swipe_inital_offset = 25
// Amount of pixels after which the navigation triggers // Amount of pixels after which the navigation triggers
const swipe_trigger_offset = 75 const swipe_trigger_offset = 75
export const swipe_nav = (node: HTMLElement, props: { enabled: boolean, prev: boolean, next: boolean }) => { export const swipe_nav = (
node: HTMLElement,
props: {
enabled: boolean,
prev: boolean,
next: boolean,
on_prev: () => void,
on_next: () => void,
},
) => {
let start_x = 0 let start_x = 0
let start_y = 0 let start_y = 0
let render_offset = 0 let render_offset = 0
@@ -43,10 +52,10 @@ export const swipe_nav = (node: HTMLElement, props: { enabled: boolean, prev: bo
if (render_offset > swipe_trigger_offset) { if (render_offset > swipe_trigger_offset) {
set_offset(1000, true) set_offset(1000, true)
node.dispatchEvent(new CustomEvent("prev")) props.on_prev()
} else if (render_offset < -swipe_trigger_offset) { } else if (render_offset < -swipe_trigger_offset) {
set_offset(-1000, true) set_offset(-1000, true)
node.dispatchEvent(new CustomEvent("next")) props.on_next()
} else { } else {
set_offset(0, true) set_offset(0, true)
} }

View File

@@ -0,0 +1,13 @@
import { writable } from "svelte/store";
import { get_user, type Subscription, type User } from "./PixeldrainAPI";
export const user = writable(
{ subscription: {} as Subscription } as User,
(set: (value: User) => void) => {
get_user().then((u: User) => {
set(u)
}).catch((err: any) => {
alert("Could not fetch user:\n" + JSON.stringify(err))
})
}
)

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import Form, { type FormConfig } from "util/Form.svelte" import Form, { type FormConfig } from "util/Form.svelte"
import { check_response, get_endpoint } from "lib/PixeldrainAPI.mjs"; import { check_response, get_endpoint } from "lib/PixeldrainAPI";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,8 +1,8 @@
<script> <script lang="ts">
import Login from "./Login.svelte"; import Login from "./Login.svelte";
import Register from "./Register.svelte"; import Register from "./Register.svelte";
const finish_login = async e => { const finish_login = async () => {
location.reload() location.reload()
} }

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Form, { type FormConfig } from "util/Form.svelte" import Form, { type FormConfig } from "util/Form.svelte"
import { get_endpoint } from "lib/PixeldrainAPI.mjs"; import { get_endpoint } from "lib/PixeldrainAPI";
let form: FormConfig = { let form: FormConfig = {
fields: [ fields: [

View File

@@ -1,9 +1,9 @@
<script> <script lang="ts">
import TabMenu from "util/TabMenu.svelte"; import TabMenu from "util/TabMenu.svelte";
import Register from "./Register.svelte"; import Register from "./Register.svelte";
import Login from "./Login.svelte"; import Login from "./Login.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { get_user } from "lib/PixeldrainAPI.mjs"; import { get_user } from "lib/PixeldrainAPI";
let pages = [ let pages = [
{ {
@@ -33,4 +33,4 @@ onMount(async () => {
}) })
</script> </script>
<TabMenu pages={pages} title="Login" large_tabs /> <TabMenu pages={pages} title="Login" />

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import { formatDataVolume, formatDataVolumeBits } from "util/Formatting.svelte"; import { formatDataVolume, formatDataVolumeBits } from "util/Formatting";
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import Footer from "layout/Footer.svelte" import Footer from "layout/Footer.svelte"
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte" import LoadingIndicator from "util/LoadingIndicator.svelte"

View File

@@ -1,5 +1,5 @@
<script> <script>
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
let loading = false let loading = false

View File

@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
let loading = false let loading = false

View File

@@ -2,7 +2,7 @@
import { onMount } from "svelte"; import { onMount } from "svelte";
import Modal from "util/Modal.svelte"; import Modal from "util/Modal.svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import { get_user, put_user } from "lib/PixeldrainAPI.mjs"; import { get_user, put_user } from "lib/PixeldrainAPI";
// When the always flag is set then the pop-up will also show if the user // When the always flag is set then the pop-up will also show if the user
// already has an affiliate ID set // already has an affiliate ID set

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import Pro from "icons/Pro.svelte"; import Pro from "icons/Pro.svelte";
import { formatDataVolume } from "util/Formatting.svelte"; import { formatDataVolume } from "util/Formatting";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
import SuccessMessage from "util/SuccessMessage.svelte"; import SuccessMessage from "util/SuccessMessage.svelte";

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import Euro from "util/Euro.svelte"; import Euro from "util/Euro.svelte";
import { formatDate } from "util/Formatting.svelte"; import { formatDate } from "util/Formatting";
import LoadingIndicator from "util/LoadingIndicator.svelte"; import LoadingIndicator from "util/LoadingIndicator.svelte";
import MollieDeposit from "./MollieDeposit.svelte"; import MollieDeposit from "./MollieDeposit.svelte";

View File

@@ -1,6 +1,6 @@
<script> <script>
import ProgressBar from "util/ProgressBar.svelte"; import ProgressBar from "util/ProgressBar.svelte";
import { formatDataVolume } from "util/Formatting.svelte" import { formatDataVolume } from "util/Formatting"
export let total = 0 export let total = 0
export let used = 0 export let used = 0

View File

@@ -3,7 +3,7 @@ import { onMount } from "svelte";
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";
import ToggleButton from "layout/ToggleButton.svelte"; import ToggleButton from "layout/ToggleButton.svelte";
import { check_response, get_endpoint, get_user, type User } from "lib/PixeldrainAPI.mjs"; import { check_response, get_endpoint, get_user, type User } from "lib/PixeldrainAPI";
let user: User = null let user: User = null
let secret = "" let secret = ""

View File

@@ -14,7 +14,7 @@ import Dashboard from "./dashboard/Dashboard.svelte";
import AffiliatePrompt from "./AffiliatePrompt.svelte"; import AffiliatePrompt from "./AffiliatePrompt.svelte";
import FileManager from "./filemanager/FileManager.svelte"; import FileManager from "./filemanager/FileManager.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { get_user, type User } from "lib/PixeldrainAPI.mjs"; import { get_user, type User } from "lib/PixeldrainAPI";
let pages: Tab[] = [ let pages: Tab[] = [
{ {

Some files were not shown because too many files have changed in this diff Show More