better theme & location logic

This commit is contained in:
Jamie 2021-12-30 20:40:45 -08:00
parent 1659345d13
commit ef1f0c32ac
14 changed files with 155 additions and 56 deletions

View file

@ -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" />

View file

@ -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" />}

View file

@ -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" />

View file

@ -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>
);

View file

@ -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

View file

@ -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>

View file

@ -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} /> */}

View file

@ -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>
);
};

View file

@ -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 {}

View file

@ -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 {}

View file

@ -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'))
);

View file

@ -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);

View file

@ -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 {

View file

@ -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] }