21 feature app redesign #23

Open
tristonarmstrong wants to merge 18 commits from 21-feature-app-redesign into main
14 changed files with 618 additions and 217 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -14,8 +14,8 @@
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1", "@tauri-apps/api": "^1",
"kaioken": "^0.10.5", "kaioken": "^0.17.0",
"vite-plugin-kaioken": "^0.0.7" "vite-plugin-kaioken": "^0.3.9"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1", "@tauri-apps/cli": "^1",

433
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,9 +11,10 @@ edition = "2021"
tauri-build = { version = "1", features = [] } tauri-build = { version = "1", features = [] }
[dependencies] [dependencies]
tauri = { version = "1", features = [ "fs-read-file", "fs-create-dir", "fs-exists", "fs-write-file", "path-all", "shell-open"] } tauri = { version = "1", features = [ "macos-private-api", "fs-read-file", "fs-create-dir", "fs-exists", "fs-write-file", "path-all", "shell-open"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
window-vibrancy = "0.4.0"
[features] [features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!

View File

@ -1,15 +1,25 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command use tauri::Manager;
#[tauri::command] use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial};
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet]) .setup(|app| {
let window = app.get_window("main").unwrap();
#[cfg(target_os = "macos")]
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, Some(16.0))
.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![greet])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
} }

View File

@ -32,13 +32,15 @@
"all": true "all": true
} }
}, },
"macOSPrivateApi": true,
"windows": [ "windows": [
{ {
"title": "KlectrRadio", "title": "KlectrRadio",
"width": 300, "width": 800,
"height": 600, "height": 600,
"decorations": true, "resizable": true,
"resizable": true "minWidth": 800,
"minHeight": 600
} }
], ],
"security": { "security": {

View File

@ -1,9 +1,7 @@
import { Route, Router, useEffect } from "kaioken" import { useEffect } from "kaioken"
import Main from "./pages/Main"
import { useStorage } from "./hooks/storageStores" import { useStorage } from "./hooks/storageStores"
import { useStationsStore } from "./hooks/stationStores" import { useStationsStore } from "./hooks/stationStores"
import Add from "./pages/Add" import { Popular, Navigation, LocalRadio, RecommendedRadio, MusicPlayer } from "./components"
import Player from "./pages/Player"
export function App() { export function App() {
const { getStationsFile } = useStorage() const { getStationsFile } = useStorage()
@ -16,11 +14,37 @@ export function App() {
}, []) }, [])
return ( return (
<Router basePath=""> <div className="flex flex-row gap-2 px-2">
<Route path="/" element={Main} />
<Route path="/add" element={Add} /> {/* Left Section __________*/}
<Route path="/player" element={Player} /> <div className="pt-2 min-h-[98vh] max-h-[98vh] min-w-[150px] flex flex-col gap-4 justify-start">
</Router> <div className="w-full flex justify-center">
<input type="text" className="rounded-md border p-1 w-full" placeholder="Search" />
</div>
<div>
<h2 className="text-gray-500 text-sm">Music</h2>
<Navigation />
</div>
<button className="px-4 border border-blue-500 rounded-md text-blue-500 hover:bg-blue-100 w-full">Create New +</button>
</div>
{/* Right Section __________*/}
<div className="flex-1 min-h-[98vh] max-h-[98vh] flex h-0 p-2 min-w-0">
<div className="bg-gray-200 w-full border rounded-xl p-4 flex-col flex gap-4">
<MusicPlayer />
<Popular />
{/* Bottom Section __________*/}
<div id="bottom" className="flex-1 rounded-xl flex flex-row gap-4 min-h-0">
<LocalRadio />
<RecommendedRadio />
</div>
</div>
</div>
</div>
) )
} }

5
src/components/index.ts Normal file
View File

@ -0,0 +1,5 @@
export * from "./music_player"
export * from "./popular"
export * from "./navigation"
export * from "./local_radio"
export * from "./recommended_radio"

View File

