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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
<script>
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 { color_by_name } from "util/Util.svelte";
import ServerDiagnostics from "./ServerDiagnostics.svelte";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<script>
import { onMount } from "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";
export let user_id = ""

View File

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

View File

@@ -1,6 +1,6 @@
<script>
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 Chart from "util/Chart.svelte";

View File

@@ -1,6 +1,6 @@
<script>
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 Modal from "util/Modal.svelte";
let dispatch = createEventDispatcher()

View File

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

View File

@@ -3,7 +3,7 @@ import { createEventDispatcher } from "svelte"
import { flip } from "svelte/animate"
import FilePicker from "./FilePicker.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"
let dispatch = createEventDispatcher()

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ import { createEventDispatcher } from "svelte";
import BandwidthUsage from "./BandwidthUsage.svelte";
import IconBlock from "layout/IconBlock.svelte";
import FileTitle from "layout/FileTitle.svelte";
import { formatDataVolume } from "util/Formatting.svelte";
import { formatDataVolume } from "util/Formatting";
let dispatch = createEventDispatcher()
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 RateLimit from "./RateLimit.svelte";
import Torrent from "./Torrent.svelte";
import { stats } from "lib/StatsSocket.mjs"
import { stats } from "lib/StatsSocket"
import Zip from "./Zip.svelte";
import SlowDown from "layout/SlowDown.svelte";
import TextBlock from "layout/TextBlock.svelte";

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<script>
import { createEventDispatcher } from "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 TextBlock from "layout/TextBlock.svelte";
import TorrentItem from "./TorrentItem.svelte"

View File

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

View File

