mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 12:13:27 +00:00
better theme & location logic
This commit is contained in:
parent
1659345d13
commit
ef1f0c32ac
|
@ -61,12 +61,12 @@ export default function App() {
|
|||
}}
|
||||
>
|
||||
<Router>
|
||||
<div className="flex flex-col h-screen overflow-hidden text-gray-900 bg-white border border-gray-200 select-none rounded-xl dark:border-gray-550 dark:text-white dark:bg-gray-800">
|
||||
<div className="flex flex-row h-screen overflow-hidden text-gray-900 bg-white border border-gray-200 select-none rounded-xl dark:border-gray-500 dark:text-white dark:bg-gray-750">
|
||||
<DebugGlobalStore />
|
||||
<Sidebar />
|
||||
<div className="flex flex-col w-full min-h-full">
|
||||
<TopBar />
|
||||
<div className="flex flex-row min-h-full">
|
||||
<Sidebar />
|
||||
<div className="relative flex w-full bg-white dark:bg-gray-800">
|
||||
<div className="relative flex w-full">
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Redirect to="/explorer" />
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
PlusIcon,
|
||||
ServerIcon
|
||||
} from '@heroicons/react/solid';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
Book,
|
||||
|
@ -30,6 +31,7 @@ import {
|
|||
import React, { useEffect } from 'react';
|
||||
import { NavLink, NavLinkProps } from 'react-router-dom';
|
||||
import { useLocations } from '../../store/locations';
|
||||
import { TrafficLights } from '../os/TrafficLights';
|
||||
import { Button } from '../primitive';
|
||||
import { Dropdown } from '../primitive/Dropdown';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
|
@ -60,12 +62,29 @@ const Heading: React.FC<{}> = ({ children }) => (
|
|||
export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
const locations = useLocations();
|
||||
return (
|
||||
<div className="w-46 flex flex-col flex-wrap flex-shrink-0 min-h-full bg-gray-50 dark:bg-gray-650 !bg-opacity-60 border-gray-100 border-r dark:border-gray-600 px-3 py-1">
|
||||
<div className="w-46 flex flex-col flex-wrap flex-shrink-0 min-h-full bg-gray-50 dark:bg-gray-950 !bg-opacity-60 border-gray-100 border-r dark:border-gray-600 px-3 py-1">
|
||||
<div className="mt-2 mb-1 -ml-1">
|
||||
<TrafficLights
|
||||
onClose={appWindow.close}
|
||||
onFullscreen={appWindow.maximize}
|
||||
onMinimize={appWindow.minimize}
|
||||
className="p-1.5"
|
||||
/>
|
||||
</div>
|
||||
<Dropdown
|
||||
buttonProps={{
|
||||
justifyLeft: true,
|
||||
className:
|
||||
'mb-1 rounded !bg-gray-50 border-gray-150 hover:!bg-gray-1000 flex-shrink-0 w-[175px] dark:!bg-gray-600 dark:hover:!bg-gray-550 dark:!border-gray-550 dark:hover:!border-gray-500',
|
||||
`mb-1 shadow-xs rounded flex-shrink-0 w-[175px]
|
||||
!bg-gray-50
|
||||
border-gray-150
|
||||
hover:!bg-gray-1000
|
||||
|
||||
dark:!bg-gray-650
|
||||
dark:hover:!bg-gray-550
|
||||
|
||||
dark:!border-gray-600
|
||||
dark:hover:!border-gray-500`,
|
||||
variant: 'gray'
|
||||
}}
|
||||
// buttonIcon={<Book weight="bold" className="w-4 h-4 mt-0.5 mr-1" />}
|
||||
|
|
|
@ -56,16 +56,9 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
<>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="flex h-[2.95rem] -mt-0.5 max-w z-50 rounded-t-2xl flex-shrink-0 items-center border-b bg-gray-50 dark:bg-gray-650 border-gray-100 dark:border-gray-600 !bg-opacity-60 backdrop-blur "
|
||||
className="flex h-[2.95rem] -mt-0.5 max-w z-50 pl-3 rounded-tr-2xl items-center border-b bg-gray-50 dark:bg-gray-750 border-gray-100 dark:border-gray-600 !bg-opacity-60 backdrop-blur shadow-xl"
|
||||
>
|
||||
<div className="ml-1 mr-32">
|
||||
<TrafficLights
|
||||
onClose={appWindow.close}
|
||||
onFullscreen={appWindow.maximize}
|
||||
onMinimize={appWindow.minimize}
|
||||
className="p-1.5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TopBarButton icon={ChevronLeftIcon} onClick={goBack} />
|
||||
<TopBarButton icon={ChevronRightIcon} />
|
||||
{/* <div className="flex mx-8 space-x-[1px]">
|
||||
|
@ -82,7 +75,7 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
<div className="relative flex h-7">
|
||||
<input
|
||||
placeholder="Search"
|
||||
className="w-32 h-[30px] focus:w-52 text-sm p-3 rounded-lg outline-none focus:ring-2 placeholder-gray-400 dark:placeholder-gray-500 bg-gray-50 border border-gray-250 dark:bg-gray-700 dark:border-gray-600 focus:ring-gray-100 dark:focus:ring-gray-600 transition-all"
|
||||
className="w-32 h-[30px] focus:w-52 text-sm p-3 rounded-lg outline-none focus:ring-2 placeholder-gray-400 dark:placeholder-gray-500 bg-gray-50 border border-gray-250 dark:bg-gray-850 dark:border-gray-600 focus:ring-gray-100 dark:focus:ring-gray-600 transition-all"
|
||||
/>
|
||||
<div className="space-x-1 absolute top-[2px] right-1">
|
||||
<Shortcut chars="⌘S" />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { useFocusState } from '../../hooks/useFocusState';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
|
||||
export interface TrafficLightsProps extends DefaultProps {
|
||||
|
@ -9,17 +10,19 @@ export interface TrafficLightsProps extends DefaultProps {
|
|||
}
|
||||
|
||||
export const TrafficLights: React.FC<TrafficLightsProps> = (props) => {
|
||||
const [focused] = useFocusState()
|
||||
return (
|
||||
<div className={clsx('flex flex-row space-x-2 px-3', props.className)}>
|
||||
<Light mode="close" action={props.onClose} />
|
||||
<Light mode="minimize" action={props.onMinimize} />
|
||||
<Light mode="fullscreen" action={props.onFullscreen} />
|
||||
<div className={clsx('flex flex-row space-x-2 px-2 group', props.className)}>
|
||||
<Light mode="close" action={props.onClose} focused={focused} />
|
||||
<Light mode="minimize" action={props.onMinimize} focused={focused} />
|
||||
<Light mode="fullscreen" action={props.onFullscreen} focused={focused} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface LightProps {
|
||||
mode: 'close' | 'minimize' | 'fullscreen';
|
||||
focused: boolean;
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
|
@ -27,10 +30,10 @@ const Light: React.FC<LightProps> = (props) => {
|
|||
return (
|
||||
<div
|
||||
onClick={props.action}
|
||||
className={clsx('w-[12px] h-[12px] rounded-full', {
|
||||
'bg-red-400': props.mode == 'close',
|
||||
'bg-green-400': props.mode == 'fullscreen',
|
||||
'bg-yellow-400': props.mode == 'minimize'
|
||||
className={clsx('w-[13px] h-[13px] rounded-full bg-gray-500', {
|
||||
'!bg-red-400': props.mode == 'close' && props.focused,
|
||||
'!bg-green-400': props.mode == 'fullscreen' && props.focused,
|
||||
'!bg-yellow-400': props.mode == 'minimize' && props.focused
|
||||
})}
|
||||
></div>
|
||||
);
|
||||
|
|
|
@ -15,14 +15,14 @@ const variants = {
|
|||
hover:bg-gray-100
|
||||
active:bg-gray-50
|
||||
dark:bg-gray-650
|
||||
dark:hover:bg-gray-650
|
||||
dark:hover:bg-gray-600
|
||||
dark:active:bg-gray-700
|
||||
dark:active:opacity-80
|
||||
|
||||
border-gray-100
|
||||
hover:border-gray-200
|
||||
active:border-gray-200
|
||||
dark:border-gray-700
|
||||
dark:border-gray-600
|
||||
dark:active:border-gray-600
|
||||
dark:hover:border-gray-600
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ interface StatItemProps {
|
|||
|
||||
const StatItem: React.FC<StatItemProps> = (props) => {
|
||||
return (
|
||||
<div className="flex flex-col p-4 mt-2 rounded-md bg-gray-50 dark:bg-gray-800">
|
||||
<div className="flex flex-col p-4 mt-2 rounded-md bg-gray-50 dark:bg-gray-650">
|
||||
<span className="text-sm text-gray-400">{props.name}</span>
|
||||
<span className="text-2xl font-bold">
|
||||
{props.value}
|
||||
|
@ -23,7 +23,7 @@ const StatItem: React.FC<StatItemProps> = (props) => {
|
|||
export const OverviewScreen: React.FC<{}> = (props) => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full p-5 bg-white dark:bg-gray-900">
|
||||
<div className="flex flex-col w-full h-full px-5 py-3">
|
||||
<div className="flex flex-wrap space-x-2">
|
||||
<StatItem name="Total capacity" value="26.5" unit="TB" />
|
||||
<StatItem name="Index size" value="103" unit="MB" />
|
||||
|
@ -32,7 +32,7 @@ export const OverviewScreen: React.FC<{}> = (props) => {
|
|||
<StatItem name="Total at-risk" value="1.5" unit="TB" />
|
||||
<StatItem name="Total backed up" value="25.3" unit="TB" />
|
||||
</div>
|
||||
<hr className="my-5 dark:border-gray-800" />
|
||||
<hr className="my-5 dark:border-gray-600" />
|
||||
<div className='-mt-[1px] space-x-2'>
|
||||
<Tag color='red'>Videos</Tag>
|
||||
<Tag color='orange'>DSLR Photos</Tag>
|
||||
|
@ -42,7 +42,7 @@ export const OverviewScreen: React.FC<{}> = (props) => {
|
|||
<Tag color='blue'>Documents</Tag>
|
||||
<Tag color='purple'>Repositories</Tag>
|
||||
</div>
|
||||
<hr className="my-5 dark:border-gray-800" />
|
||||
<hr className="my-5 dark:border-gray-600" />
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -26,7 +26,7 @@ const config = useAppState()
|
|||
]);
|
||||
|
||||
return (
|
||||
<div className='bg-gray-900'>
|
||||
<div className=''>
|
||||
<div className="px-5">
|
||||
{/* <FileList files={dummyIFile} /> */}
|
||||
{/* <Spline scene={WINDOWS_SCENE} /> */}
|
||||
|
|
|
@ -2,8 +2,30 @@ import React from 'react';
|
|||
|
||||
export const SpacesScreen: React.FC<{}> = (props) => {
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full bg-white dark:bg-gray-900 p-5">
|
||||
<h1 className=" font-bold text-xl">Spaces</h1>
|
||||
<div className="flex flex-col w-full h-full p-5">
|
||||
<h1 className="text-xl font-bold ">Spaces</h1>
|
||||
|
||||
<div className="flex flex-wrap p-2 my-3 space-x-2 bg-black rounded">
|
||||
<div className="w-10 h-10 rounded bg-gray-950"/>
|
||||
<div className="w-10 h-10 bg-gray-900 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-850"/>
|
||||
<div className="w-10 h-10 bg-gray-800 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-750"/>
|
||||
<div className="w-10 h-10 bg-gray-700 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-650"/>
|
||||
<div className="w-10 h-10 bg-gray-600 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-550"/>
|
||||
<div className="w-10 h-10 bg-gray-400 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-450"/>
|
||||
<div className="w-10 h-10 bg-gray-400 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-350"/>
|
||||
<div className="w-10 h-10 bg-gray-300 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-250"/>
|
||||
<div className="w-10 h-10 bg-gray-200 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-150"/>
|
||||
<div className="w-10 h-10 bg-gray-100 rounded"/>
|
||||
<div className="w-10 h-10 rounded bg-gray-50"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct Model {
|
|||
pub id: u32,
|
||||
pub path: String,
|
||||
pub rule: PathRule,
|
||||
pub location_id: String,
|
||||
pub location_id: u32,
|
||||
#[ts(type = "string")]
|
||||
pub date_created: Option<NaiveDateTime>,
|
||||
#[ts(type = "string")]
|
||||
|
@ -40,7 +40,7 @@ pub enum Relation {
|
|||
from = "Column::LocationId",
|
||||
to = "super::locations::Column::Id"
|
||||
)]
|
||||
Library,
|
||||
Locations,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
|
@ -24,10 +24,9 @@ pub struct Model {
|
|||
pub is_ejectable: bool,
|
||||
pub is_root_filesystem: bool,
|
||||
pub is_online: bool,
|
||||
pub library_id: String,
|
||||
pub client_id: String,
|
||||
pub library_id: u32,
|
||||
pub client_id: u32,
|
||||
|
||||
pub watch_active: bool,
|
||||
#[ts(type = "string")]
|
||||
pub date_created: Option<NaiveDateTime>,
|
||||
#[ts(type = "string")]
|
||||
|
@ -48,6 +47,9 @@ pub enum Relation {
|
|||
to = "super::client::Column::Id"
|
||||
)]
|
||||
Client,
|
||||
// TODO: fix??????
|
||||
// #[sea_orm(has_many = "super::location_paths::Entity")]
|
||||
// LocationPaths,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
|
@ -31,7 +31,6 @@ CREATE TABLE IF NOT EXISTS locations (
|
|||
is_ejectable BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_root_filesystem BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_online BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
watch_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
date_created DATE NOT NULL DEFAULT (datetime('now')),
|
||||
last_indexed DATE NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
|
|
@ -1,12 +1,35 @@
|
|||
use crate::db::{connection::DB_INSTANCE, entity::file};
|
||||
use crate::db::{connection::DB_INSTANCE, entity::file, entity::location_paths, entity::locations};
|
||||
use crate::file::{checksum::create_meta_checksum, init};
|
||||
use crate::util::time;
|
||||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use sea_orm::QueryFilter;
|
||||
use sea_orm::{entity::*, QueryOrder};
|
||||
use std::{collections::HashMap, ffi::OsStr, fs, path::Path, path::PathBuf, time::Instant};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use super::locations::get_location_and_paths;
|
||||
use super::watcher::watch_dir;
|
||||
|
||||
pub async fn scan_paths(location_id: u32) -> Result<()> {
|
||||
// get location by location_id from db and include location_paths
|
||||
let (_location, location_paths) = get_location_and_paths(location_id).await?;
|
||||
|
||||
// loop over and scan() paths
|
||||
if !location_paths.is_empty() {
|
||||
for location_path in location_paths {
|
||||
if location_path.rule != location_paths::PathRule::Exclude {
|
||||
scan(&location_path.path).await?;
|
||||
}
|
||||
if location_path.rule != location_paths::PathRule::NoWatch {
|
||||
watch_dir(&location_path.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// creates a vector of valid path buffers from a directory
|
||||
pub async fn scan(path: &str) -> Result<()> {
|
||||
println!("Scanning directory: {}", &path);
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use super::init;
|
||||
use crate::{
|
||||
db::{connection::DB_INSTANCE, entity::locations},
|
||||
db::{
|
||||
connection::DB_INSTANCE,
|
||||
entity::{file, location_paths, locations},
|
||||
},
|
||||
native::methods::get_mounts,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::Utc;
|
||||
use sea_orm::EntityTrait;
|
||||
use sea_orm::ColumnTrait;
|
||||
use sea_orm::Set;
|
||||
use sea_orm::{ActiveModelTrait, QueryOrder};
|
||||
use sea_orm::{EntityTrait, QueryFilter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::io::Write;
|
||||
|
@ -18,6 +22,34 @@ struct DotSpaceDrive {
|
|||
location_id: u32,
|
||||
}
|
||||
|
||||
pub async fn get_location_and_paths(
|
||||
location_id: u32,
|
||||
) -> Result<(locations::Model, Vec<location_paths::Model>)> {
|
||||
let db = DB_INSTANCE.get().unwrap();
|
||||
|
||||
// get location by location_id from db and include location_paths
|
||||
let location = match locations::Entity::find()
|
||||
.filter(file::Column::Id.eq(location_id))
|
||||
.one(db)
|
||||
.await
|
||||
{
|
||||
Ok(location) => location,
|
||||
Err(_) => return Err(anyhow!("location_not_found")),
|
||||
};
|
||||
|
||||
// get location paths
|
||||
let location_paths = match location_paths::Entity::find()
|
||||
.filter(location_paths::Column::LocationId.eq(location_id))
|
||||
.all(db)
|
||||
.await
|
||||
{
|
||||
Ok(location_paths) => location_paths,
|
||||
Err(_) => vec![],
|
||||
};
|
||||
|
||||
Ok((location.unwrap(), location_paths))
|
||||
}
|
||||
|
||||
pub async fn create_location(path: &str) -> Result<()> {
|
||||
let db = DB_INSTANCE.get().unwrap();
|
||||
let primary_library = init::get_primary_library(&db).await?;
|
||||
|
@ -54,17 +86,23 @@ pub async fn create_location(path: &str) -> Result<()> {
|
|||
available_capacity: Set(mount.available_capacity.try_into().unwrap()),
|
||||
is_ejectable: Set(mount.is_ejectable),
|
||||
is_removable: Set(mount.is_removable),
|
||||
library_id: Set(primary_library.id.to_string()),
|
||||
watch_active: Set(true),
|
||||
library_id: Set(primary_library.id),
|
||||
date_created: Set(Some(Utc::now().naive_utc())),
|
||||
last_indexed: Set(Some(Utc::now().naive_utc())),
|
||||
is_root_filesystem: Set(mount.is_root_filesystem),
|
||||
is_online: Set(true),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
location.save(db).await?;
|
||||
|
||||
// insert root path as location_path to database
|
||||
let location_path = location_paths::ActiveModel {
|
||||
location_id: Set(next_location_id),
|
||||
path: Set(path.to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
location_path.save(db).await?;
|
||||
|
||||
// write a file called .spacedrive to path containing the location id in JSON format
|
||||
let mut dotfile = std::fs::File::create(format!("{}/.spacedrive", path))?;
|
||||
let data = DotSpaceDrive {
|
||||
|
|
|
@ -40,16 +40,16 @@ module.exports = {
|
|||
350: '#A6AABF',
|
||||
400: '#9196A8',
|
||||
450: '#71758A',
|
||||
500: '#505468',
|
||||
550: '#434656',
|
||||
600: '#323846',
|
||||
650: '#222630',
|
||||
700: '#1C202A',
|
||||
750: '#282A34',
|
||||
800: '#1C1E26',
|
||||
850: '#30303E',
|
||||
900: '#15171F',
|
||||
950: '#12141A'
|
||||
500: '#2E313B',
|
||||
550: '#2C2E39',
|
||||
600: '#15191E',
|
||||
650: '#12141A',
|
||||
700: '#121317',
|
||||
750: '#0D0E11',
|
||||
800: '#0C0C0F',
|
||||
850: '#08090D',
|
||||
900: '#060609',
|
||||
950: '#030303'
|
||||
}
|
||||
}
|
||||
// fontFamily: { sans: ['Inter', ...defaultTheme.fontFamily.sans] }
|
||||
|
|
Loading…
Reference in a new issue