diff --git a/res/include/style/layout.css b/res/include/style/layout.css
index 5159d36..58a9193 100644
--- a/res/include/style/layout.css
+++ b/res/include/style/layout.css
@@ -454,8 +454,11 @@ input[type="submit"]:disabled, input[type="submit"].disabled,
input[type="button"]:disabled, input[type="button"].disabled,
input[type="color"]:disabled, input[type="color"].disabled,
select:disabled , select.disabled {
- background: var(--input_color_dark);
+ background: var(--layer_1_color);
+ color: var(--input_color);
box-shadow: none;
+ transition: none;
+ padding: 4px 5px 4px 5px;
}
/* Dropdown list of the select tag */
diff --git a/res/template/account/user_buckets.html b/res/template/account/user_buckets.html
new file mode 100644
index 0000000..f0f55ea
--- /dev/null
+++ b/res/template/account/user_buckets.html
@@ -0,0 +1,21 @@
+{{define "user_buckets"}}
+
+
+ {{template "meta_tags" "Buckets"}}
+ {{template "user_style" .}}
+
+
+
+
+ {{template "page_top" .}}
+
Live
Day
-
Week
+
Week
Two Weeks
-
Month
+
Month
Quarter
Half-year
Year
diff --git a/svelte/rollup.config.js b/svelte/rollup.config.js
index a6afe24..63abd25 100644
--- a/svelte/rollup.config.js
+++ b/svelte/rollup.config.js
@@ -31,6 +31,7 @@ const builddir = "../res/static/svelte"
export default [
"filesystem",
"modal",
+ "user_buckets",
].map((name, index) => ({
input: `src/${name}.js`,
output: {
diff --git a/svelte/src/filesystem/Filesystem.svelte b/svelte/src/filesystem/Filesystem.svelte
index 28ad2c0..63be476 100644
--- a/svelte/src/filesystem/Filesystem.svelte
+++ b/svelte/src/filesystem/Filesystem.svelte
@@ -9,6 +9,8 @@ import FileManager from './filemanager/FileManager.svelte';
import Audio from './viewers/Audio.svelte';
import Image from './viewers/Image.svelte';
import Video from './viewers/Video.svelte';
+import PDF from './viewers/PDF.svelte';
+import PixeldrainLogo from '../util/PixeldrainLogo.svelte';
// Elements
let file_viewer
@@ -18,12 +20,18 @@ let toolbar_visible = (window.innerWidth > 600)
let toolbar_toggle = () => {
toolbar_visible = !toolbar_visible
if (!toolbar_visible) {
- sharebar.setVisible(false)
+ sharebar_visible = false
}
}
let sharebar
let sharebar_visible = false
+$: {
+ if (typeof(sharebar) !== "undefined") {
+ sharebar.setVisible(sharebar_visible)
+ }
+}
+
let details
let details_visible = false
let download_frame
@@ -34,16 +42,21 @@ let state = {
parents: initialNode.parents,
base: initialNode.base,
- // When navigating into a file or directory the siblings array will be
- // populated with the previous base's children
- siblings: [],
- current_sibling: -1,
+ // These are used to navigate forward and backward within a directory (using
+ // the previous and next buttons on the toolbar). The cached siblings will
+ // be used so that we don't need to make an extra request to the parent
+ // directory. The siblings_path variable is used to verify that the parent
+ // directory is still the same. If it's sifferent the siblings array is not
+ // used
+ siblings_path: "",
+ siblings: null,
// Root path of the bucket. Used for navigation by prepending it to a file
// path
path_root: "/d/"+initialNode.bucket.id,
loading: true,
- viewer_type: ""
+ viewer_type: "",
+ shuffle: false,
}
// Tallys
@@ -51,28 +64,6 @@ $: total_directories = state.base.children.reduce((acc, cur) => cur.type === "di
$: total_files = state.base.children.reduce((acc, cur) => cur.type === "file" ? acc + 1 : acc, 0)
$: total_file_size = state.base.children.reduce((acc, cur) => acc + cur.file_size, 0)
-const navigate = (path, pushHist) => {
- state.loading = true
-
- fs_get_node(
- state.bucket.id, path,
- ).then(resp => {
- window.document.title = resp.base.name+" ~ pixeldrain"
- if (pushHist) {
- window.history.pushState(
- {}, window.document.title, "/d/"+resp.bucket.id+resp.base.path,
- )
- }
-
- openNode(resp)
- }).catch(err => {
- console.error(err)
- alert(err)
- }).finally(() => {
- state.loading = false
- })
-}
-
const sort_children = children => {
children.sort((a, b) => {
// Sort directories before files
@@ -83,25 +74,37 @@ const sort_children = children => {
})
}
-const openNode = (node) => {
- // Sort directory children
- sort_children(node.base.children)
+const navigate = (path, pushHist) => {
+ state.loading = true
+ fs_get_node(state.bucket.id, path).then(resp => {
+ window.document.title = resp.base.name+" ~ pixeldrain"
+ if (pushHist) {
+ window.history.pushState(
+ {}, window.document.title, "/d/"+resp.bucket.id+resp.base.path,
+ )
+ }
+
+ // Sort directory children
+ sort_children(resp.base.children)
+
+ open_node(resp)
+ }).catch(err => {
+ console.error(err)
+ alert(err)
+ }).finally(() => {
+ state.loading = false
+ })
+}
+
+const open_node = (node) => {
// If the new node is a child of the previous node we save the parent's
// children array
if (node.parents.length > 0 && node.parents[node.parents.length-1].path === state.base.path) {
console.debug("Current parent path and new node path match. Saving siblings")
- state.siblings = state.base.children
- state.current_sibling = -1
- // Find which sibling is currently open
- for (let i = 0; i < state.siblings.length; i++) {
- if (state.siblings[i].name === node.base.name) {
- state.current_sibling = i
- console.debug("Current sibling ID is", i)
- break
- }
- }
+ state.siblings_path = node.parents[node.parents.length-1].path
+ state.siblings = state.base.children
}
// Update shared state
@@ -126,6 +129,11 @@ const openNode = (node) => {
state.base.file_type === "application/x-matroska"
) {
state.viewer_type = "video"
+ } else if (
+ state.base.file_type === "application/pdf" ||
+ state.base.file_type === "application/x-pdf"
+ ) {
+ state.viewer_type = "pdf"
} else {
state.viewer_type = ""
}
@@ -133,46 +141,77 @@ const openNode = (node) => {
// Remove spinner
state.loading = false
}
-onMount(() => openNode(initialNode))
+onMount(() => open_node(initialNode))
// Opens a sibling of the currently open file. The offset is relative to the
// file which is currently open. Give a positive number to move forward and a
// negative number to move backward
-const open_sibling = offset => {
+const open_sibling = async offset => {
+ if (state.parents.length == 0) {
+ return
+ }
+
state.loading = true
- // Get the parent directory
- fs_get_node(
- state.bucket.id, state.parents[state.parents.length - 1].path,
- ).then(resp => {
- // Sort directory children
- sort_children(resp.base.children)
+ // Check if we already have siblings cached
+ if (state.siblings != null && state.siblings_path == state.parents[state.parents.length - 1].path) {
+ console.debug("Using cached siblings")
+ } else {
+ console.debug("Cached siblings not available. Fetching new")
+ try {
+ let resp = await fs_get_node(state.bucket.id, state.parents[state.parents.length - 1].path)
+ // Sort directory children to make sure the order is consistent
+ sort_children(resp.base.children)
+
+ // Save new siblings in global state
+ state.siblings_path = state.parents[state.parents.length - 1].path
+ state.siblings = resp.base.children
+ } catch (err) {
+ console.error(err)
+ alert(err)
+ state.loading = false
+ return
+ }
+ }
+
+ let next_sibling = null
+
+ if (state.shuffle) {
+ // Shuffle is on, pick a random sibling
+ for (let i = 0; i < 10; i++) {
+ next_sibling = state.siblings[Math.floor(Math.random()*state.siblings.length)]
+
+ // If we selected the same sibling we already have open we try
+ // again. Else we break the loop
+ if (next_sibling.name !== state.base.name) {
+ break
+ }
+ }
+ } else {
// Loop over the parent node's children to find the one which is
// currently open. Then, if possible, we save the one which comes before
// or after it
- let next_sibling = null
- for (let i = 0; i < resp.base.children.length; i++) {
+ for (let i = 0; i < state.siblings.length; i++) {
if (
- resp.base.children[i].name === state.base.name &&
+ state.siblings[i].name === state.base.name &&
i+offset >= 0 && // Prevent underflow
- i+offset < resp.base.children.length // Prevent overflow
+ i+offset < state.siblings.length // Prevent overflow
) {
- next_sibling = resp.base.children[i+offset]
- console.debug("Next sibling is", next_sibling)
+ next_sibling = state.siblings[i+offset]
+ break
}
}
+ }
- // If we found a sibling we open it
- if (next_sibling !== null) {
- navigate(next_sibling.path, true)
- }
- }).catch(err => {
- console.error(err)
- alert(err)
- }).finally(() => {
+ // If we found a sibling we open it
+ if (next_sibling !== null) {
+ console.debug("Opening sibling", next_sibling)
+ navigate(next_sibling.path,true)
+ } else {
+ console.debug("No siblings found")
state.loading = false
- })
+ }
}
// Capture browser back and forward navigation buttons
@@ -185,24 +224,42 @@ window.onpopstate = (e) => {
};
const keydown = e => {
+ if (e.ctrlKey || e.altKey || e.metaKey) {
+ return // prevent custom shortcuts from interfering with system shortcuts
+ }
+
switch (e.key) {
case "Escape":
hide();
- return;
+ break;
case "i":
details_window.toggle()
+ break;
case "s":
download()
+ break;
+ case "r":
+ state.shuffle = !state.shuffle
+ break;
+ case "a", "ArrowLeft":
+ open_sibling(-1)
+ break;
+ case "d", "ArrowRight":
+ open_sibling(1)
+ break;
}
};
const download = () => {
download_frame.src = fs_get_file_url(state.bucket.id, state.base.path) + "?attach"
}
+const share = () => {
+
+}
-
+
{#if state.loading}
@@ -215,7 +272,9 @@ const download = () => {
menu
-
home
+
+
+
@@ -282,7 +355,7 @@ const download = () => {
-
+
Node details
Name {state.base.name}
@@ -475,5 +548,12 @@ const download = () => {
.toolbar_statistic {
text-align: center;
}
+.button_row {
+ display: flex;
+ flex-direction: row;
+}
+.button_row > * {
+ flex: 1 1 auto;
+}
diff --git a/svelte/src/filesystem/FilesystemAPI.svelte b/svelte/src/filesystem/FilesystemAPI.svelte
index b6034c3..d36100f 100644
--- a/svelte/src/filesystem/FilesystemAPI.svelte
+++ b/svelte/src/filesystem/FilesystemAPI.svelte
@@ -1,5 +1,13 @@
Share on:
-
+
+
+
+
+
E-Mail
-
+
+
+
+
+
Reddit
-
+
+
+
+
+
Twitter
-
+
+
+
+
+
Facebook
-
+
+
+
+
+
Tumblr
@@ -42,4 +80,9 @@ export const toggle = () => { setVisible(!visible) }
transition: left 0.5s;
}
.visible { left: 8em; }
+.button_full_width > svg {
+ height: 3em;
+ width: 3em;
+ fill: currentColor;
+}
diff --git a/svelte/src/filesystem/filemanager/FileManager.svelte b/svelte/src/filesystem/filemanager/FileManager.svelte
index fcf6483..017104a 100644
--- a/svelte/src/filesystem/filemanager/FileManager.svelte
+++ b/svelte/src/filesystem/filemanager/FileManager.svelte
@@ -20,8 +20,6 @@ const node_click = (index) => {
dispatch("navigate", state.base.children[index].path)
} else if (mode === "selecting") {
state.base.children[index].fm_selected = !state.base.children[index].fm_selected
- } else if (mode === "deleting") {
- state.base.children[index].fm_delete = !state.base.children[index].fm_delete
}
}
const navigate_up = () => {
@@ -66,9 +64,23 @@ const node_icon = node => {
return "/res/img/mime/empty.png"
}
-const delete_node = () => {
- if (mode !== "deleting") {
- mode = "deleting"
+const delete_selected = () => {
+ if (mode !== "selecting") {
+ return
+ }
+
+ let count = state.base.children.reduce((acc, cur) => {
+ if (cur.fm_selected) {
+ acc++
+ }
+ return acc
+ }, 0)
+
+ let confirmSingle = `Are you sure you want to delete this file? This action is irreversible.`
+ let confirmMulti = `Are you sure you want to delete these ${count} files? This action is irreversible.`
+ if (count === 0 ||
+ (count === 1 && !confirm(confirmSingle)) ||
+ (count > 1 && !confirm(confirmMulti))) {
return
}
@@ -77,7 +89,7 @@ const delete_node = () => {
// Save all promises with deletion requests in an array
let promises = []
state.base.children.forEach(child => {
- if (!child.fm_delete) { return }
+ if (!child.fm_selected) { return }
promises.push(fs_delete_node(state.bucket.id, child.path))
})
@@ -89,53 +101,53 @@ const delete_node = () => {
reload()
})
}
-const delete_toggle = () => {
- // Turn on deletion mode if it's not already
- if (mode !== "deleting") {
- mode = "deleting"
+const toggle_select = () => {
+ if (mode !== "selecting") {
+ mode = "selecting"
return
}
- // Return to normal and unmark all the marked files
- mode = "viewing"
+ // Unmark all the selected files and return to viewing mode
state.base.children.forEach((child, i) => {
- if (child.fm_delete) {
- state.base.children[i].fm_delete = false
+ if (child.fm_selected) {
+ state.base.children[i].fm_selected = false
}
})
+ mode = "viewing"
}
- {#if mode === "deleting"}
-