mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-05 21:03:20 +00:00
Implement switching sort / display
This commit is contained in:
parent
a1d740aa05
commit
8ef99d1ff7
5 changed files with 86 additions and 42 deletions
|
@ -1,5 +1,7 @@
|
|||
use std::cell::OnceCell;
|
||||
use std::time::Duration;
|
||||
use rocket::data::ByteUnit;
|
||||
use rocket::serde::Serialize;
|
||||
|
||||
/// The maximum amount of bytes that can be uploaded at once
|
||||
pub const MAX_UPLOAD_SIZE: ByteUnit = ByteUnit::Mebibyte(100_000);
|
||||
|
@ -10,3 +12,16 @@ pub const ENCRYPTION_ROUNDS: u32 = 12;
|
|||
pub const SESSION_LIFETIME_SECONDS: u64 = 3600 * 24 * 14; // 14 days
|
||||
|
||||
pub const SESSION_COOKIE_NAME: &'static str = "storage-session";
|
||||
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FileConstants<'a> {
|
||||
pub display_options: &'a[&'a str],
|
||||
pub sort_keys: &'a[&'a str],
|
||||
}
|
||||
pub const FILE_CONSTANTS: FileConstants = FileConstants {
|
||||
display_options: &["list", "grid"],
|
||||
sort_keys: &["name", "last_modified", "size"],
|
||||
};
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ async fn rocket() -> _ {
|
|||
warn!("warn");
|
||||
error!("error");
|
||||
|
||||
// TODO: move to own fn
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(std::env::var("DATABASE_URL").unwrap().as_str())
|
||||
|
@ -96,6 +97,7 @@ async fn rocket() -> _ {
|
|||
Arc::new(Mutex::new(manager))
|
||||
};
|
||||
|
||||
// TODO: move to own func
|
||||
let memory_store: MemoryStore::<SessionData> = MemoryStore::default();
|
||||
let store: SessionStore<SessionData> = SessionStore {
|
||||
store: Box::new(memory_store),
|
||||
|
@ -110,6 +112,7 @@ async fn rocket() -> _ {
|
|||
.path("/")
|
||||
};
|
||||
|
||||
// TODO: move to constants
|
||||
let metadata = GlobalMetadata {
|
||||
app_name: env!("CARGO_PKG_NAME").to_string(),
|
||||
app_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::cell::OnceCell;
|
||||
use std::io::Cursor;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
@ -13,6 +14,7 @@ use rocket_dyn_templates::{context, Template};
|
|||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use tokio::sync::Mutex;
|
||||
use crate::consts::FILE_CONSTANTS;
|
||||
use crate::guards::{AuthUser};
|
||||
use crate::managers::libraries::LibraryManager;
|
||||
use crate::routes::ui::auth;
|
||||
|
@ -30,14 +32,20 @@ pub async fn redirect_list_library_files(user: AuthUser, libraries: &State<Arc<M
|
|||
{
|
||||
let libs = libraries.lock().await;
|
||||
let library = libs.get(library_id).await?;
|
||||
Ok(Redirect::to(uri!(list_library_files(library_id, library.model().name, ""))))
|
||||
Ok(Redirect::to(uri!(list_library_files(library_id, library.model().name, "", Some("name"), Some("asc"), Some("list")))))
|
||||
}
|
||||
|
||||
|
||||
#[get("/library/<library_id>/<_>/<path..>")]
|
||||
pub async fn list_library_files(user: AuthUser, route: &Route, libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: PathBuf)
|
||||
-> Result<Template, ResponseError>
|
||||
{
|
||||
#[get("/library/<library_id>/<_>/<path..>?<sort_key>&<sort_dir>&<display>")]
|
||||
pub async fn list_library_files(
|
||||
user: AuthUser,
|
||||
route: &Route,
|
||||
libraries: &State<Arc<Mutex<LibraryManager>>>,
|
||||
library_id: &str,
|
||||
path: PathBuf,
|
||||
sort_key: Option<String>,
|
||||
sort_dir: Option<String>,
|
||||
display: Option<String>,
|
||||
) -> Result<Template, ResponseError> {
|
||||
let libs = libraries.lock().await;
|
||||
let library = libs.get(library_id).await?;
|
||||
let files = library.list_files(&PathBuf::from(&path)).await
|
||||
|
@ -72,10 +80,36 @@ pub async fn list_library_files(user: AuthUser, route: &Route, libraries: &State
|
|||
library: library.model(),
|
||||
files: files,
|
||||
parent,
|
||||
path_segments: segments
|
||||
path_segments: segments,
|
||||
// TODO: have struct?
|
||||
options: FileDisplayOptions {
|
||||
// TODO: prevent bad values
|
||||
// TODO: fix login errror msg -------_____------
|
||||
sort_key: validate_option(sort_key, FILE_CONSTANTS.sort_keys, "name"),
|
||||
sort_dir: validate_option(sort_dir, &["asc", "desc"], "asc"),
|
||||
display: validate_option(display, FILE_CONSTANTS.display_options, "list"),
|
||||
},
|
||||
DATA: FILE_CONSTANTS
|
||||
}))
|
||||
}
|
||||
|
||||
/// Checks if option is in list of valid values, if not returns default_value
|
||||
fn validate_option(option: Option<String>, valid_values: &[&str], default_value: &str) -> String {
|
||||
if let Some(option) = option {
|
||||
if valid_values.contains(&&*option) {
|
||||
return option.to_string()
|
||||
}
|
||||
}
|
||||
default_value.to_string()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FileDisplayOptions {
|
||||
sort_key: String,
|
||||
sort_dir: String,
|
||||
display: String
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct PathSegmentPiece {
|
||||
pub path: PathBuf,
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::io::BufReader;
|
|||
use std::path::PathBuf;
|
||||
use anyhow::{anyhow, Error};
|
||||
use int_enum::IntEnum;
|
||||
use rocket::FromFormField;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
@ -18,7 +19,7 @@ pub enum StorageBackendMap {
|
|||
S3(S3Storage)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, FromFormField)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FileType {
|
||||
File,
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
<div class="dropdown-trigger">
|
||||
<button class="button is-small has-background-white-ter" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
<span class="icon">
|
||||
<i class="fa fa-list is-small"></i>
|
||||
<i class="fa {{#if (eq options.display 'grid')}}fa-grip{{else}}fa-list{{/if}} is-small"></i>
|
||||
</span>
|
||||
<span>List</span>
|
||||
<span>{{#if (eq options.display 'grid')}}Grid{{else}}List{{/if}}</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
@ -46,8 +46,11 @@
|
|||
</div>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a href="#" class="dropdown-item is-active"> <i class="fa fa-list"></i> List View</a>
|
||||
<a class="dropdown-item"> <i class="fa fa-grip"></i> Grid View</a>
|
||||
<a href="?display=list&sort_key={{options.sort_key}}&sort_dir={{options.sort_dir}}"
|
||||
class="dropdown-item {{#if (eq options.display 'list')}}is-active{{/if}}"
|
||||
><i class="fa fa-list"></i> List View</a>
|
||||
<a href="?display=grid&sort_key={{options.sort_key}}&sort_dir={{options.sort_dir}}"
|
||||
class="dropdown-item {{#if (eq options.display 'grid')}}is-active{{/if}}"> <i class="fa fa-grip"></i> Grid View</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -57,7 +60,7 @@
|
|||
<span class="icon">
|
||||
<i class="fa fa-arrow-up is-small"></i>
|
||||
</span>
|
||||
<span>Sort by Name (asc)</span>
|
||||
<span>Sort by {{ options.sort_key }} ({{ options.sort_dir }})</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
@ -65,12 +68,20 @@
|
|||
</div>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a href="#" class="dropdown-item is-active"> By name ascending</a>
|
||||
<a class="dropdown-item"> By name descending</a>
|
||||
<a class="dropdown-item"> By last modified ascending</a>
|
||||
<a class="dropdown-item"> By last modified descending</a>
|
||||
<a class="dropdown-item"> By size ascending</a>
|
||||
<a class="dropdown-item"> By size ascending</a>
|
||||
{{#each DATA.sort_keys}}
|
||||
<a href="?display={{../options.display}}&sort_key={{this}}&sort_dir=asc"
|
||||
class="dropdown-item {{#if (eq ../options.sort_dir "asc")}}
|
||||
{{#if (eq ../options.sort_key this)}}
|
||||
is-active
|
||||
{{/if}}
|
||||
{{/if}}">By {{this}} ascending</a>
|
||||
<a href="?display={{../options.display}}&sort_key={{this}}&sort_dir=desc"
|
||||
class="dropdown-item {{#if (eq ../options.sort_dir "desc")}}
|
||||
{{#if (eq ../options.sort_key this)}}
|
||||
is-active
|
||||
{{/if}}
|
||||
{{/if}}">By {{this}} descending</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -166,6 +177,7 @@
|
|||
<script>
|
||||
const LIBRARY_ID = "{{ library.id }}";
|
||||
const LIBRARY_PATH = "/{{ parent }}";
|
||||
{{!-- let OPTIONS = JSON.parse(`{{{ options }}}`) --}}
|
||||
</script>
|
||||
<script src="/static/js/add_button.js"></script>
|
||||
<script>
|
||||
|
@ -175,27 +187,6 @@ document.addEventListener('alpine:init', () => {
|
|||
});
|
||||
</script>
|
||||
<script src="//unpkg.com/alpinejs" defer></script>
|
||||
{{!-- <script type="module">
|
||||
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
|
||||
|
||||
createApp({
|
||||
delimiters: ['{%', '%}'],
|
||||
setup() {
|
||||
const message = ref('Hello Vue!')
|
||||
const loaded = ref(false)
|
||||
const touchPrompt = ref(false)
|
||||
return {
|
||||
loaded,
|
||||
message,
|
||||
touchPrompt
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.loaded = true
|
||||
console.info("Vue ready")
|
||||
}
|
||||
}).mount('#app')
|
||||
</script> --}}
|
||||
{{/inline}}
|
||||
{{/layouts/main}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue