Compare commits

...

4 Commits

Author SHA1 Message Date
2ddf210a5f Color scheme for dark mode 2024-06-16 20:17:18 -05:00
bef20ccc5a Styles for homepage 2024-06-16 20:02:20 -05:00
c713fcaae8 Changes to colors 2024-06-16 19:15:53 -05:00
32d131db67 Remove daisy, rewrite login UI 2024-06-16 18:13:03 -05:00
9 changed files with 144 additions and 85 deletions

View File

@ -9,10 +9,10 @@
<title>Solid App</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&family=Inter:wght@100..900&display=swap" rel="stylesheet">
</head>
<body data-theme="black">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

View File

@ -0,0 +1,11 @@
export function ArrowClockwiseIcon(props: {
fill: string,
class?: string,
size?: number,
}) {
return (
<svg class={props.class} width={props.size ?? 32} height={props.size ?? 32} fill={props.fill} viewBox="0 0 256 256">
<path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1-5.66-13.66l17-17-10.55-9.65-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,1,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60l10.93,10L226.34,50.3A8,8,0,0,1,240,56Z" />
</svg>
);
}

View File

@ -0,0 +1,12 @@
export function PlayIcon(props: {
fill: string,
class?: string,
size?: number,
}) {
return (
<svg class={props.class} width={props.size ?? 32} height={props.size ?? 32} fill={props.fill} viewBox="0 0 256 256">
<path d="M240,128a15.74,15.74,0,0,1-7.6,13.51L88.32,229.65a16,16,0,0,1-16.2.3A15.86,15.86,0,0,1,64,216.13V39.87a15.86,15.86,0,0,1,8.12-13.82,16,16,0,0,1,16.2.3L232.4,114.49A15.74,15.74,0,0,1,240,128Z" />
</svg>
);
}

View File

@ -2,8 +2,48 @@
@tailwind components;
@tailwind utilities;
/* Dark mode */
:root {
--c-bg: #09090b;
--c-on-bg: white;
--c-border-1: #a1a1aa;
--c-bg-inversed: rgba(228, 228, 240, 0.75);
--c-primary: #c4b5fd;
--c-on-primary: #09090b;
--c-primary-bg: #1f162c;
--c-primary-bg-2: #332a3f;
--c-on-primary-bg: #f5f3ff;
--c-primary-border: #6b58a1;
}
/* Light mode */
@media (prefers-color-scheme: light) {
:root {
--c-bg: white;
--c-on-bg: #09090b;
--c-border-1: #a1a1aa;
--c-bg-inversed: rgba(39, 39, 42, 0.75);
--c-primary: #7c3aed;
--c-on-primary: white;
--c-primary-bg: #f5f3ff;
--c-primary-bg-2: #ede9fe;
--c-on-primary-bg: #110627;
--c-primary-border: #c4b5fd;
}
}
html {
font-family: "Inter", sans-serif;
font-optical-sizing: auto;
font-variation-settings: "slnt" 0;
background-color: var(--c-bg);
color: var(--c-on-bg);
}

View File

