Convert the whole filesystem UI to Typescript
This commit is contained in:
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -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";
|
||||
|
@@ -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";
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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 = {}
|
||||
|
@@ -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";
|
||||
|
@@ -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 = [];
|
||||
|
@@ -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 = ""
|
||||
|
@@ -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>
|
||||
|
@@ -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"
|
||||
|
@@ -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 = ""
|
||||
|
@@ -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 = []
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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()
|
||||
|
@@ -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: "",
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { formatDataVolume, formatThousands } from "util/Formatting.svelte"
|
||||
import { formatDataVolume, formatThousands } from "util/Formatting"
|
||||
|
||||
export let list = {
|
||||
files: [],
|
||||
|
@@ -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 = ""
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -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"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { formatDataVolume } from "util/Formatting.svelte";
|
||||
import { formatDataVolume } from "util/Formatting";
|
||||
|
||||
export let item = {
|
||||
size: 0,
|
||||
|
@@ -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()
|
||||
|
@@ -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";
|
||||
|
@@ -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 -->
|
||||
|
@@ -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">
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
@@ -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">
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
}
|
@@ -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}
|
||||
|
@@ -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>
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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";
|
@@ -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
|
||||
|
@@ -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) {
|
||||
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)}
|
||||
/>
|
||||
|
@@ -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 {
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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]
|
||||
|
@@ -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>
|
||||
|
@@ -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 () => {
|
||||
|
@@ -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 = []
|
||||
|
@@ -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}
|
||||
|
@@ -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])
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
|
@@ -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}
|
||||
|
@@ -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?"
|
||||
|
@@ -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 = (
|
||||
|
@@ -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(
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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) }>
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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">
|
||||
|
@@ -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">
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 = {}
|
||||
|
@@ -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 = []
|
||||
|
@@ -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>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { copy_text } from "util/Util.svelte";
|
||||
|
||||
export let text = ""
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
export let title = ""
|
||||
</script>
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
export let toggle = false;
|
||||
</script>
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
export let icon_href = ""
|
||||
export let width = "750px"
|
||||
</script>
|
||||
|
@@ -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}
|
||||
|
@@ -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()
|
||||
|
@@ -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) {
|
@@ -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)
|
||||
}
|
13
svelte/src/lib/UserStore.ts
Normal file
13
svelte/src/lib/UserStore.ts
Normal 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))
|
||||
})
|
||||
}
|
||||
)
|
@@ -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()
|
||||
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
|
@@ -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: [
|
||||
|
@@ -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" />
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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 = ""
|
||||
|
@@ -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
Reference in New Issue
Block a user