Compare commits

...

4 Commits

5 changed files with 117 additions and 28 deletions

View File

@ -95,7 +95,6 @@ func loadAlbumCover(albumId string) {
imgBytes := response.Body() imgBytes := response.Body()
// Write the image to cache // Write the image to cache
log.Printf("write %s: %+v", albumId, imgBytes[:10])
err = os.WriteFile(albumCacheFile, imgBytes, 0644) err = os.WriteFile(albumCacheFile, imgBytes, 0644)
if err != nil { if err != nil {
coverInfo.Error = errors.New("error writing album cover to disk") coverInfo.Error = errors.New("error writing album cover to disk")
@ -108,6 +107,10 @@ func loadAlbumCover(albumId string) {
// Tries to load the album cover // Tries to load the album cover
func (a *App) GetAlbumCover(albumId string) ([]byte, error) { func (a *App) GetAlbumCover(albumId string) ([]byte, error) {
if albumId == "" {
return nil, nil
}
cacheMutex.Lock() cacheMutex.Lock()
coverInfo, ok := albumCoverCacheInfo[albumId] coverInfo, ok := albumCoverCacheInfo[albumId]
cacheMutex.Unlock() cacheMutex.Unlock()
@ -130,6 +133,5 @@ func (a *App) GetAlbumCover(albumId string) ([]byte, error) {
return nil, errors.New("error reading cover file") return nil, errors.New("error reading cover file")
} }
log.Printf("%s : %+v", albumId, bytes[:10])
return bytes, nil return bytes, nil
} }

View File

@ -1,52 +1,128 @@
import { For, createMemo, createResource, createSignal, onMount } from "solid-js"; import { batch, createEffect, createMemo, createResource, createSignal, onMount } from "solid-js";
import { GetAlbumCover, GetRandomAlbums } from "../../wailsjs/go/main/App"; import { GetAlbumCover, GetRandomAlbums, TriggerAlbumReload } from "../../wailsjs/go/main/App";
import { main } from "../../wailsjs/go/models"; import { main } from "../../wailsjs/go/models";
import { createStore } from "solid-js/store";
type AlbumsData = {
status: "loading" | "ok",
albums: Array<main.Album | null>
}
export function Home() { export function Home() {
const [hidden, setHidden] = createSignal(true); const [hidden, setHidden] = createSignal(true);
const [albums] = createResource(GetRandomAlbums);
const [albumsStore, setAlbumsStore] = createStore<AlbumsData>({
status: "loading",
albums: new Array(20).fill(null),
});
onMount(() => { onMount(() => {
// Fade in the UI // Fade in the UI
setTimeout(() => setHidden(false), 150); setTimeout(() => setHidden(false), 150);
// Load the albums
loadAlbums();
}); });
// Loads the random albums from Go.
// This function, when called multiple times, returns the same set.
const loadAlbums = async() => {
const response = await GetRandomAlbums();
// Update the albums
batch(() => {
for (let i = 0; i < response.length; i += 1) {
const album = response[i];
setAlbumsStore("albums", i, album);
}
});
};
const triggerAlbumReload = () => {
// Assign all albums to null
batch(() => {
for (let i = 0; i < 20; i += 1) {
setAlbumsStore("albums", i, null);
}
});
TriggerAlbumReload();
setTimeout(loadAlbums, 50);
};
const els = albumsStore.albums.map((_, idx) => (<Album albums={albumsStore.albums} idx={idx} />));
return ( return (
<div class={`min-h-screen ${hidden() ? "opacity-0" : "opacity-100"} transition-opacity`}> <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> <h1 class="font-black text-2xl pt-6 pb-4 pl-2">
Random albums
<button
class="text-xs font-normal mx-1 p-1 rounded underline hover:bg-zinc-900"
onClick={triggerAlbumReload}
>
Reload
</button>
</h1>
<div class="pb-4 overflow-scroll whitespace-nowrap"> <div class="pb-4 overflow-scroll whitespace-nowrap">
<For each={albums()}> {els}
{(album) => <Album album={album} />}
</For>
</div> </div>
</div> </div>
); );
} }
function Album(props: { album: main.Album }) { function Album(props: { albums: Array<main.Album | null>, idx: number }) {
const [coverBytes] = createResource(async() => await GetAlbumCover(props.album.id)); const [coverBytes, {mutate, refetch}] = createResource<Array<number> | null>(async() => GetAlbumCover(props.albums[props.idx]?.id ?? ""));
const [albumName, setAlbumName] = createSignal("");
const [artistName, setArtistName] = createSignal("");
const [imageBase64, setImageBase64] = createSignal("");
const [imgReady, setImgReady] = createSignal(false);
const base64Image = createMemo(() => { const album = createMemo(() => props.albums[props.idx]);
if (coverBytes.state !== "ready") return "";
// At runtime this is a string, not a number array createEffect(() => {
const bytes = coverBytes() as unknown as string; const a = album();
return `data:;base64,${bytes}`; if (a === null) {
setImgReady(false);
mutate(null);
return;
} else {
setAlbumName(a.name);
setArtistName(a.albumArtist);
}
refetch();
}); });
createEffect(() => {
const status = coverBytes.state;
if (status === "ready") {
const bytes = coverBytes();
if (bytes === null) return;
console.log("new image pushed, ");
setImageBase64(`data:;base64,${coverBytes()}`);
setImgReady(true);
}
});
const isNull = createMemo(() => !album());
const opacity = createMemo(() => `${isNull() ? "opacity-0" : "opacity-100"} transition-opacity`);
const imgOpacity = createMemo(() => (imgReady() && !isNull() ? "opacity-100" : "opacity-0"));
return ( return (
<div class="inline-block mx-2 p-1 w-32 rounded bg-zinc-900"> <div class="inline-block mx-2 p-1 w-32 rounded bg-zinc-900">
<div class="w-30 h-30" >
<img <img
class="inline-block rounded w-30 h-30 min-w-30 min-h-30" class={`inline-block rounded w-30 h-30 transition-opacity ${imgOpacity()}`}
src={base64Image()} src={imageBase64()}
alt="" alt=""
/> />
<br />
<div class="text-sm overflow-hidden overflow-ellipsis pt-1">
{props.album.name}
</div> </div>
<div class="text-xs overflow-hidden overflow-ellipsis opacity-50">
{props.album.albumArtist} <div class={`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>
</div> </div>

View File

@ -9,3 +9,5 @@ export function GetRandomAlbums():Promise<Array<main.Album>>;
export function Greet(arg1:string):Promise<string>; export function Greet(arg1:string):Promise<string>;
export function Login(arg1:string,arg2:string,arg3:string):Promise<boolean>; export function Login(arg1:string,arg2:string,arg3:string):Promise<boolean>;
export function TriggerAlbumReload():Promise<void>;

View File

@ -17,3 +17,7 @@ export function Greet(arg1) {
export function Login(arg1, arg2, arg3) { export function Login(arg1, arg2, arg3) {
return window['go']['main']['App']['Login'](arg1, arg2, arg3); return window['go']['main']['App']['Login'](arg1, arg2, arg3);
} }
export function TriggerAlbumReload() {
return window['go']['main']['App']['TriggerAlbumReload']();
}

View File

@ -55,7 +55,6 @@ func (a *App) Login(server, username, password string) (bool, error) {
// Set global session // Set global session
LoggedUser = successData LoggedUser = successData
// Begin to load the list of albums on the background // Begin to load the list of albums on the background
randomAlbumWaitGroup.Add(1)
go loadAlbums(server) go loadAlbums(server)
return true, nil return true, nil
@ -68,6 +67,11 @@ func (a *App) Login(server, username, password string) (bool, error) {
} }
} }
// Triggers a reload of random albums
func (a *App) TriggerAlbumReload() {
go loadAlbums(serverUrl)
}
// Waits for the random albums to be loaded, and returns them. // Waits for the random albums to be loaded, and returns them.
// This function assumes that the random albums are being loaded in the background. // This function assumes that the random albums are being loaded in the background.
func (a *App) GetRandomAlbums() ([]Album, error) { func (a *App) GetRandomAlbums() ([]Album, error) {
@ -80,9 +84,10 @@ func (a *App) GetRandomAlbums() ([]Album, error) {
// Loads a list of random albums from the server. // Loads a list of random albums from the server.
func loadAlbums(serverUrl string) { func loadAlbums(serverUrl string) {
randomAlbumWaitGroup.Add(1)
defer randomAlbumWaitGroup.Done() defer randomAlbumWaitGroup.Done()
log.Print("begin loadAlbums") log.Print("begin loadAlbums")
client := resty.New()
var errorData AuthError var errorData AuthError
response, err := client.R(). response, err := client.R().