UI changes (now responsive) && fixed YT recommendations method
This commit is contained in:
@@ -1,91 +1,38 @@
|
||||
<script lang="ts">
|
||||
import SearchBar from '$lib/components/util/searchBar.svelte'
|
||||
import type { LayoutData } from './$types'
|
||||
import NavTab from '$lib/components/util/navTab.svelte'
|
||||
import MixTab from '$lib/components/util/mixTab.svelte'
|
||||
import MediaPlayer from '$lib/components/media/mediaPlayer.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import IconButton from '$lib/components/util/iconButton.svelte'
|
||||
|
||||
export let data: LayoutData
|
||||
|
||||
let mixData = [
|
||||
{
|
||||
name: 'J-Core Mix',
|
||||
color: 'red',
|
||||
id: 'SomeId',
|
||||
},
|
||||
{
|
||||
name: 'Best of: 葉月ゆら',
|
||||
color: 'purple',
|
||||
id: 'SomeId',
|
||||
},
|
||||
]
|
||||
|
||||
$: currentPathname = data.url.pathname
|
||||
|
||||
let newMixNameInputOpen = false
|
||||
import Navbar from '$lib/components/util/navbar.svelte'
|
||||
import Sidebar from '$lib/components/util/sidebar.svelte'
|
||||
import { queue } from '$lib/stores'
|
||||
|
||||
// I'm thinking I might want to make /albums, /artists, and /playlists all there own routes and just wrap them in a (library) layout
|
||||
let sidebar: Sidebar
|
||||
|
||||
$: currentlyPlaying = $queue.current
|
||||
$: shuffled = $queue.isShuffled
|
||||
</script>
|
||||
|
||||
<main id="grid-wrapper" class="h-full">
|
||||
<nav id="navbar" class="items-center">
|
||||
<strong class="pl-6 text-3xl">
|
||||
<i class="fa-solid fa-record-vinyl mr-1" />
|
||||
Lazuli
|
||||
</strong>
|
||||
<SearchBar />
|
||||
<div class="flex h-full justify-end p-4">
|
||||
<IconButton halo={true} on:click={() => goto('/user')}>
|
||||
<i slot="icon" class="fa-solid fa-user text-lg" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</nav>
|
||||
<section id="sidebar" class="relative pt-4 text-sm font-normal">
|
||||
<div class="mb-10">
|
||||
<NavTab label="Home" icon="fa-solid fa-wave-square" redirect="/" disabled={currentPathname === '/'} />
|
||||
<NavTab label="Playlists" icon="fa-solid fa-bars-staggered" redirect="/playlists" disabled={/^\/playlists.*$/.test(currentPathname)} />
|
||||
<NavTab label="Library" icon="fa-solid fa-book" redirect="/library" disabled={/^\/library.*$/.test(currentPathname)} />
|
||||
</div>
|
||||
<h1 class="mb-1 flex h-5 items-center justify-between pl-6 text-sm text-neutral-400">
|
||||
Your Mixes
|
||||
<IconButton halo={true} on:click={() => (mixData = [{ name: 'New Mix', color: 'grey', id: 'SomeId' }, ...mixData])}>
|
||||
<i slot="icon" class="fa-solid fa-plus" />
|
||||
</IconButton>
|
||||
</h1>
|
||||
<div>
|
||||
{#each mixData as mix}
|
||||
<MixTab {...mix} />
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
<section id="content-wrapper" class="no-scrollbar overflow-x-clip overflow-y-scroll pr-8">
|
||||
<Navbar on:opensidebar={sidebar.open} />
|
||||
<Sidebar bind:this={sidebar} />
|
||||
<section id="content-wrapper" class="overflow-y-scroll no-scrollbar">
|
||||
<slot />
|
||||
</section>
|
||||
<MediaPlayer />
|
||||
{#if currentlyPlaying}
|
||||
<MediaPlayer
|
||||
mediaItem={currentlyPlaying}
|
||||
{shuffled}
|
||||
mediaSession={'mediaSession' in navigator ? navigator.mediaSession : null}
|
||||
on:stop={() => $queue.clear()}
|
||||
on:next={() => $queue.next()}
|
||||
on:previous={() => $queue.previous()}
|
||||
on:toggleShuffle={() => $queue.toggleShuffle()}
|
||||
/>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
#grid-wrapper,
|
||||
#navbar {
|
||||
display: grid;
|
||||
column-gap: 3rem;
|
||||
grid-template-columns: 12rem auto 12rem;
|
||||
}
|
||||
|
||||
#grid-wrapper {
|
||||
row-gap: 1rem;
|
||||
grid-template-rows: 4.5rem auto;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
grid-area: 1 / 1 / 2 / 4;
|
||||
}
|
||||
#sidebar {
|
||||
grid-area: 2 / 1 / 3 / 2;
|
||||
}
|
||||
#content-wrapper {
|
||||
grid-area: 2 / 2 / 3 / 4;
|
||||
display: grid;
|
||||
grid-template-rows: min-content auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,68 @@
|
||||
<script lang="ts">
|
||||
import ScrollableCardMenu from '$lib/components/media/scrollableCardMenu.svelte'
|
||||
import type { PageData } from './$types'
|
||||
import Loader from '$lib/components/util/loader.svelte'
|
||||
import MediaCard from '$lib/components/media/mediaCard.svelte'
|
||||
import AlbumCard from '$lib/components/media/albumCard.svelte'
|
||||
|
||||
export let data: PageData
|
||||
</script>
|
||||
|
||||
<div id="main">
|
||||
<div id="main" class="grid">
|
||||
{#await data.recommendations}
|
||||
<Loader />
|
||||
{:then recommendations}
|
||||
<ScrollableCardMenu header={'Listen Again'} cardDataList={recommendations} />
|
||||
<div id="card-wrapper" class="grid w-full gap-4 justify-self-center px-[5%] pt-8">
|
||||
{#each recommendations.filter((item) => item.type === 'album') as album}
|
||||
<AlbumCard {album} />
|
||||
{/each}
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@media (min-width: 350px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 550px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 950px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1150px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1350px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1550px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 2200px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 3000px) {
|
||||
#card-wrapper {
|
||||
grid-template-columns: repeat(10, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { itemDisplayState } from '$lib/stores'
|
||||
import type { LayoutData } from './$types.js'
|
||||
import { fade } from 'svelte/transition'
|
||||
import IconButton from '$lib/components/util/iconButton.svelte'
|
||||
import { page } from '$app/stores'
|
||||
|
||||
export let data: LayoutData
|
||||
|
||||
$: currentPathname = data.url.pathname
|
||||
$: currentPathname = $page.url.pathname
|
||||
</script>
|
||||
|
||||
<main class="py-4">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { PageServerData } from './$types'
|
||||
import { itemDisplayState } from '$lib/stores'
|
||||
import Loader from '$lib/components/util/loader.svelte'
|
||||
import AlbumCard from './albumCard.svelte'
|
||||
import AlbumCard from '$lib/components/media/albumCard.svelte'
|
||||
import ListItem from '$lib/components/media/listItem.svelte'
|
||||
|
||||
export let data: PageServerData
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<script lang="ts">
|
||||
import LazyImage from '$lib/components/media/lazyImage.svelte'
|
||||
import IconButton from '$lib/components/util/iconButton.svelte'
|
||||
import ArtistList from '$lib/components/media/artistList.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { queue, newestAlert } from '$lib/stores'
|
||||
import ServiceLogo from '$lib/components/util/serviceLogo.svelte'
|
||||
|
||||
export let album: Album
|
||||
|
||||
async function playAlbum() {
|
||||
const itemsResponse = await fetch(`/api/connections/${album.connection.id}/album/${album.id}/items`, {
|
||||
credentials: 'include',
|
||||
}).catch(() => null)
|
||||
|
||||
if (!itemsResponse || !itemsResponse.ok) {
|
||||
$newestAlert = ['warning', 'Failed to play album']
|
||||
return
|
||||
}
|
||||
|
||||
const data = (await itemsResponse.json()) as { items: Song[] }
|
||||
$queue.setQueue(data.items)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="overflow-hidden p-3">
|
||||
<div id="thumbnail-wrapper" class="relative aspect-square w-full overflow-clip rounded">
|
||||
<button id="thumbnail" class="h-full w-full" on:click={() => goto(`/details/album?id=${album.id}&connection=${album.connection.id}`)}>
|
||||
<LazyImage thumbnailUrl={album.thumbnailUrl} alt={`${album.name} jacket`} objectFit={'cover'} />
|
||||
</button>
|
||||
<div id="play-button" class="absolute left-1/2 top-1/2 h-1/4 -translate-x-1/2 -translate-y-1/2 opacity-0 transition-opacity">
|
||||
<IconButton halo={true} on:click={playAlbum}>
|
||||
<i slot="icon" class="fa-solid fa-play text-2xl" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div id="connection-type-icon" class="absolute left-2 top-2 h-9 w-9 opacity-0 transition-opacity">
|
||||
<ServiceLogo type={album.connection.type} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-2 text-center text-sm">
|
||||
<div class="line-clamp-2">{album.name}</div>
|
||||
<div class="line-clamp-2 text-neutral-400">
|
||||
<ArtistList mediaItem={album} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#thumbnail-wrapper:hover > #thumbnail {
|
||||
filter: brightness(35%);
|
||||
}
|
||||
#thumbnail-wrapper:hover > #play-button {
|
||||
opacity: 1;
|
||||
}
|
||||
/* #connection-type-icon {
|
||||
filter: grayscale();
|
||||
} */
|
||||
#thumbnail-wrapper:hover > #connection-type-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
#thumbnail {
|
||||
transition: filter 150ms ease;
|
||||
}
|
||||
</style>
|
||||
@@ -2,13 +2,8 @@ import type { LayoutServerLoad } from './$types'
|
||||
|
||||
export const ssr = false
|
||||
|
||||
export const load: LayoutServerLoad = ({ url, locals }) => {
|
||||
const { pathname, search } = url
|
||||
export const load: LayoutServerLoad = ({ locals }) => {
|
||||
return {
|
||||
url: {
|
||||
pathname,
|
||||
search,
|
||||
},
|
||||
user: locals.user,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
import '../app.css'
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css'
|
||||
import AlertBox from '$lib/components/util/alertBox.svelte'
|
||||
import { newestAlert, backgroundImage, pageWidth } from '$lib/stores'
|
||||
import { newestAlert, backgroundImage } from '$lib/stores'
|
||||
import { fade } from 'svelte/transition'
|
||||
|
||||
let alertBox: AlertBox
|
||||
$: if ($newestAlert && alertBox) alertBox.addAlert(...$newestAlert)
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={$pageWidth} />
|
||||
<div class="no-scrollbar relative h-screen font-notoSans text-white">
|
||||
<div class="no-scrollbar relative h-screen overflow-x-clip font-notoSans text-white">
|
||||
<div class="fixed isolate -z-10 h-full w-screen bg-black">
|
||||
<div id="background-gradient" class="absolute z-10 h-1/2 w-full bg-cover" />
|
||||
{#key $backgroundImage}
|
||||
|
||||
Reference in New Issue
Block a user