@ -2,6 +2,8 @@ import { batch, createEffect, createMemo, createResource, createSignal, onMount
import { GetAlbumCover, GetRandomAlbums, TriggerAlbumReload } from "../../wailsjs/go/main/App";
import { main } from "../../wailsjs/go/models";
import { createStore } from "solid-js/store";
import { ArrowClockwiseIcon } from "../icons/ArrowClockwiseIcon";
import { PlayIcon } from "../icons/PlayIcon";
type AlbumsData = {
status: "loading" | "ok",
@ -24,7 +26,7 @@ export function Home() {
});
// Loads the random albums from Go.
// This function, when called multiple times, returns the same set.
// This function returns the same set when called multiple times.
const loadAlbums = async() => {
const response = await GetRandomAlbums();
@ -54,16 +56,16 @@ export function Home() {
return (
<div class={`min-h-screen ${hidden() ? "opacity-0" : "opacity-100"} transition-opacity`}>
<h1 class="font-black text-2xl pt-6 pb-4 pl-2">
Random albums
<h1 class="font-title font-black text-2xl pt-6 pb-4 pl-2">
<span class="text-c-primary">Random albums</span>
<button
class="text-xs font-normal mx-1 p-1 rounded underline hover:bg-zinc-900"
class="mx-1 p-1 rounded underline hover:bg-c-primary-bg-2 cursor-pointer"
onClick={triggerAlbumReload}
>
Reload
<ArrowClockwiseIcon class="h-4 w-4 inline-block" fill="var(--c-primary)" />
</button>
</h1>
<div class="pb-4 overflow-scroll whitespace-nowrap">
<div class="pb-1 overflow-x-scroll whitespace-nowrap">
{els}
</div>
</div>
@ -109,22 +111,30 @@ function Album(props: { albums: Array<main.Album | null>, idx: number }) {
const imgOpacity = createMemo(() => (imgReady() && !isNull() ? "opacity-100" : "opacity-0"));
return (
<div class="inline-block mx-2 p-1 w-32 rounded bg-zinc-900">
<div class="w-30 h-30" >
<div class="inline-block mx-1 p-1 w-32 rounded bg-c-primary-bg text-c-on-primary-bg
hover:shadow hover:bg-c-primary-bg-2 transition-shadow group"
>
<div class="w-30 h-30 relative" >
<img
class={`inline-block rounded w-30 h-30 transition-opacity ${imgOpacity()}`}
src={imageBase64()}
alt=""
/>
<button
class="inline-block absolute bottom-1 right-1 bg-[var(--c-bg-inversed)] z-10 cursor-pointer h-8 w-8 rounded-full
hover:bg-c-primary transition-all opacity-0 group-hover:opacity-100"
>
<PlayIcon class="inline-block p-2 h-full w-full" fill="var(--c-primary-bg)" />
</button>
</div>
<div class={`text-sm overflow-hidden overflow-ellipsis pt-1 ${opacity()}`} title={albumName()}>
<div class={`font-title font-medium text-sm overflow-hidden overflow-ellipsis pt-1 ${opacity()}`} title={albumName()}>
{albumName()}
</div>
<div class={`text-xs overflow-hidden overflow-ellipsis ${opacity()}`} title={artistName()}>
{artistName()}
</div>
</div>
);
}

View File

@ -1,14 +1,26 @@
import { Show, createSignal } from "solid-js";
import { Show, createSignal, onMount } from "solid-js";
import { Login as GoLogin } from "../../wailsjs/go/main/App";
import { useNavigate } from "@solidjs/router";
function runAndDelay(ms: number, fn: () => void): Promise<void> {
fn();
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
const delay = (ms: number, fn: () => void) => new Promise((resolve) => setTimeout(() => {
fn();
resolve(null);
}, ms));
export function Login() {
const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal("");
const [server, setServer] = createSignal("");
const [username, setUsername] = createSignal("");
const [password, setPassword] = createSignal("");
const [fade, setFade] = createSignal(false);
const [fadeUI, setFadeUI] = createSignal(false);
const navigate = useNavigate();
const login = async(ev: Event) => {
@ -17,54 +29,55 @@ export function Login() {
setError("");
GoLogin(server(), username(), password())
.then(() => {
setFade(true);
setTimeout(() => {
navigate("/home");
}, 150);
})
.then(() => runAndDelay(150, () => setFadeUI(true)))
.then(() => navigate("/home"))
.catch((err) => {
setError(err);
setLoading(false);
});
};
onMount(() => {
setFadeUI(true);
delay(150, () => setFadeUI(false));
});
return (
<div class={`w-screen h-screen flex items-center justify-center ${fade() ? "opacity-0" : "opacity-100"} transition-opacity`}>
<div class="w-80">
<div class={`w-screen h-screen flex items-center justify-center ${fadeUI() ? "opacity-0" : "opacity-100"} transition-opacity`}>
<div class="max-w-80 px-1 overflow-hidden">
<form onSubmit={login}>
<h1 class="text-center font-black text-xl">Login</h1>
<label class="form-control" for="login-server">
<h1 class="text-center font-title font-black text-xl text-c-primary mb-4">Login to Navidrome</h1>
<label for="login-server">
<div class="label">
<span class="label-text text-xs">Server URL</span>
<span class="font-title label-text text-xs">Server URL:</span>
</div>
<input id="login-server" type="text"
onInput={(e) => setServer(e.target.value)}
class="input input-bordered"
class="border border-c-primary-border rounded bg-c-primary-bg inline-block w-full p-1"
placeholder="https://"
required
disabled={loading()}
/>
</label>
<label class="form-control" for="login-username">
<label for="login-username">
<div class="label">
<span class="label-text text-xs">Username</span>
<span class="font-title label-text text-xs">Username</span>
</div>
<input id="login-username" type="text"
onInput={(e) => setUsername(e.target.value)}
class="input input-bordered"
class="border border-c-primary-border rounded bg-c-primary-bg inline-block w-full p-1"
placeholder="username"
required
disabled={loading()}
/>
</label>
<label class="form-control" for="login-password">
<label for="login-password">
<div class="label">
<span class="label-text text-xs">Password</span>
<span class="font-title label-text text-xs">Password</span>
</div>
<input id="login-password" type="password"
onInput={(e) => setPassword(e.target.value)}
class="input input-bordered"
class="border border-c-primary-border rounded bg-c-primary-bg inline-block w-full p-1"
placeholder="********"
required
disabled={loading()}
@ -72,7 +85,9 @@ export function Login() {
</label>
<br />
<div class="text-center">
<button type="submit" class={`btn btn-primary ${loading() ? "animate-pulse" : ""}`}
<button
type="submit"
class={`rounded font-title bg-c-primary text-c-on-primary py-2 px-4 mt-6 ${loading() ? "animate-pulse" : ""}`}
disabled={loading()}
>
Login

View File

@ -1,5 +1,3 @@
import daisyui from "daisyui";
/** @type {import('tailwindcss').Config} */
export default {
content: [
@ -10,45 +8,20 @@ export default {
spacing: {
"30": "7.5rem",
},
fontFamily: {
"title": ["'Inter Tight'", "Inter", "sans-serif"],
},
colors: {
"c-bg": "var(--c-bg)",
"c-on-bg": "var(--c-on-bg)",
"c-border-1": "var(--c-border-1)",
"c-primary": "var(--c-primary)",
"c-on-primary": "var(--c-on-primary)",
"c-primary-bg": "var(--c-primary-bg)",
"c-primary-bg-2": "var(--c-primary-bg-2)",
"c-on-primary-bg": "var(--c-on-primary-bg)",
"c-primary-border": "var(--c-primary-border)",
},
},
plugins: [
daisyui,
],
daisyui: {
themes: [
"light",
"dark",
"cupcake",
"bumblebee",
"emerald",
"corporate",
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"garden",
"forest",
"aqua",
"lofi",
"pastel",
"fantasy",
"wireframe",
"black",
"luxury",
"dracula",
"cmyk",
"autumn",
"business",
"acid",
"lemonade",
"night",
"coffee",
"winter",
"dim",
"nord",
"sunset",
],
},
};

View File

@ -26,10 +26,8 @@ func main() {
Title: "my-wails-solid",
Width: 1024,
Height: 768,
MinWidth: 1024,
MinHeight: 768,
MaxWidth: 1280,
MaxHeight: 800,
MinWidth: 250,
MinHeight: 400,
DisableResize: false,
Fullscreen: false,
Frameless: false,

View File

@ -23,12 +23,12 @@ var randomAlbums []Album
var serverUrl = ""
var client = resty.New()
// (Tries to) login to a remote navidrome server
// Attempts to login to a remote navidrome server
func (a *App) Login(server, username, password string) (bool, error) {
log.Print("begin Login to server")
// client := resty.New()
// TODO: check server for leading https and trailing /, normalize
// TODO: check server string for leading https and trailing /, normalize
successData := AuthSuccess{}
errorData := AuthError{}