@@ -1,6 +1,6 @@
<script>
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 IconBlock from "layout/IconBlock.svelte";
let dispatch = createEventDispatcher()

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
<script>
<script lang="ts">
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 { fs_path_url, fs_timeseries } from "./FilesystemAPI.mjs";
import { generate_share_path, generate_share_url } from "./Sharebar.svelte";
import { fs_path_url, fs_share_path, fs_share_url, fs_timeseries, type FSNode } from "./FilesystemAPI";
import { color_by_name } from "util/Util.svelte";
import { tick } from "svelte";
import CopyButton from "layout/CopyButton.svelte";
import type { FSNavigator } from "./FSNavigator";
export let nav
export let nav: FSNavigator
export let visible = false
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) : ""
$: share_url = generate_share_url($nav.path)
$: direct_share_url = $nav.base.path ? window.location.origin+fs_path_url(generate_share_path($nav.path)) : ""
$: share_url = fs_share_url($nav.path)
$: direct_share_url = $nav.base.path ? window.location.origin+fs_path_url(fs_share_path($nav.path)) : ""
let chart
let chart_timespan = 0
@@ -40,7 +40,7 @@ let total_downloads = 0
let total_transfer = 0
$: 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) {
// Wait for the chart element to render, if it's not rendered already
await tick()
@@ -94,7 +94,6 @@ let update_chart = async (base, timespan, interval) => {
},
}
resp.downloads.timestamps.forEach((val, idx) => {
let date = new Date(val);
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 type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI.mjs";
import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI";
import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI";
import type { Writable } from "svelte/store"
export class FSNavigator {

View File

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

View File

@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { onMount } from "svelte";
import LoadingIndicator from "util/LoadingIndicator.svelte";
import EditWindow from "./edit_window/EditWindow.svelte";
@@ -7,21 +7,21 @@ import Breadcrumbs from "./Breadcrumbs.svelte";
import DetailsWindow from "./DetailsWindow.svelte";
import FilePreview from "./viewers/FilePreview.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 { FSNavigator } from "./FSNavigator"
import { writable } from "svelte/store";
import TransferLimit from "file_viewer/TransferLimit.svelte";
import { stats } from "lib/StatsSocket.mjs"
import { css_from_path } from "filesystem/edit_window/Branding.js";
import { stats } from "lib/StatsSocket"
import { css_from_path } from "filesystem/edit_window/Branding";
import AffiliatePrompt from "user_home/AffiliatePrompt.svelte";
let file_viewer
let file_preview
let toolbar
let upload_widget
let file_viewer: HTMLDivElement
let file_preview: FilePreview
let toolbar: Toolbar
let upload_widget: FSUploadWidget
let details_visible = false
let edit_window
let edit_window: EditWindow
let edit_visible = false
const loading = writable(true)
@@ -29,7 +29,7 @@ const nav = new FSNavigator(true)
onMount(() => {
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
// 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) {
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
}
@@ -127,7 +127,7 @@ const keydown = e => {
};
const download = () => {
let a = document.createElement("a")
const a = document.createElement("a")
if (nav.base.type === "file") {
a.href = fs_path_url(nav.base.path) + "?attach"

View File

@@ -24,18 +24,38 @@ export type FSNode = {
mode_octal: string,
created_by: string,
abuse_type: string | undefined,
abuse_report_time: string | undefined,
abuse_type?: string,
abuse_report_time?: string,
file_size: number,
file_type: string,
sha256_sum: string,
id: string | undefined,
properties: {} | undefined,
link_permissions: FSPermissions | undefined,
user_permissions: [string: FSPermissions] | undefined,
password_permissions: [string: FSPermissions] | undefined,
id?: string,
properties?: FSNodeProperties,
link_permissions?: FSPermissions,
user_permissions?: { [index: string]: FSPermissions },
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 = {
@@ -53,38 +73,26 @@ export type FSContext = {
// ==============
export type NodeOptions = {
mode: number | undefined,
created: string | undefined,
modified: string | undefined,
shared: boolean | undefined,
mode?: number,
created?: string,
modified?: string,
shared?: boolean,
// Permissions
link_permissions: FSPermissions | undefined,
user_permissions: FSPermissions | undefined,
password_permissions: FSPermissions | undefined,
// 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,
}
link_permissions?: FSPermissions,
user_permissions?: { [index: string]: FSPermissions },
password_permissions?: { [index: string]: FSPermissions },
} & FSNodeProperties
// API methods
// ===========
// 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()
form.append("action", "mkdir")
if (opts && opts.mode) {
if (opts !== undefined && opts.mode !== undefined) {
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) +
"&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) => {
@@ -181,7 +199,7 @@ export const fs_timeseries = async (path: string, start: Date, end: Date, interv
"&end=" + end.toISOString() +
"&interval=" + interval
)
)
) as NodeTimeSeries
}
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) => {
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 Button from "layout/Button.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 dialog
let button: HTMLButtonElement
let dialog: HTMLDialogElement
export let no_login_label = "Pixeldrain"
@@ -40,7 +41,7 @@ const open = () => {
}
// Close the dialog when the user clicks the background
const click = e => {
const click = (e: MouseEvent) => {
if (e.target === dialog) {
dialog.close()
}
@@ -53,7 +54,7 @@ const click = e => {
<PixeldrainLogo style="height: 1.6em; width: 1.6em;"/>
{/if}
<span class="button_username" class:hide_name>
{window.user.username === "" ? no_login_label : window.user.username}
{$user.username === "" ? no_login_label : $user.username}
</span>
</button>
</div>
@@ -62,28 +63,28 @@ const click = e => {
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<dialog bind:this={dialog} on:click={click}>
<div class="menu">
{#if window.user.username !== ""}
{#if $user.username !== undefined && $user.username !== ""}
<Button link_href="/user" link_target={target} icon="dashboard" label="Dashboard" />
<div class="separator"></div>
<div class="stats_table">
<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><Euro amount={window.user.balance_micro_eur}/></div>
<div><Euro amount={$user.balance_micro_eur}/></div>
{/if}
<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>{formatDataVolume(window.user.monthly_transfer_used, 3)}</div>
<div>{formatDataVolume($user.monthly_transfer_used, 3)}</div>
</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"/>
{:else}
<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/prepaid/transactions" link_target={target} icon="receipt" label="Transactions"/>
{#if window.user.is_admin}
{#if $user.is_admin}
<div class="separator"></div>
<Button link_href="/admin" link_target={target} icon="admin_panel_settings" label="Admin Panel"/>
{/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 { generate_share_url } from "./Sharebar.svelte";
import { copy_text } from "util/Util.svelte";
import FileStats from "./FileStats.svelte";
import type { FSNavigator } from "./FSNavigator";
import EditWindow from "./edit_window/EditWindow.svelte";
import FilePreview from "./viewers/FilePreview.svelte";
import { fs_share_url } from "./FilesystemAPI";
let dispatch = createEventDispatcher()
export let nav
export let nav: FSNavigator
export let details_visible = false
export let edit_window
export let edit_window: EditWindow
export let edit_visible = false
export let file_viewer
export let file_preview
export let file_viewer: HTMLDivElement
export let file_preview: FilePreview
$: share_url = generate_share_url($nav.path)
$: share_url = fs_share_url($nav.path)
let link_copied = false
export const copy_link = () => {
if (share_url === "") {
@@ -60,7 +63,7 @@ export const toggle_fullscreen = () => {
}
let expanded = true
let expand = e => {
let expand = (e: Event) => {
e.preventDefault()
e.stopPropagation()
expanded = !expanded

View File

@@ -1,6 +1,6 @@
<script lang="ts">
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";
export let file: FSNode
@@ -10,7 +10,7 @@ let new_user_perms = <FSPermissions>{read: true}
const add_user = (e: SubmitEvent) => {
e.preventDefault()
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)
}
@@ -24,7 +24,7 @@ let new_password_perms = <FSPermissions>{read: true}
const add_password = (e: SubmitEvent) => {
e.preventDefault()
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)
}

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 hsl2rgb from "pure-color/convert/hsl2rgb";
import rgb2hex from "pure-color/convert/rgb2hex";

View File

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

View File

@@ -1,29 +1,24 @@
<script>
import { fs_rename, fs_update } from "filesystem/FilesystemAPI.mjs";
<script lang="ts">
import { fs_rename, fs_update, type FSNode, type FSNodeProperties, type NodeOptions } from "filesystem/FilesystemAPI";
import Modal from "util/Modal.svelte";
import BrandingOptions from "./BrandingOptions.svelte";
import { branding_from_node } from "./Branding";
import FileOptions from "./FileOptions.svelte";
import SharingOptions from "./SharingOptions.svelte";
import AccessControl from "./AccessControl.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav
let file = {
path: "",
name: "",
id: "",
mode_octal: "",
properties: {},
};
export let nav: FSNavigator
let file: FSNode = {} as FSNode
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
// should be opened after the user finishes editing and 3 is the default tab
// 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
open_after_edit = oae
if (open_tab !== "") {
@@ -38,12 +33,12 @@ export const edit = (f, oae = false, open_tab = "") => {
shared = !(file.id === undefined || file.id === "")
if (file.properties === undefined) {
file.properties = {}
file.properties = {} as FSNodeProperties
}
if (shared && file.link_permissions === undefined) {
// 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"
@@ -59,51 +54,31 @@ export const edit = (f, oae = false, open_tab = "") => {
let tab = "file"
let open_after_edit = false
let shared = false
let new_name = ""
let shared = 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) => {
console.debug("Saving file", file.path)
let new_file
let new_file: FSNode
try {
nav.set_loading(true)
let opts = {
shared: shared,
}
branding_enabled: JSON.stringify(branding_enabled)
} as NodeOptions
opts.branding_enabled = branding_enabled ? "true" : ""
if (branding_enabled && file.properties) {
for (let field of branding_fields) {
if (file.properties[field] !== undefined) {
console.log("setting", field, "to", file.properties[field])
opts[field] = file.properties[field]
}
if (branding_enabled && file.properties !== undefined) {
for (let field of Object.keys(file.properties)) {
console.log("setting", field, "to", file.properties[field])
opts[field] = file.properties[field]
}
}
if (shared && file.link_permissions !== undefined) {
if (shared) {
opts.link_permissions = file.link_permissions
}
if (shared && file.user_permissions !== undefined) {
opts.user_permissions = file.user_permissions
}
if (shared && file.password_permissions !== undefined) {
opts.password_permissions = file.password_permissions
}
@@ -179,11 +154,10 @@ const save = async (keep_editing = false) => {
{:else if tab === "share"}
<SharingOptions bind:file bind:shared on:save={() => save(true)} />
{:else if tab === "access"}
<AccessControl bind:file bind:shared />
<AccessControl bind:file />
{:else if tab === "branding"}
<BrandingOptions
bind:enabled={branding_enabled}
bind:colors={branding_colors}
bind: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 { fs_delete_all } from "filesystem/FilesystemAPI.mjs";
import { fs_delete_all, type FSNode } from "filesystem/FilesystemAPI";
import PathLink from "filesystem/util/PathLink.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav
export let file = {}
export let new_name
export let visible
export let open_after_edit
export let nav: FSNavigator
export let file: FSNode = {} as FSNode
export let new_name: string
export let visible: boolean
export let open_after_edit: boolean
$: is_root_dir = file.path === "/"+file.id
const delete_file = async e => {
const delete_file = async (e: MouseEvent) => {
e.preventDefault()
try {

View File

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

View File

@@ -1,20 +1,21 @@
<script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { domain_url } from "util/Util.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()
export let shared
export let file
export let shared: boolean
export let file: FSNode = {} as FSNode
let embed_html
let preview_area
let embed_html: string
let preview_area: HTMLDivElement
$: is_shared = file.id !== undefined && file.id !== ""
$: share_link = window.location.protocol+"//"+window.location.host+"/d/"+file.id
$: embed_iframe(file)
let embed_iframe = file => {
let embed_iframe = (file: FSNode) => {
if (!is_shared) {
example = false
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
// 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

View File

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

View File

@@ -1,10 +1,11 @@
<script>
<script lang="ts">
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()
export let nav
export let nav: FSNavigator
export let show_hidden = false
export let large_icons = false
</script>
@@ -13,8 +14,8 @@ export let large_icons = false
{#each $nav.children as child, index (child.path)}
<a
href={"/d"+fs_encode_path(child.path)}
on:click|preventDefault={() => dispatch("node_click", index)}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})}
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
class="node"
class:node_selected={child.fm_selected}
class:hidden={child.name.startsWith(".") && !show_hidden}
@@ -26,7 +27,7 @@ export let large_icons = false
{#if child.id}
<a
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"
>
<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 { fs_mkdir } from "filesystem/FilesystemAPI.mjs";
import { fs_mkdir } from "filesystem/FilesystemAPI";
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 error_msg = ""
let create_dir = async () => {

View File

@@ -1,13 +1,15 @@
<script>
<script lang="ts">
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
let file_picker
export let nav: FSNavigator
let file_picker: FilePicker
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)
let fileids = []

View File

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

View File

@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { createEventDispatcher, onMount } from "svelte"
import ListView from "./ListView.svelte"
import GalleryView from "./GalleryView.svelte"
@@ -6,19 +6,20 @@ import CompactView from "./CompactView.svelte"
import Modal from "util/Modal.svelte";
import LoadingIndicator from "util/LoadingIndicator.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 modal
let modal: Modal
let dispatch = createEventDispatcher()
let directory_view = ""
let loading = false
const loading_evt = e => loading = e.detail
let large_icons = false
let show_hidden = false
export let select_multiple = false
export const open = path => {
export const open = (path: string) => {
modal.show()
nav.navigate(path, false)
}
@@ -32,8 +33,8 @@ $: selected_files = $nav.children.reduce((acc, file) => {
// Navigation functions
const node_click = e => {
let index = e.detail
const node_click = (e: CustomEvent<FMNodeEvent>) => {
const index = e.detail.index
if (nav.children[index].type === "dir") {
nav.navigate(nav.children[index].path, true)
@@ -41,16 +42,15 @@ const node_click = e => {
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 (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) {
e.detail.event.preventDefault()
node_select({detail: e.detail.index})
e.detail.original.preventDefault()
node_select(e)
}
}
const node_select = e => {
let index = e.detail
mode = "selecting"
const node_select = (e: CustomEvent<FMNodeEvent>) => {
const index = e.detail.index
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
let shift_pressed = false
let last_selected_node = -1
const detect_shift = (e) => {
const detect_shift = (e: KeyboardEvent) => {
if (e.key === "Shift") {
shift_pressed = e.type === "keydown"
}
}
const select_node = index => {
const select_node = (index: number) => {
if (select_multiple && shift_pressed) {
// 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
@@ -101,7 +101,7 @@ const select_node = index => {
}
let done = () => {
let selected_files = []
let selected_files: FSNode[] = []
for (let i = 0; i < nav.children.length; i++) {
if (nav.children[i].fm_selected) {
selected_files.push(nav.children[i])

View File

@@ -1,9 +1,10 @@
<script>
<script lang="ts">
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()
export let nav
export let nav: FSNavigator
export let show_hidden = false
export let large_icons = false
</script>
@@ -12,8 +13,8 @@ export let large_icons = false
{#each $nav.children as child, index (child.path)}
<a class="file"
href={"/d"+fs_encode_path(child.path)}
on:click|preventDefault={() => dispatch("node_click", index)}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})}
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
class:selected={child.fm_selected}
class:hidden={child.name.startsWith(".") && !show_hidden}
class:large_icons

View File

@@ -1,11 +1,12 @@
<script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { formatDataVolume } from "util/Formatting.svelte";
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI.mjs"
import { formatDataVolume } from "util/Formatting";
import { fs_encode_path, fs_node_icon } from "filesystem/FilesystemAPI"
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
export let nav
export let nav: FSNavigator
export let show_hidden = false
export let large_icons = false
export let hide_edit = false
@@ -22,8 +23,8 @@ export let hide_branding = false
{#each $nav.children as child, index (child.path)}
<a
href={"/d"+fs_encode_path(child.path)}
on:click|preventDefault={() => dispatch("node_click", index)}
on:contextmenu={e => dispatch("node_context", {event: e, index: index})}
on:click|preventDefault={e => dispatch("node_click", {index: index, original: e})}
on:contextmenu={e => dispatch("node_context", {index: index, original: e})}
class="node"
class:node_selected={child.fm_selected}
class:hidden={child.name.startsWith(".") && !show_hidden}
@@ -46,19 +47,19 @@ export let hide_branding = false
{:else if child.id}
<a
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"
>
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
</a>
{/if}
{#if child.properties && child.properties.branding_enabled && !hide_branding}
<button class="action_button" on:click|preventDefault|stopPropagation={() => dispatch("node_branding", index)}>
{#if child.properties !== undefined && child.properties.branding_enabled !== undefined && !hide_branding}
<button class="action_button" on:click|preventDefault|stopPropagation={e => dispatch("node_branding", {index: index, original: e})}>
<i class="icon">palette</i>
</button>
{/if}
{#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>
</button>
{/if}

View File

@@ -1,13 +1,14 @@
<script>
<script lang="ts">
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 search_term = ""
let search_results = []
let search_results: string[] = []
let selected_result = 0
let searching = false
let last_searched_term = ""
@@ -71,7 +72,7 @@ const search = async (limit = 10) => {
}
}
const clear_search = (blur) => {
const clear_search = (blur: boolean) => {
error = ""
search_term = ""
search_results = []
@@ -89,12 +90,12 @@ const clear_search = (blur) => {
// Cursor navigation events can only be prevented with keydown. But we want to
// 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") {
e.preventDefault()
}
}
const input_keyup = e => {
const input_keyup = (e: KeyboardEvent) => {
if (e.key === "Escape") {
clear_search(true)
} 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)
clear_search(false)
}
const window_keydown = (e) => {
const window_keydown = (e: KeyboardEvent) => {
if (e.ctrlKey || e.altKey || e.metaKey) {
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
}
@@ -171,7 +172,7 @@ const window_keydown = (e) => {
/>
{#if search_term !== ""}
<!-- 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>
</button>
{/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 { fade } from "svelte/transition";
import UploadProgress from "./UploadProgress.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
export let nav
export let nav: FSNavigator
const max_concurrent_uploads = 5
let file_input_field
let file_input_change = (e) => {
let file_input_field: HTMLInputElement
let file_input_change = (e: Event) => {
// Start uploading the files async
upload_files(e.target.files)
upload_files((e.target as HTMLInputElement).files)
// This resets the file input field
file_input_field.nodeValue = ""
@@ -20,10 +32,10 @@ export const pick_files = () => {
}
let visible = false
let upload_queue = [];
let upload_queue: UploadJob[] = [];
let task_id_counter = 0
export const upload_files = async files => {
export const upload_files = async (files: File[]|FileList) => {
if (files.length === 0) {
return
} 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") {
alert("Can only upload to directory")
return
@@ -122,7 +134,7 @@ const finish_upload = () => {
start_upload()
}
const leave_confirmation = (e) => {
const leave_confirmation = (e: BeforeUnloadEvent) => {
if (state === "uploading") {
e.preventDefault()
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
import { fs_path_url, type GenericResponse } from "filesystem/FilesystemAPI.mjs"
import { fs_path_url, type GenericResponse } from "filesystem/FilesystemAPI"
// code and an error message
export const upload_file = (

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
<script>
<script lang="ts">
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 TextBlock from "layout/TextBlock.svelte"
import type { FSNavigator } from 'filesystem/FSNavigator';
export let nav
let player
export let nav: FSNavigator
let player: HTMLAudioElement
let playing = false
let media_session = false
let siblings = []
@@ -13,7 +14,7 @@ let siblings = []
export const toggle_playback = () => playing ? player.pause() : player.play()
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
// below 5 seconds
if (player.fastSeek && delta > 5) {
@@ -40,7 +41,7 @@ onMount(() => {
media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play());
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('nexttrack', () => nav.open_sibling(1));
}
@@ -56,8 +57,8 @@ onMount(() => {
bind:this={player}
class="player"
src={fs_path_url($nav.base.path)}
autoplay="autoplay"
controls="controls"
autoplay
controls
on:pause={() => playing = false }
on:play={() => playing = true }
on:ended={() => nav.open_sibling(1) }>

View File

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

View File

@@ -1,13 +1,14 @@
<script>
<script lang="ts">
import { createEventDispatcher } from "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 { formatDataVolume, formatDate } from "util/Formatting.svelte";
import { formatDataVolume, formatDate } from "util/Formatting";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
export let nav
export let nav: FSNavigator
</script>
<slot></slot>

View File

@@ -1,7 +1,7 @@
<script>
<script lang="ts">
import { onMount, tick } from "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 Audio from "./Audio.svelte";
import File from "./File.svelte";
@@ -12,14 +12,17 @@ import Video from "./Video.svelte";
import Torrent from "./Torrent.svelte";
import Zip from "./Zip.svelte";
import CustomBanner from "./CustomBanner.svelte";
import { stats } from "lib/StatsSocket.mjs"
import { stats } from "lib/StatsSocket"
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 upload_widget
export let edit_window
export let nav: FSNavigator
export let upload_widget: FsUploadWidget
export let edit_window: EditWindow
let viewer
let viewer: any
let viewer_type = ""
let last_path = ""
@@ -64,7 +67,7 @@ export const toggle_fullscreen = () => {
}
return false
}
export const seek = delta => {
export const seek = (delta: number) => {
if (viewer && viewer.seek) {
viewer.seek(delta)
}

View File

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

View File

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

View File

@@ -1,14 +1,15 @@
<script>
<script lang="ts">
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"
export const update = () => {
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."
return
}
@@ -24,8 +25,8 @@ export const update = () => {
}
}
let text_pre
const text = async file => {
let text_pre: HTMLPreElement
const text = async (file: FSNode) => {
text_type = "text"
await tick()
@@ -41,8 +42,8 @@ const text = async file => {
})
}
let md_container
const markdown = async file => {
let md_container: HTMLElement
const markdown = async (file: FSNode) => {
text_type = "markdown"
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 Magnet from "icons/Magnet.svelte";
import { formatDate } from "util/Formatting.svelte"
import { formatDate } from "util/Formatting"
import TorrentItem from "./TorrentItem.svelte"
import IconBlock from "layout/IconBlock.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 type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
export let nav
export let nav: FSNavigator
let status = "loading"
@@ -48,15 +63,7 @@ export const update = async () => {
}
}
let torrent = {
trackers: [],
comment: "",
created_by: "",
created_at: "",
info_hash: "",
files: null,
}
let torrent: TorrentInfo = {} as TorrentInfo
let magnet = ""
</script>

View File

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

View File

@@ -1,16 +1,17 @@
<script>
<script lang="ts">
import { onMount, createEventDispatcher, tick } from "svelte";
import { video_position } from "lib/VideoPosition.mjs";
import { fs_path_url } from "filesystem/FilesystemAPI.mjs";
import { video_position } from "lib/VideoPosition";
import { fs_path_url } from "filesystem/FilesystemAPI";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
export let nav
export let nav: FSNavigator
// Used to detect when the file path changes
let last_path = ""
let loaded = false
let player
let player: HTMLVideoElement
let playing = false
let media_session = false
let loop = false
@@ -64,13 +65,13 @@ onMount(() => {
media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play());
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('nexttrack', () => dispatch("open_sibling", 1));
}
})
const video_keydown = e => {
const video_keydown = (e: KeyboardEvent) => {
if (e.key === " ") {
// Prevent spacebar from pausing playback in Chromium. This conflicts
// with our own global key handler, causing the video to immediately
@@ -78,7 +79,6 @@ const video_keydown = e => {
e.stopPropagation()
}
}
</script>
<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 { formatDataVolume, formatDate } from "util/Formatting.svelte"
import { formatDataVolume, formatDate } from "util/Formatting"
import ZipItem from "file_viewer/viewers/ZipItem.svelte";
import IconBlock from "layout/IconBlock.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()
export let nav
export let nav: FSNavigator
let status = "loading"
let zip = {
size: 0,
children: [],
}
let zip: ZipEntry = {size: 0} as ZipEntry
let uncomp_size = 0
let comp_ratio = 0
let archive_type = ""
@@ -38,7 +45,7 @@ export const update = async () => {
return
}
zip = await resp.json()
zip = await resp.json() as ZipEntry
// 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
@@ -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
if (file.children) {
Object.entries(file.children).forEach(child => {
recursive_set_url(file.download_url + "/" + child[0], child[1])
});
if (file.children !== undefined) {
for (const [name, child] of Object.entries(file.children)) {
recursive_set_url(file.download_url + "/" + name, child)
}
}
}
const recursive_size = (file) => {
const recursive_size = (file: ZipEntry) => {
let size = file.size
// If the file has children (array is iterable) we call this function on all
// the children and add the size to our size accumulator
if (file.children.forEach) {
file.children.forEach(child => {
size += recursive_size(child)
});
if (file.children !== undefined) {
for (const v of Object.values(file.children)) {
size += recursive_size(v)
}
}
// Return the total size of this file and all its children

View File

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

View File

@@ -1,6 +1,6 @@
<script>
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";
export let job = {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,12 +48,18 @@ export const get_endpoint = () => {
if ((window as any).api_endpoint !== undefined) {
return (window as any).api_endpoint as string
}
console.warn("api_endpoint property is not defined on window")
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) => {
let text = await resp.text()
if (resp.status >= 400) {

View File

@@ -3,7 +3,16 @@ const swipe_inital_offset = 25
// Amount of pixels after which the navigation triggers
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_y = 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) {
set_offset(1000, true)
node.dispatchEvent(new CustomEvent("prev"))
props.on_prev()
} else if (render_offset < -swipe_trigger_offset) {
set_offset(-1000, true)
node.dispatchEvent(new CustomEvent("next"))
props.on_next()
} else {
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">
import { createEventDispatcher, onMount } from "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()

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<script>
import { onMount } from "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 CopyButton from "layout/CopyButton.svelte";

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
import { onMount } from "svelte";
import Modal from "util/Modal.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
// already has an affiliate ID set

View File

@@ -1,7 +1,7 @@
<script>
import { onMount } from "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 ProgressBar from "util/ProgressBar.svelte";
import SuccessMessage from "util/SuccessMessage.svelte";

View File

@@ -1,7 +1,7 @@
<script>
import { onMount } from "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 MollieDeposit from "./MollieDeposit.svelte";

View File

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

View File

@@ -3,7 +3,7 @@ import { onMount } from "svelte";
import Button from "layout/Button.svelte";
import CopyButton from "layout/CopyButton.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 secret = ""

View File

@@ -14,7 +14,7 @@ import Dashboard from "./dashboard/Dashboard.svelte";
import AffiliatePrompt from "./AffiliatePrompt.svelte";
import FileManager from "./filemanager/FileManager.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[] = [
{

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