Add access controls window
This commit is contained in:
@@ -29,6 +29,8 @@ export default [
|
||||
file: `${builddir}/${name}.js`,
|
||||
},
|
||||
plugins: [
|
||||
sveltePreprocess(),
|
||||
|
||||
svelte({
|
||||
preprocess: sveltePreprocess(),
|
||||
compilerOptions: {
|
||||
|
@@ -66,6 +66,7 @@ const share_tumblr = () => {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
background: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-top-left-radius: 16px;
|
||||
border-bottom-left-radius: 16px;
|
||||
text-align: center;
|
||||
|
@@ -240,6 +240,7 @@ h1 {
|
||||
flex-direction: row;
|
||||
overflow: auto;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
padding: 0 2px 2px 2px;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -259,6 +260,7 @@ h1 {
|
||||
}
|
||||
.compatibility_warning {
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-bottom: 2px solid #6666FF;
|
||||
padding: 4px;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { fs_encode_path } from "./FilesystemAPI";
|
||||
import { fs_encode_path } from "./FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
</script>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import Chart from "../util/Chart.svelte";
|
||||
import { formatDataVolume, formatDate, formatThousands } from "../util/Formatting.svelte";
|
||||
import Modal from "../util/Modal.svelte";
|
||||
import { fs_path_url, fs_timeseries } from "./FilesystemAPI";
|
||||
import { fs_path_url, fs_timeseries } from "./FilesystemAPI.mjs";
|
||||
import { generate_share_path, generate_share_url } from "./Sharebar.svelte";
|
||||
import { color_by_name } from "../util/Util.svelte";
|
||||
import { tick } from "svelte";
|
||||
@@ -121,7 +121,7 @@ let update_chart = async (base, timespan, interval) => {
|
||||
</script>
|
||||
|
||||
<Modal bind:visible={visible} title="Details" width={($nav.base.type === "file" ? 1000 : 750) + "px"}>
|
||||
<table style="width: 100%;">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
@@ -132,11 +132,15 @@ let update_chart = async (base, timespan, interval) => {
|
||||
<td>{$nav.base.path}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Created</td>
|
||||
<td>Created by user</td>
|
||||
<td>{$nav.base.created_by}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Creation date</td>
|
||||
<td>{formatDate($nav.base.created, true, true, true)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Modified</td>
|
||||
<td>Modification date</td>
|
||||
<td>{formatDate($nav.base.modified, true, true, true)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -216,6 +220,11 @@ let update_chart = async (base, timespan, interval) => {
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
table {
|
||||
/* yes this sucks, sue me */
|
||||
width: 98%;
|
||||
margin: auto;
|
||||
}
|
||||
td:first-child {
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI";
|
||||
import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI";
|
||||
import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI.mjs";
|
||||
import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI.mjs";
|
||||
import type { Writable } from "svelte/store"
|
||||
|
||||
export class FSNavigator {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { formatDataVolume, formatThousands } from "../util/Formatting.svelte"
|
||||
import { fs_path_url } from "./FilesystemAPI";
|
||||
import { fs_path_url } from "./FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
|
||||
|
@@ -7,7 +7,7 @@ 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';
|
||||
import { fs_path_url } from './FilesystemAPI.mjs';
|
||||
import Menu from './Menu.svelte';
|
||||
import { FSNavigator } from "./FSNavigator"
|
||||
import { writable } from 'svelte/store';
|
||||
@@ -226,6 +226,7 @@ const download = () => {
|
||||
text-align: left;
|
||||
box-shadow: none;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* File preview area (row 2) */
|
||||
@@ -243,6 +244,7 @@ const download = () => {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* This max-width needs to be synced with the .toolbar max-width in
|
||||
|
@@ -22,6 +22,7 @@ export type FSNode = {
|
||||
modified: Date,
|
||||
mode_string: string,
|
||||
mode_octal: string,
|
||||
created_by: string,
|
||||
|
||||
abuse_type: string | undefined,
|
||||
abuse_report_time: Date | undefined,
|
||||
@@ -31,15 +32,16 @@ export type FSNode = {
|
||||
sha256_sum: string,
|
||||
|
||||
id: string | undefined,
|
||||
read_password: string | undefined,
|
||||
write_password: string | undefined,
|
||||
properties: {} | undefined,
|
||||
link_permissions: FSPermissions | undefined,
|
||||
user_permissions: [string: FSPermissions] | undefined,
|
||||
password_permissions: [string: FSPermissions] | undefined,
|
||||
}
|
||||
|
||||
export type FSPermissions = {
|
||||
create: boolean,
|
||||
owner: boolean,
|
||||
read: boolean,
|
||||
update: boolean,
|
||||
write: boolean,
|
||||
delete: boolean,
|
||||
}
|
||||
|
||||
@@ -56,6 +58,12 @@ export type NodeOptions = {
|
||||
modified: Date | undefined,
|
||||
shared: boolean | undefined,
|
||||
|
||||
// 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,
|
||||
@@ -116,8 +124,12 @@ export const fs_update = async (path: string, opts: NodeOptions) => {
|
||||
form.append("action", "update")
|
||||
|
||||
for (let key of Object.keys(opts)) {
|
||||
if ((key === "created" || key === "modified") && opts[key] !== undefined) {
|
||||
if (opts[key] === undefined) {
|
||||
continue
|
||||
} else if ((key === "created" || key === "modified")) {
|
||||
form.append(key, opts[key].toISOString())
|
||||
} else if (typeof opts[key] === "object") {
|
||||
form.append(key, JSON.stringify(opts[key]))
|
||||
} else {
|
||||
form.append(key, opts[key])
|
||||
}
|
@@ -31,7 +31,7 @@ export const generate_share_path = path => {
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { fs_update, fs_encode_path } from "./FilesystemAPI";
|
||||
import { fs_update, fs_encode_path } from "./FilesystemAPI.mjs";
|
||||
|
||||
export let visible = false
|
||||
export let share_url = ""
|
||||
@@ -134,6 +134,7 @@ const share_tumblr = () => {
|
||||
overflow-x: hidden;
|
||||
float: left;
|
||||
background: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
|
@@ -106,7 +106,7 @@ let expand = e => {
|
||||
{/if}
|
||||
|
||||
<!-- Share button is enabled when: The browser has a sharing API, or the user can edit the file (to enable sharing)-->
|
||||
{#if $nav.base.id !== "me" && (navigator.share !== undefined || $nav.permissions.update === true)}
|
||||
{#if $nav.base.id !== "me" && (navigator.share !== undefined || $nav.permissions.write === true)}
|
||||
<button on:click={share}>
|
||||
<i class="icon">share</i>
|
||||
<span>Share</span>
|
||||
@@ -133,7 +133,7 @@ let expand = e => {
|
||||
<span>Deta<u>i</u>ls</span>
|
||||
</button>
|
||||
|
||||
{#if $nav.base.id !== "me" && $nav.permissions.update === true}
|
||||
{#if $nav.base.id !== "me" && $nav.permissions.write === true}
|
||||
<button on:click={() => edit_window.edit(nav.base, true, "file")} class:button_highlight={edit_visible}>
|
||||
<i class="icon">edit</i>
|
||||
<span><u>E</u>dit</span>
|
||||
@@ -149,6 +149,7 @@ let expand = e => {
|
||||
overflow-y: scroll;
|
||||
transition: max-height 0.3s;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
|
137
svelte/src/filesystem/edit_window/AccessControl.svelte
Normal file
137
svelte/src/filesystem/edit_window/AccessControl.svelte
Normal file
@@ -0,0 +1,137 @@
|
||||
<script lang="ts">
|
||||
import Button from "../../layout/Button.svelte";
|
||||
import type { FSNode, FSPermissions } from "../FilesystemAPI.mjs";
|
||||
import PermissionButton from "./PermissionButton.svelte";
|
||||
|
||||
export let file: FSNode
|
||||
|
||||
let new_user_id = ""
|
||||
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[new_user_id] = structuredClone(new_user_perms)
|
||||
}
|
||||
const del_user = (id: string) => {
|
||||
delete file.user_permissions[id]
|
||||
file.user_permissions = file.user_permissions
|
||||
}
|
||||
|
||||
let new_password = ""
|
||||
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[new_password] = structuredClone(new_password_perms)
|
||||
}
|
||||
const del_password = (pass: string) => {
|
||||
delete file.password_permissions[pass]
|
||||
file.password_permissions = file.password_permissions
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
Access controls are only available for users with an account currently. Even
|
||||
if you set 'Anyone with the link' to write, they will need to be logged in
|
||||
to write to the directory.
|
||||
</p>
|
||||
<p>
|
||||
Users can always delete files they uploaded themselves, even if they don't
|
||||
have delete permissions. You can see who the owner of a file is in the
|
||||
Details window.
|
||||
</p>
|
||||
<h2>Link permissions</h2>
|
||||
<div class="row">
|
||||
<div class="grow id">
|
||||
Anyone with the link can...
|
||||
</div>
|
||||
<div class="perms">
|
||||
<PermissionButton bind:permissions={file.link_permissions}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>User permissions</h2>
|
||||
<p>
|
||||
Enter a username here to give them access to this directory. The user will
|
||||
not receive an e-mail invite. Giving write access to a user without giving
|
||||
read access as well does not actually allow them to write anything.
|
||||
</p>
|
||||
<form on:submit={add_user} class="row">
|
||||
<input type="text" bind:value={new_user_id} placeholder="Username" class="grow" size="1">
|
||||
<Button type="submit" icon="add" label="Add"/>
|
||||
<div class="perms">
|
||||
<PermissionButton bind:permissions={new_user_perms}/>
|
||||
</div>
|
||||
</form>
|
||||
{#if file.user_permissions !== undefined}
|
||||
{#each Object.keys(file.user_permissions) as id (id)}
|
||||
<div class="row">
|
||||
<Button click={() => del_user(id)} icon="delete"/>
|
||||
<div class="grow id">
|
||||
{id}
|
||||
</div>
|
||||
<div class="perms">
|
||||
<PermissionButton bind:permissions={file.user_permissions[id]}/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<h2>Password permissions</h2>
|
||||
<p>
|
||||
Allow users to enter a password to give them access to this directory.
|
||||
</p>
|
||||
<p>
|
||||
<b>This feature is not implemented currently!</b>
|
||||
</p>
|
||||
<form on:submit={add_password} class="row">
|
||||
<input type="text" bind:value={new_password} placeholder="Password" class="grow" size="1">
|
||||
<Button type="submit" icon="add" label="Add"/>
|
||||
<div class="perms">
|
||||
<PermissionButton bind:permissions={new_password_perms}/>
|
||||
</div>
|
||||
</form>
|
||||
{#if file.password_permissions !== undefined}
|
||||
{#each Object.keys(file.password_permissions) as password (password)}
|
||||
<div class="row">
|
||||
<Button click={() => del_password(password)} icon="delete"/>
|
||||
<div class="grow id">
|
||||
{password}
|
||||
</div>
|
||||
<div class="perms">
|
||||
<PermissionButton bind:permissions={file.password_permissions[password]}/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
border-bottom: 1px solid var(--separator);
|
||||
margin-bottom: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
.row > .grow {
|
||||
min-width: 150px;
|
||||
flex: 1 1 content;
|
||||
}
|
||||
.row > * {
|
||||
flex: 0 0 content;
|
||||
}
|
||||
.id {
|
||||
margin-left: 0.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.perms {
|
||||
text-align: right;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
@@ -68,7 +68,7 @@ const add_styles = (style, properties) => {
|
||||
style.body_color = properties.brand_body_color
|
||||
style.body_background = properties.brand_body_color
|
||||
style.body_text_color = add_contrast(properties.brand_body_color, 75)
|
||||
style.shaded_background = set_alpha(properties.brand_body_color, 0.8)
|
||||
style.shaded_background = set_alpha(properties.brand_body_color, 0.75)
|
||||
style.separator = add_contrast(properties.brand_body_color, 8)
|
||||
style.shadow_color = darken(properties.brand_body_color, 0.8)
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import FilePicker from "../filemanager/FilePicker.svelte";
|
||||
import { fs_update, fs_node_type } from "../FilesystemAPI";
|
||||
import { fs_update, fs_node_type } from "../FilesystemAPI.mjs";
|
||||
import CustomBanner from "../viewers/CustomBanner.svelte";
|
||||
import HelpButton from "../../layout/HelpButton.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
@@ -1,10 +1,11 @@
|
||||
<script>
|
||||
import { fs_rename, fs_update } from "../FilesystemAPI";
|
||||
import { fs_rename, fs_update } from "../FilesystemAPI.mjs";
|
||||
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";
|
||||
|
||||
export let nav
|
||||
let file = {
|
||||
@@ -40,6 +41,11 @@ export const edit = (f, oae = false, open_tab = "") => {
|
||||
file.properties = {}
|
||||
}
|
||||
|
||||
if (shared && file.link_permissions === undefined) {
|
||||
// Default to read-only for public links
|
||||
file.link_permissions = {read: true, write: false, delete: false}
|
||||
}
|
||||
|
||||
branding_enabled = file.properties.branding_enabled === "true"
|
||||
if (branding_enabled) {
|
||||
custom_css = branding_from_node(file)
|
||||
@@ -76,7 +82,9 @@ const save = async (keep_editing = false) => {
|
||||
let new_file
|
||||
try {
|
||||
nav.set_loading(true)
|
||||
let opts = {shared: shared}
|
||||
let opts = {
|
||||
shared: shared,
|
||||
}
|
||||
|
||||
opts.branding_enabled = branding_enabled ? "true" : ""
|
||||
|
||||
@@ -89,6 +97,16 @@ const save = async (keep_editing = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (shared && file.link_permissions !== undefined) {
|
||||
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
|
||||
}
|
||||
|
||||
new_file = await fs_update(file.path, opts)
|
||||
|
||||
if (new_name !== file.name) {
|
||||
@@ -135,6 +153,12 @@ const save = async (keep_editing = false) => {
|
||||
<i class="icon">share</i>
|
||||
Sharing
|
||||
</button>
|
||||
{#if shared && $nav.permissions.owner}
|
||||
<button class:button_highlight={tab === "access"} on:click={() => tab = "access"}>
|
||||
<i class="icon">key</i>
|
||||
Access control
|
||||
</button>
|
||||
{/if}
|
||||
<button class:button_highlight={tab === "branding"} on:click={() => tab = "branding"}>
|
||||
<i class="icon">palette</i>
|
||||
Branding
|
||||
@@ -153,11 +177,9 @@ const save = async (keep_editing = false) => {
|
||||
bind:open_after_edit
|
||||
/>
|
||||
{:else if tab === "share"}
|
||||
<SharingOptions
|
||||
bind:file
|
||||
bind:shared
|
||||
on:save={() => save(true)}
|
||||
/>
|
||||
<SharingOptions bind:file bind:shared on:save={() => save(true)} />
|
||||
{:else if tab === "access"}
|
||||
<AccessControl bind:file bind:shared />
|
||||
{:else if tab === "branding"}
|
||||
<BrandingOptions
|
||||
bind:enabled={branding_enabled}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Button from "../../layout/Button.svelte";
|
||||
import { fs_delete_all } from "../FilesystemAPI";
|
||||
import { fs_delete_all } from "../FilesystemAPI.mjs";
|
||||
import PathLink from "../util/PathLink.svelte";
|
||||
|
||||
export let nav
|
||||
@@ -47,12 +47,14 @@ const delete_file = async e => {
|
||||
<label for="file_name">Name</label>
|
||||
<input form="edit_form" bind:value={new_name} id="file_name" type="text" class="form_input" disabled={is_root_dir}/>
|
||||
</div>
|
||||
{#if $nav.permissions.delete === true}
|
||||
<h2>Delete</h2>
|
||||
<p>
|
||||
Delete this file or directory. If this is a directory then all
|
||||
subfiles will be deleted as well. This action cannot be undone.
|
||||
</p>
|
||||
<Button click={delete_file} red icon="delete" label="Delete" style="align-self: flex-start;"/>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.form_grid {
|
||||
|
10
svelte/src/filesystem/edit_window/PermissionButton.svelte
Normal file
10
svelte/src/filesystem/edit_window/PermissionButton.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import ToggleButton from "../../layout/ToggleButton.svelte";
|
||||
import type { FSPermissions } from "../FilesystemAPI.mjs";
|
||||
|
||||
export let permissions = <FSPermissions>{}
|
||||
</script>
|
||||
|
||||
<ToggleButton group_first bind:on={permissions.read}>Read</ToggleButton>
|
||||
<ToggleButton group_middle bind:on={permissions.write}>Write</ToggleButton>
|
||||
<ToggleButton group_last bind:on={permissions.delete}>Delete</ToggleButton>
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { fs_mkdir } from "../FilesystemAPI.js";
|
||||
import { fs_mkdir } from "../FilesystemAPI.mjs";
|
||||
import Button from "../../layout/Button.svelte";
|
||||
|
||||
export let nav;
|
||||
@@ -22,7 +22,7 @@ let create_dir = async () => {
|
||||
if (err.value && err.value === "node_already_exists") {
|
||||
error_msg = "A directory with this name already exists"
|
||||
} else {
|
||||
error_msg = "Server returned an error: "+err
|
||||
error_msg = "Server returned an error: code: '"+err.value+"' message: "+err.message
|
||||
}
|
||||
} finally {
|
||||
nav.set_loading(false)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import FilePicker from "../../file_viewer/FilePicker.svelte";
|
||||
import { fs_import } from "../FilesystemAPI";
|
||||
import { fs_import } from "../FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
let file_picker
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { fs_delete_all, fs_rename } from './../FilesystemAPI.ts'
|
||||
import { fs_delete_all, fs_rename } from '../FilesystemAPI.mjs'
|
||||
import { onMount } from 'svelte'
|
||||
import CreateDirectory from './CreateDirectory.svelte'
|
||||
import ListView from './ListView.svelte'
|
||||
@@ -298,7 +298,7 @@ onMount(() => {
|
||||
</button>
|
||||
|
||||
<div class="toolbar_spacer"></div>
|
||||
{#if $nav.permissions.update}
|
||||
{#if $nav.permissions.write}
|
||||
<button on:click={() => upload_widget.pick_files()} title="Upload files to this directory">
|
||||
<i class="icon">cloud_upload</i>
|
||||
</button>
|
||||
@@ -407,6 +407,7 @@ onMount(() => {
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
background: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { fs_node_icon, fs_node_type, fs_encode_path } from "./../FilesystemAPI";
|
||||
import { fs_node_icon, fs_node_type, fs_encode_path } from "../FilesystemAPI.mjs";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { formatDataVolume } from "./../../util/Formatting.svelte";
|
||||
import { fs_encode_path, fs_node_icon } from "./../FilesystemAPI"
|
||||
import { fs_encode_path, fs_node_icon } from "../FilesystemAPI.mjs"
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
@@ -57,7 +57,7 @@ export let hide_branding = false
|
||||
<i class="icon">palette</i>
|
||||
</button>
|
||||
{/if}
|
||||
{#if $nav.permissions.update && !hide_edit}
|
||||
{#if $nav.permissions.write && !hide_edit}
|
||||
<button class="action_button" on:click|preventDefault|stopPropagation={() => dispatch("node_settings", index)}>
|
||||
<i class="icon">edit</i>
|
||||
</button>
|
||||
@@ -69,11 +69,11 @@ export let hide_branding = false
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
.directory {
|
||||
display: table;
|
||||
margin: 8px auto 16px auto;
|
||||
background: var(--body_color);
|
||||
background: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-collapse: collapse;
|
||||
border-radius: 8px;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { fs_search, fs_encode_path, fs_thumbnail_url } from "../FilesystemAPI";
|
||||
import { fs_search, fs_encode_path, fs_thumbnail_url } from "../FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
|
||||
|
@@ -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 "./../FilesystemAPI"
|
||||
import { fs_path_url, type GenericResponse } from "../FilesystemAPI.mjs"
|
||||
|
||||
// code and an error message
|
||||
export const upload_file = (
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { fs_path_url, fs_encode_path, fs_node_icon } from "../../filesystem/FilesystemAPI.ts"
|
||||
import { fs_path_url, fs_encode_path, fs_node_icon } from "../FilesystemAPI.mjs"
|
||||
import FileTitle from "../../layout/FileTitle.svelte";
|
||||
import TextBlock from "../../layout/TextBlock.svelte"
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import IconBlock from "../../layout/IconBlock.svelte";
|
||||
import { fs_thumbnail_url } from "../FilesystemAPI";
|
||||
import { fs_thumbnail_url } from "../FilesystemAPI.mjs";
|
||||
import TextBlock from "../../layout/TextBlock.svelte"
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount, tick } from "svelte";
|
||||
import Spinner from "../../util/Spinner.svelte";
|
||||
import { fs_node_type, fs_thumbnail_url } from "./../FilesystemAPI";
|
||||
import { fs_node_type, fs_thumbnail_url } from "../FilesystemAPI.mjs";
|
||||
import FileManager from "../filemanager/FileManager.svelte";
|
||||
import Audio from "./Audio.svelte";
|
||||
import File from "./File.svelte";
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { swipe_nav } from "../../lib/SwipeNavigate.ts";
|
||||
import { fs_path_url } from "./../FilesystemAPI.ts";
|
||||
import { fs_path_url } from "../FilesystemAPI.mjs";
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { fs_path_url } from "../FilesystemAPI";
|
||||
import { fs_path_url } from "../FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
</script>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { tick } from "svelte";
|
||||
import { fs_path_url } from "../FilesystemAPI";
|
||||
import { fs_path_url } from "../FilesystemAPI.mjs";
|
||||
|
||||
export let nav
|
||||
let text_type = "text"
|
||||
|
@@ -5,7 +5,7 @@ import { formatDate } from "../../util/Formatting.svelte"
|
||||
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 "../FilesystemAPI";
|
||||
import { fs_node_icon, fs_path_url } from "../FilesystemAPI.mjs";
|
||||
import CopyButton from "../../layout/CopyButton.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { onMount, createEventDispatcher, tick } from "svelte";
|
||||
import { fs_path_url } from "../FilesystemAPI";
|
||||
import { fs_path_url } from "../FilesystemAPI.mjs";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav
|
||||
@@ -169,6 +169,7 @@ const fullscreen = () => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
padding: 0 2px 2px 2px;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -195,6 +196,7 @@ const fullscreen = () => {
|
||||
}
|
||||
.compatibility_warning {
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-bottom: 2px solid #6666FF;
|
||||
padding: 4px;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import { formatDataVolume, formatDate } from "../../util/Formatting.svelte"
|
||||
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 "../FilesystemAPI";
|
||||
import { fs_node_icon, fs_path_url } from "../FilesystemAPI.mjs";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
@@ -8,13 +8,13 @@ export let disabled = false;
|
||||
export let icon = ""
|
||||
export let icon_small = false;
|
||||
export let label = ""
|
||||
export let title = ""
|
||||
export let title = null
|
||||
export let link_href = ""
|
||||
export let link_target = "_self"
|
||||
export let click = e => {}
|
||||
export let style = ""
|
||||
export let type = ""
|
||||
export let form = ""
|
||||
export let style = null
|
||||
export let type = null
|
||||
export let form = null
|
||||
|
||||
let click_int = e => {
|
||||
if (highlight_on_click) {
|
||||
@@ -43,7 +43,7 @@ let click_int = e => {
|
||||
style={style}
|
||||
type={type}
|
||||
form={form}
|
||||
disabled={disabled ? "disabled":""}
|
||||
disabled={disabled ? "disabled":null}
|
||||
>
|
||||
{#if icon !== ""}
|
||||
<i class="icon" class:small={icon_small}>{icon}</i>
|
||||
@@ -63,7 +63,7 @@ let click_int = e => {
|
||||
class:flat
|
||||
title={title}
|
||||
style={style}
|
||||
disabled={disabled ? "disabled":""}
|
||||
disabled={disabled ? "disabled":null}
|
||||
>
|
||||
{#if icon !== ""}
|
||||
<i class="icon" class:small={icon_small}>{icon}</i>
|
||||
|
@@ -38,6 +38,7 @@ export let width = "750px"
|
||||
vertical-align: middle;
|
||||
overflow-wrap: anywhere;
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ export let center = false
|
||||
margin: 8px auto;
|
||||
|
||||
background-color: var(--shaded_background);
|
||||
backdrop-filter: blur(4px);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
30
svelte/src/layout/ToggleButton.svelte
Normal file
30
svelte/src/layout/ToggleButton.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
export let on = false
|
||||
export let group_first = false
|
||||
export let group_middle = false
|
||||
export let group_last = false
|
||||
</script>
|
||||
|
||||
<button
|
||||
on:click={() => on = !on}
|
||||
type="button"
|
||||
class="button"
|
||||
class:button_highlight={on}
|
||||
class:group_first
|
||||
class:group_middle
|
||||
class:group_last
|
||||
>
|
||||
{#if on}
|
||||
<i class="icon">check</i>
|
||||
{:else}
|
||||
<i class="icon">close</i>
|
||||
{/if}
|
||||
|
||||
<slot></slot>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.button {
|
||||
flex: 0 0 content;
|
||||
}
|
||||
</style>
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { FSNavigator } from "../../filesystem/FSNavigator.ts"
|
||||
import { fs_encode_path, fs_node_icon } from "../../filesystem/FilesystemAPI.ts";
|
||||
import { fs_encode_path, fs_node_icon } from "../../filesystem/FilesystemAPI.mjs";
|
||||
import Button from "../../layout/Button.svelte";
|
||||
import CreateDirectory from "../../filesystem/filemanager/CreateDirectory.svelte";
|
||||
import FSUploadWidget from "../../filesystem/upload_widget/FSUploadWidget.svelte";
|
||||
@@ -17,7 +17,7 @@ onMount(() => nav.navigate("/me", false))
|
||||
|
||||
<div class="wrapper" use:drop_target={{upload: (files) => upload_widget.upload_files(files)}}>
|
||||
<div class="toolbar">
|
||||
{#if $nav.permissions.update}
|
||||
{#if $nav.permissions.write}
|
||||
<Button
|
||||
click={() => upload_widget.pick_files()}
|
||||
icon="cloud_upload"
|
||||
|
6
svelte/tsconfig.json
Normal file
6
svelte/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"verbatimModuleSyntax": true,
|
||||
"target": "ES2017",
|
||||
}
|
||||
}
|
@@ -33,6 +33,8 @@ func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request,
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
} else if err.Error() == "unavailable_for_legal_reasons" {
|
||||
wc.serveUnavailableForLegalReasons(w, r)
|
||||
} else if err.Error() == "permission_denied" {
|
||||
wc.serveForbidden(w, r)
|
||||
} else {
|
||||
log.Error("Failed to get path: %s", err)
|
||||
wc.templates.Run(w, r, "500", td)
|
||||
|
@@ -264,7 +264,7 @@ func (s styleSheet) String() string {
|
||||
s.BodyBackground.CSS(),
|
||||
s.BodyText.CSS(),
|
||||
s.Separator.CSS(),
|
||||
s.BodyColor.WithAlpha(0.8).CSS(), // shaded_background
|
||||
s.BodyColor.WithAlpha(0.75).CSS(), // shaded_background
|
||||
s.CardColor.CSS(),
|
||||
s.Chart1.CSS(),
|
||||
s.Chart2.CSS(),
|
||||
|
Reference in New Issue
Block a user