Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better UI for macOS #33

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.3", features = [] }

[dependencies]
tauri = { version = "1.3", features = ["shell-open"] }
tauri = { version = "1.3", features = [ "macos-private-api", "shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sysinfo = "0.29.2"
Expand All @@ -22,6 +22,7 @@ fuzzy-matcher = "*"
rayon = "1.7.0"
dirs = "5.0.1"
notify = "6.0.1"
window-vibrancy = "0.3.2"

[features]
# this feature is used for production builds or when `devPath` points to the filesystem
Expand Down
55 changes: 25 additions & 30 deletions src-tauri/src/filesystem.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use crate::{CachedPath, StateSafe};
use crate::{ CachedPath, StateSafe };
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use serde::{ Deserialize, Serialize };
use std::collections::HashMap;
use std::fs;
use std::fs::{read_dir, File};
use std::fs::{ read_dir, File };
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use sysinfo::{Disk, DiskExt, System, SystemExt};
use std::sync::{ Arc, Mutex };
use sysinfo::{ Disk, DiskExt, System, SystemExt };
use tauri::State;
use walkdir::WalkDir;

const CACHE_FILE_PATH: &str = "./system_cache.json";

const fn bytes_to_gb(bytes: u64) -> u16 {
(bytes / (1e+9 as u64)) as u16
(bytes / (1e9 as u64)) as u16
}

#[derive(Serialize)]
Expand Down Expand Up @@ -90,11 +90,12 @@ impl Volume {

let name = {
let volume_name = disk.name().to_str().unwrap();
match volume_name.is_empty() {
true => "Local Volume",
false => volume_name,
}
.to_string()
(
match volume_name.is_empty() {
true => "Local Volume",
false => volume_name,
}
).to_string()
};

let mountpoint = disk.mount_point().to_path_buf();
Expand All @@ -115,8 +116,7 @@ impl Volume {
fn create_cache(&self, state_mux: &StateSafe) {
let state = &mut state_mux.lock().unwrap();

let volume = state
.system_cache
let volume = state.system_cache
.entry(self.mountpoint.to_string_lossy().to_string())
.or_insert_with(HashMap::new);

Expand All @@ -131,21 +131,19 @@ impl Volume {
let file_path = entry.path().to_string_lossy().to_string();

let walkdir_filetype = entry.file_type();
let file_type = if walkdir_filetype.is_dir() {
"directory"
} else {
"file"
}
.to_string();
let file_type = (
if walkdir_filetype.is_dir() {
"directory"
} else {
"file"
}
).to_string();

let cache_guard = &mut system_cache.lock().unwrap();
cache_guard
.entry(file_name)
.or_insert_with(Vec::new)
.push(CachedPath {
file_path,
file_type,
});
cache_guard.entry(file_name).or_insert_with(Vec::new).push(CachedPath {
file_path,
file_type,
});
});
}
}
Expand All @@ -162,10 +160,7 @@ pub fn save_system_cache(state_mux: &StateSafe) {
let state = &mut state_mux.lock().unwrap();
let serialized_cache = serde_json::to_string(&state.system_cache).unwrap();

let mut file = fs::OpenOptions::new()
.write(true)
.open(CACHE_FILE_PATH)
.unwrap();
let mut file = fs::OpenOptions::new().write(true).open(CACHE_FILE_PATH).unwrap();
file.write_all(serialized_cache.as_bytes()).unwrap();
}

Expand Down
35 changes: 24 additions & 11 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")]

mod filesystem;
mod search;

use filesystem::{get_volumes, open_directory};
use filesystem::{ get_volumes, open_directory };
use search::search_directory;
use serde::{Deserialize, Serialize};
use serde::{ Deserialize, Serialize };
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::{ Arc, Mutex };
use tauri::Manager;
use window_vibrancy::{ apply_blur, apply_vibrancy, NSVisualEffectMaterial };

#[derive(Serialize, Deserialize)]
pub struct CachedPath {
Expand All @@ -26,14 +28,25 @@ pub struct AppState {
pub type StateSafe = Arc<Mutex<AppState>>;

fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
get_volumes,
open_directory,
search_directory
])
tauri::Builder
::default()
.setup(|app| {
let window = app.get_window("main").unwrap();

#[cfg(target_os = "macos")]
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None).expect(
"Unsupported platform! 'apply_vibrancy' is only supported on macOS"
);

#[cfg(target_os = "windows")]
apply_blur(&window, Some((18, 18, 18, 125))).expect(
"Unsupported platform! 'apply_blur' is only supported on Windows"
);

Ok(())
})
.invoke_handler(tauri::generate_handler![get_volumes, open_directory, search_directory])
.manage(Arc::new(Mutex::new(AppState::default())))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

7 changes: 5 additions & 2 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
"resizable": true,
"title": "file-explorer",
"width": 1024,
"height": 678
"height": 678,
"transparent": true,
"decorations": true
}
]
],
"macOSPrivateApi": true
}
}
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function App() {

return (
<div className="p-4">
<div className="flex justify-between pb-5">
<div className="flex justify-between pb-5 ">
<FolderNavigation
onBackArrowClick={onBackArrowClick}
canGoBackward={canGoBackward()}
Expand Down
47 changes: 23 additions & 24 deletions src/components/MainBody/DirectoryContents.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import DirectoryEntity from "./DirectoryEntity";
import {DirectoryContent} from "../../types";
import { DirectoryContent } from "../../types";

interface Props {
content: DirectoryContent[];
onDirectoryClick: (filePath: string) => any;
content: DirectoryContent[];
onDirectoryClick: (filePath: string) => any;
}

export function DirectoryContents({content, onDirectoryClick}: Props) {
return <>
{content.length === 0 ? "There are no files in this directory." : ""}
export function DirectoryContents({ content, onDirectoryClick }: Props) {
return (
<div className=" rounded-lg overflow-hidden">
{content.length === 0 ? "There are no files in this directory." : ""}

{content.map((content, idx) => {
const [fileType, [fileName, filePath]] = Object.entries(content)[0];
{content.map((content, idx) => {
const [fileType, [fileName, filePath]] = Object.entries(content)[0];

return (
<DirectoryEntity
type={fileType === "Directory" ? "directory" : "file"}
onClick={() =>
fileType === "Directory"
? onDirectoryClick(filePath)
: undefined
}
key={idx}
name={fileName}
/>
);
})}

</>;
}
return (
<DirectoryEntity
type={fileType === "Directory" ? "directory" : "file"}
onClick={() =>
fileType === "Directory" ? onDirectoryClick(filePath) : undefined
}
key={idx}
name={fileName}
/>
);
})}
</div>
);
}
43 changes: 25 additions & 18 deletions src/components/MainBody/DirectoryEntity.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import {MouseEventHandler} from "react";
import {DirectoryEntityType} from "../../types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faFile, faFolder } from "@fortawesome/free-solid-svg-icons"
import { MouseEventHandler } from "react";
import { DirectoryEntityType } from "../../types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFile, faFolder } from "@fortawesome/free-solid-svg-icons";

interface Props {
name: string;
type: DirectoryEntityType;
onClick?: MouseEventHandler<HTMLButtonElement>;
name: string;
type: DirectoryEntityType;
onClick?: MouseEventHandler<HTMLButtonElement>;
}

export default function DirectoryEntity({ name, type, onClick }: Props) {
return (
<>
<button className="bg-background hover:bg-darker cursor-pointer w-full h-8 flex" onClick={onClick}>
<div className="mr-1">
<FontAwesomeIcon icon={type == "file" ? faFile : faFolder} size="lg" color={type == "file" ? "gray" : "#FFD54F"} />
</div>
{name}
</button>
</>
)
}
return (
<>
<button
className="bg-background hover:bg-darker cursor-pointer w-full h-[100%] flex p-3"
onClick={onClick}
>
<div className="mr-1">
<FontAwesomeIcon
icon={type == "file" ? faFile : faFolder}
size="lg"
color={type == "file" ? "gray" : "#FFD54F"}
/>
</div>
{name}
</button>
</>
);
}
28 changes: 17 additions & 11 deletions src/components/MainBody/Volumes/VolumeComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import {Volume} from "../../../types";
import {MouseEventHandler} from "react";
import { Volume } from "../../../types";
import { MouseEventHandler } from "react";

interface Props {
volume: Volume;
onClick: MouseEventHandler<HTMLButtonElement>;
volume: Volume;
onClick: MouseEventHandler<HTMLButtonElement>;
}

export default function VolumeComponent({ volume, onClick }: Props) {
return (
<button onClick={onClick} className="p-5 w-56 bg-darker radius rounded cursor-pointer">
<h3>{volume.name} ({volume.mountpoint})</h3>
<progress max="100" value={(volume.used_gb / volume.total_gb) * 100} /> <br/>
{volume.available_gb} GB free of {volume.total_gb} GB
</button>
)
return (
<button
onClick={onClick}
className="p-5 w-82 bg-darker radius rounded cursor-pointer"
>
<h3>
{volume.name} ({volume.mountpoint})
</h3>
<progress max="100" value={(volume.used_gb / volume.total_gb) * 100} />{" "}
<br />
{volume.available_gb} GB free of {volume.total_gb} GB
</button>
);
}
Loading