@ -0,0 +1,90 @@
export function LocalRadio() {
return (
<div id="bottom-l" className="flex flex-col min-h-0 overflow-y-hidden font-bold p-2 bg-white flex-1 rounded-xl shadow-md gap-2">
<h2 >Local Radio</h2>
<div className="flex flex-col gap-4 overflow-y-scroll h-full">
{dummyData.map(x => (
<div className="flex flex-row min-w-[100px] gap-2 items-center">
<img src={x.image} width={50} className="rounded-xl" />
<h3 className="text-xl text-gray-500">{x.title}</h3>
</div>
))}
</div>
</div>
)
}
const dummyData = [
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
}
]

View File

@ -0,0 +1,66 @@
export function MusicPlayer() {
return (
<div className="flex justify-between p-2 bg-gray-300 flex-[0.5] rounded-xl max-h-[100px] gap-2 items-center pb-5">
<div className="flex gap-2 items-center flex-[0.5]">
<img
width={50}
className="rounded-xl"
src="https://www.thispersondoesnotexist.com"
alt="station art"
/>
<div className="flex flex-col" style={{ overflowX: 'hidden' }}>
<p className="text-sm" style={{ textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>Camden to Chinatown</p>
<p className="text-sm" style={{ textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>Loafy Building, Raimu</p>
</div>
</div>
<div className="flex-1 flex flex-col items-center">
<div className="flex gap-1 justify-center">
<svg
id="playbutton"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-play w-5"
>
<polygon points="6 3 20 12 6 21 6 3" />
</svg>
</div>
<div className="flex gap-1">
<p>0:40</p>
<input type="range" />
<p>1:44</p>
</div>
</div>
<div className="flex flex-row flex-[0.5] gap-1 justify-end items-end">
<svg
id="volume"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-volume-1 w-5"
>
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
<path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
</svg>
<input type="range" />
</div>
</div>
)
}

View File

@ -0,0 +1,18 @@
export function Navigation() {
return (
<ul>
<li className="flex gap-2 hover:bg-blue-100 rounded-md p-1 cursor-pointer hover:text-blue-500">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" className="lucide lucide-radio-tower w-[15px]">
<path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9" />
<path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5" />
<circle cx="12" cy="9" r="2" />
<path d="M16.2 4.8c2 2 2.26 5.11.8 7.47" />
<path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1" />
<path d="M9.5 18h5" />
<path d="m8 22 4-11 4 11" />
</svg>Radio
</li>
</ul>
)
}

View File

@ -0,0 +1,67 @@
export function Popular() {
return (
<div className="bg-gray-300 flex-1 rounded-xl flex flex-col max-h-[200px] overflow-hidden">
<div id="middle-main" className="font-bold p-4 bg-white flex-1 rounded-xl shadow-md flex flex-col gap-2">
<h2>Popular</h2>
<div className="flex gap-4 overflow-y-scroll">
{dummyData.map(x => (
<div className="flex flex-col min-w-[100px]">
<img src={x.image} width={100} className="rounded-xl" />
<p className="text-[.8rem] text-gray-500">{x.title}</p>
<small className="text-[.6rem] text-gray-300">{x.location}</small>
</div>
))}
</div>
</div>
</div>
)
}
const dummyData = [
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
]

View File

@ -0,0 +1,65 @@
export function RecommendedRadio() {
return (
<div id="bottom-r" className="flex-col flex font-bold p-2 bg-white flex-1 rounded-xl shadow-md gap-2">
<h2>Recommended For You</h2>
<div className="flex flex-col gap-4 overflow-y-scroll h-full">
{dummyData.map(x => (
<div className="flex flex-row min-w-[100px] items-center gap-2">
<img src={x.image} width={50} className="rounded-xl" />
<h3 className="text-xl text-gray-500">{x.title}</h3>
</div>
))}
</div>
</div>
)
}
const dummyData = [
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
{
location: 'Florida, USA',
title: '93.3 FLZ',
image: 'https://thispersondoesnotexist.com'
},
]

View File

@ -9,8 +9,16 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
/* background-color: #000022dd; */ @apply bg-gray-100;
@apply bg-gray-100 mt-6; }
html,
body,
#root {
margin: 0 !important;
overlay: hidden;
max-width: 100vw;
max-height: 100vh;
} }
.paper { .paper {