Create Now Page (#25)
Release to Netlify / deploy-to-netlify (push) Successful in 2m10s Details

Reviewed-on: #25
Co-authored-by: chris b <blue.iron1752@chrisb.xyz>
Co-committed-by: chris b <blue.iron1752@chrisb.xyz>
This commit is contained in:
chris b 2023-11-04 18:45:12 -04:00 committed by gitea web
parent 81775661c2
commit 3e505c355c
Signed by: gitea web
GPG Key ID: 5C5A145CBA890DA4
17 changed files with 626 additions and 185 deletions

View File

@ -14,6 +14,7 @@
true,
{
"severity": "warning",
"ignore": ["css-nesting", "text-size-adjust"],
"ignorePartialSupport": true
}
],
@ -34,7 +35,6 @@
}
],
"shorthand-property-no-redundant-values": true,
"unit-case": "lower",
"unit-no-unknown": true,
"length-zero-no-unit": true,
"unit-disallowed-list": [
@ -54,6 +54,7 @@
"camelCaseSvgKeywords": true
}
],
"value-no-vendor-prefix": true
"value-no-vendor-prefix": true,
"media-feature-range-notation": ["prefix"]
}
}

14
package-lock.json generated
View File

@ -21,14 +21,15 @@
"import-sort": "6.0.0",
"import-sort-parser-typescript": "6.0.0",
"import-sort-style-custom": "2.1.2",
"postcss-html": "1.5.0",
"lastfm-njs": "^2.0.1",
"postcss-html": "^1.5.0",
"prettier": "3.0.3",
"prettier-plugin-astro": "0.12.1",
"prettier-plugin-import-sort": "0.0.7",
"prettier-plugin-packagejson": "2.4.6",
"stylelint": "15.11.0",
"stylelint-config-clean-order": "5.2.0",
"stylelint-config-html": "1.1.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-standard": "34.0.0",
"stylelint-no-unsupported-browser-features": "7.0.0",
"typescript": "5.2.2"
@ -5593,6 +5594,15 @@
"language-subtag-registry": "~0.3.2"
}
},
"node_modules/lastfm-njs": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/lastfm-njs/-/lastfm-njs-2.0.1.tgz",
"integrity": "sha512-RJsOGjKI8EDowKsJhvPObUXkPXDQM2EuXhDPXK+G9YKBdVXAHX7IrRKs7sFZWI4bDhF56ZXQC08/AeYvKYYoEA==",
"dev": true,
"engines": {
"node": ">=18"
}
},
"node_modules/ldjson-stream": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ldjson-stream/-/ldjson-stream-1.2.1.tgz",

View File

@ -7,7 +7,7 @@
"build": "astro build",
"dev": "astro dev",
"lint:astro": "astro check",
"lint:css": "stylelint \"src/**/*.css\"",
"lint:css": "stylelint \"src/**/*.{css,astro}\"",
"lint:js": "eslint ./ --ext .js,.ts,.tsx,.astro",
"prettier": "prettier './**/*' --write --list-different --ignore-unknown",
"prettier:check": "prettier './**/*' -c --ignore-unknown",
@ -29,14 +29,15 @@
"import-sort": "6.0.0",
"import-sort-parser-typescript": "6.0.0",
"import-sort-style-custom": "2.1.2",
"postcss-html": "1.5.0",
"lastfm-njs": "^2.0.1",
"postcss-html": "^1.5.0",
"prettier": "3.0.3",
"prettier-plugin-astro": "0.12.1",
"prettier-plugin-import-sort": "0.0.7",
"prettier-plugin-packagejson": "2.4.6",
"stylelint": "15.11.0",
"stylelint-config-clean-order": "5.2.0",
"stylelint-config-html": "1.1.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-standard": "34.0.0",
"stylelint-no-unsupported-browser-features": "7.0.0",
"typescript": "5.2.2"

View File

@ -0,0 +1,99 @@
---
---
<script>
class Album extends HTMLDivElement {
static observedAttributes = ['data-artist', 'data-title', 'data-cover', 'data-now-playing'];
constructor() {
super();
}
#renderElement() {
const artist = this.dataset.artist;
const title = this.dataset.title;
const cover = this.dataset.cover;
const nowPlaying = this.dataset.nowPlaying === 'true';
if (artist && title && cover) {
const imgTag = document.createElement('img');
imgTag.setAttribute('id', 'AlbumArt');
imgTag.classList.add('album-cover');
imgTag.src = cover;
imgTag.title = `${title} by ${artist}`;
const caption = document.createElement('p');
caption.classList.add('album-name');
caption.innerText = `${title} by ${artist}`;
this.appendChild(imgTag);
this.appendChild(caption);
this.classList.remove('broken-album');
this.classList.add('album');
if (nowPlaying) {
imgTag.classList.add('now-playing');
} else {
imgTag.classList.remove('now-playing');
}
} else {
this.classList.add('broken-album');
}
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (newValue === oldValue) {
return;
} else if (name === 'data-title') {
const curveText = this.querySelector('#CurveText');
const text = document.createTextNode(newValue);
if (curveText) {
curveText.innerHTML = '';
curveText.appendChild(text);
}
} else if (name === 'data-cover') {
const albumArt = this.querySelector<HTMLImageElement>('#AlbumArt');
if (albumArt) {
albumArt.src = newValue;
}
}
}
connectedCallback() {
this.#renderElement();
}
}
customElements.define('lastfm-album', Album, { extends: 'div' });
</script>
<style is:global>
@keyframes spin-record {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.album {
position: relative;
& .album-name {
text-align: center;
}
& .album-cover {
width: 20rem;
border-radius: 10rem;
box-shadow: 0 0 0.25rem 0 darkslategray;
&.now-playing {
animation: 30000ms infinite spin-record linear;
}
}
}
</style>

View File

@ -1,61 +0,0 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class='link-card'>
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View File

@ -0,0 +1,41 @@
---
import MostPlayedAlbum from '~/components/MostPlayedAlbum.astro';
import NowPlaying from '~/components/NowPlaying.astro';
---
<section class='music angle-section'>
<div class='section-wrapper'>
<h2 class='section-title'>Listening to</h2>
<div class='records'>
<NowPlaying />
<MostPlayedAlbum />
</div>
</div>
</section>
<style>
.music {
--background: peachpuff;
& .records {
display: flex;
flex-flow: row nowrap;
gap: 5rem 1rem;
justify-content: space-around;
@media screen and (max-width: 640px) {
flex-flow: row wrap;
}
}
}
</style>
<style is:global>
.music {
& .label {
padding: 1rem 0;
font-size: 1.25rem;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,60 @@
---
import Album from '~/components/Album.astro';
---
<div is='most-played-album' class='most-played'>
<p class='label'>Most played album this month</p>
<Album />
</div>
<script>
import type { MostPlayedAlbumResponse, MostPlayedData } from '~/datamodel';
import getLargestImage from '~/utils/getLargestImage';
class MostPlayedAlbum extends HTMLDivElement {
#mostPlayedQuery = `
query MostPlayedAlbums {
lastfm {
mostPlayedAlbums(limit: 1, period: month) {
name
artist
art {
size
url
}
}
}
}`;
constructor() {
super();
this.#renderNowPlaying().catch(console.error);
}
async #renderNowPlaying() {
const mostPlayed = await this.#queryNowPlaying();
const mostPlayedAlbum = mostPlayed?.mostPlayedAlbums?.[0];
const art = getLargestImage(mostPlayedAlbum?.art);
const artistArt = document.createElement('div', { is: 'lastfm-album' });
artistArt.dataset.artist = mostPlayedAlbum?.artist ?? '';
artistArt.dataset.title = mostPlayedAlbum?.name ?? '';
artistArt.dataset.cover = art;
this.appendChild(artistArt);
}
async #queryNowPlaying(): Promise<{ mostPlayedAlbums: MostPlayedData[] } | undefined> {
const response = await fetch('https://api.chriswb.dev', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: this.#mostPlayedQuery }),
});
const json: MostPlayedAlbumResponse = await response.json();
return json?.data?.lastfm;
}
}
customElements.define('most-played-album', MostPlayedAlbum, { extends: 'div' });
</script>

View File

@ -0,0 +1,80 @@
---
import Album from '~/components/Album.astro';
---
<div is='now-playing' class='now-playing'>
<p class='label'></p>
<Album />
</div>
<script>
import type { NowPlayingData, NowPlayingResponse } from '~/datamodel';
import getLargestImage from '~/utils/getLargestImage';
class NowPlaying extends HTMLDivElement {
#nowPlayingQuery = `
query NowPlayingQuery {
lastfm {
nowplaying {
artist
title
nowplaying
art {
url
size
}
}
}
}`;
#nowPlayingElement: HTMLDivElement | null = null;
constructor() {
super();
this.#renderNowPlaying().catch(console.error);
setInterval(() => {
this.#updateNowPlaying().catch(console.error);
}, 1000 * 30);
}
async #updateElement(element: HTMLDivElement | null) {
const nowPlaying = await this.#queryNowPlaying();
if (nowPlaying && element) {
const art = getLargestImage(nowPlaying.art);
element.dataset.artist = nowPlaying?.artist;
element.dataset.title = nowPlaying?.title;
element.dataset.cover = art;
element.dataset.nowPlaying = nowPlaying.nowplaying.toString();
const label = this.querySelector('.label');
label!.textContent = nowPlaying.nowplaying ? 'Now playing' : 'Recently played';
}
}
async #updateNowPlaying() {
await this.#updateElement(this.#nowPlayingElement);
}
async #renderNowPlaying() {
const nowPlayingSection = document.createElement('div', { is: 'lastfm-album' });
await this.#updateElement(nowPlayingSection);
this.#nowPlayingElement = nowPlayingSection;
this.appendChild(nowPlayingSection);
}
async #queryNowPlaying(): Promise<NowPlayingData | undefined> {
const response = await fetch('https://api.chriswb.dev', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: this.#nowPlayingQuery }),
});
const json: NowPlayingResponse = await response.json();
return json?.data?.lastfm?.nowplaying;
}
}
customElements.define('now-playing', NowPlaying, { extends: 'div' });
</script>

View File

@ -0,0 +1,80 @@
---
import RecentPhoto from './RecentPhoto.astro';
---
<section class='photoblog angle-section'>
<div class='section-wrapper'>
<h2 class='section-title'>Photoblogging</h2>
<ol class='photo-list' is='photo-blog'></ol>
<RecentPhoto />
</div>
</section>
<script>
import type { BlogPhoto, PhotoResponse } from '~/datamodel';
class PhotoAlbum extends HTMLOListElement {
#recentPhotosQuery = `
query recentPhotosQuery {
photoBlog {
photos(limit: 6) {
photo
title
id
url
}
}
}`;
constructor() {
super();
this.#renderPhotos().catch(console.error);
}
async #renderPhotos() {
const recentPhotos = await this.#queryPhotos();
if (!recentPhotos) {
this.textContent = '';
this.style.display = 'none';
} else {
recentPhotos.forEach((photo) => {
const photoTag = document.createElement('li', { is: 'recent-photo' });
photoTag.dataset['src'] = photo.photo ?? '';
photoTag.dataset['title'] = photo.title ?? '';
photoTag.dataset['link'] = photo.url ?? '';
this.appendChild(photoTag);
});
}
}
async #queryPhotos(): Promise<BlogPhoto[] | undefined> {
const response = await fetch('https://api.chriswb.dev', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: this.#recentPhotosQuery }),
});
const json: PhotoResponse = await response.json();
return json?.data?.photoBlog?.photos;
}
}
customElements.define('photo-blog', PhotoAlbum, { extends: 'ol' });
</script>
<style>
.photoblog {
--background: lavender;
& .photo-list {
display: flex;
flex-flow: row wrap;
gap: 4rem 2rem;
align-items: center;
justify-content: space-around;
list-style: none;
}
}
</style>

View File

@ -0,0 +1,87 @@
---
---
<script>
class RecentPhoto extends HTMLLIElement {
constructor() {
super();
}
connectedCallback() {
const src = this.dataset.src;
const title = this.dataset.title;
const link = this.dataset.link;
if (src && title && link) {
const anchorTag = document.createElement('a');
anchorTag.href = link;
const imgTag = document.createElement('img');
imgTag.classList.add('photo');
imgTag.src = src;
imgTag.title = title;
const caption = document.createElement('p');
caption.classList.add('caption');
caption.innerText = title;
anchorTag.appendChild(imgTag);
anchorTag.appendChild(caption);
anchorTag.classList.remove('broken-photo');
anchorTag.classList.add('recent-photo');
this.classList.add('photo-item');
this.appendChild(anchorTag);
} else {
this.classList.add('broken-photo');
}
}
}
customElements.define('recent-photo', RecentPhoto, { extends: 'li' });
</script>
<style is:global>
.photo-item {
transform: rotate(2deg);
&:nth-of-type(odd) {
transform: rotate(4deg);
}
&:nth-of-type(3n + 1),
&:nth-of-type(5n + 1) {
transform: rotate(-4deg);
}
&:nth-of-type(1) {
transform: rotate(-2deg);
}
& .recent-photo {
display: block;
width: 15vw;
min-width: 20rem;
max-width: 25rem;
padding: 1.25rem;
color: currentColor;
background-color: ghostwhite;
border-radius: 0.5rem;
box-shadow: 0 0 1.25rem 0 darkgrey;
&.broken-photo {
display: none;
visibility: hidden;
}
& .caption {
padding-top: 0.5rem;
font-weight: 700;
color: currentColor;
text-align: center;
}
}
}
</style>

51
src/datamodel.ts Normal file
View File

@ -0,0 +1,51 @@
import type { LastFmImageSize } from 'lastfm-njs';
export type Art = { size: keyof typeof LastFmImageSize; url?: string };
export type MostPlayedData = {
artist: string | null;
name: string | null;
art: Art[];
mbid: string;
};
export type NowPlayingData = {
artist: string;
title: string;
nowplaying: boolean;
art: Art[];
mbid: string;
};
export type MostPlayedArtistResponse = {
data: {
lastfm: {
mostPlayedArtists: MostPlayedData[];
};
};
};
export type MostPlayedAlbumResponse = {
data: {
lastfm: {
mostPlayedAlbums: MostPlayedData[];
};
};
};
export type NowPlayingResponse = {
data: {
lastfm: { nowplaying: NowPlayingData } | null;
} | null;
};
export type BlogPhoto = {
photo: string;
title: string;
id: string;
url: string;
};
export type PhotoResponse = {
data: {
photoBlog: { photos: BlogPhoto[] } | null;
} | null;
};

7
src/env.d.ts vendored
View File

@ -1 +1,8 @@
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly PUBLIC_LASTFM_KEY: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@ -21,34 +21,39 @@ const { title } = Astro.props;
<slot />
</body>
</html>
<style is:global>
html {
font-family: system-ui, sans-serif;
}
.angle-section {
--clip-top: 0 0, 100% 6vmin;
--clip-bottom: 100% calc(100% - 3.75vmin), 0 100%;
--clip-top: 0 0, 100% 4vmin;
--clip-bottom: 100% calc(100% - 2.75vmin), 0 100%;
position: relative;
display: flex;
flex-flow: column nowrap;
align-items: center;
margin-top: -6vmin;
padding: 6rem 0;
margin-top: -4vmin;
padding: 4rem 0 6rem;
background-color: var(--background);
clip-path: polygon(var(--clip-top), var(--clip-bottom));
&:nth-of-type(odd) {
--clip-top: 0 6vmin, 100% 0;
--clip-top: 0 4vmin, 100% 0;
}
&:nth-of-type(3n + 1),
&:nth-of-type(5n + 1) {
--clip-bottom: 100% 100%, 0 calc(100% - 3.75vmin);
--clip-bottom: 100% 100%, 0 calc(100% - 2.75vmin);
}
&:first-of-type {
margin-top: 0;
}
& .section-wrapper {
width: 100%;
max-width: 80rem;
padding: 0 1.5rem;
}
}
</style>

View File

@ -1,3 +1,7 @@
:root {
--text-color: #2b2a3b;
}
/* Box sizing rules */
*,
*::before,
@ -8,6 +12,7 @@
/* Prevent font size inflation */
html {
font-family: system-ui, sans-serif;
color: var(--text-color);
text-size-adjust: none;
}

View File

@ -1,123 +1,22 @@
---
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
---
<Layout title='Welcome to Astro.'>
<main>
<svg
class='astro-a'
width='495'
height='623'
viewBox='0 0 495 623'
fill='none'
xmlns='http://www.w3.org/2000/svg'
aria-hidden='true'
>
<path
fill-rule='evenodd'
clip-rule='evenodd'
d='M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z'
fill='url(#paint0_linear_1805_24383)'></path>
<defs>
<linearGradient
id='paint0_linear_1805_24383'
x1='247.358'
y1='0'
x2='247.358'
y2='622.479'
gradientUnits='userSpaceOnUse'
>
<stop stop-opacity='0.9'></stop>
<stop offset='1' stop-opacity='0.2'></stop>
</linearGradient>
</defs>
</svg>
<h1>Welcome to <span class='text-gradient'>Astro</span></h1>
<p class='instructions'>
To get started, open the directory <code>src/pages</code> in your project.<br />
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
</p>
<ul role='list' class='link-card-grid'>
<Card
href='https://docs.astro.build/'
title='Documentation'
body='Learn how Astro works and explore the official API docs.'
/>
<Card
href='https://astro.build/integrations/'
title='Integrations'
body='Supercharge your project with new frameworks and libraries.'
/>
<Card
href='https://astro.build/themes/'
title='Themes'
body='Explore a galaxy of community-built starter themes.'
/>
<Card
href='https://astro.build/chat/'
title='Community'
body='Come say hi to our amazing Discord community. ❤️'
/>
</ul>
<h1>chris b</h1>
<p>this sure is a personal website yep</p>
<p>check out <a href='/now'>/now</a></p>
</main>
</Layout>
<style>
main {
margin: auto;
padding: 1rem;
width: 800px;
max-width: calc(100% - 2rem);
color: white;
font-size: 20px;
line-height: 1.6;
}
.astro-a {
position: absolute;
top: -32px;
left: 50%;
transform: translatex(-50%);
width: 220px;
height: auto;
z-index: -1;
}
h1 {
margin-bottom: 1em;
font-size: 4rem;
font-weight: 700;
line-height: 1;
text-align: center;
margin-bottom: 1em;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.instructions {
margin-bottom: 2rem;
border: 1px solid rgba(var(--accent-light), 25%);
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
padding: 1.5rem;
border-radius: 8px;
}
.instructions code {
font-size: 0.8em;
font-weight: bold;
background: rgba(var(--accent-light), 12%);
color: rgb(var(--accent-light));
border-radius: 4px;
padding: 0.3em 0.4em;
}
.instructions strong {
color: rgb(var(--accent-light));
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
</style>

62
src/pages/now.astro Normal file
View File

@ -0,0 +1,62 @@
---
import Layout from '~/layouts/Layout.astro';
import LastFM from '~/components/LastFM.astro';
import Photoblog from '~/components/Photoblog.astro';
---
<Layout title="Now | What I'm up to right now">
<header class='header angle-section'>
<h1>Now</h1>
<p>things I'm doing right now. or at least recently</p>
</header>
<section class='angle-section free-text'>
<div class='section-wrapper'>
<h2 class='section-title'>The Latest</h2>
<p class='free-text-paragraph'>
At work, I'm working at Roofstock on Stessa frontends and leading the design system
maitenance from the engineering side. Out of work, I've been trying to do more photography
(and working on this site!)
</p>
</div>
</section>
<LastFM />
<Photoblog />
</Layout>
<style>
html {
background-color: papayawhip;
}
.free-text {
--background: mistyrose;
& .section-wrapper {
display: flex;
flex-flow: column nowrap;
align-items: center;
& .free-text-paragraph {
max-width: 36rem;
}
}
}
.header {
--background: papayawhip;
padding: 1rem 0;
clip-path: unset;
}
</style>
<style is:global>
.section-wrapper {
& .section-title {
padding: 0 0 1.75rem;
font-size: 1.5rem;
font-variant: small-caps;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,14 @@
import type { Art } from '~/datamodel';
const getLargestImage = (images: Art[] | undefined): string | undefined => {
if (!images) return undefined;
let art = images.find((art) => art.size === 'MEGA');
if (!art) art = images.find((art) => art.size === 'EXTRALARGE');
if (!art) art = images.find((art) => art.size === 'LARGE');
if (!art) art = images.find((art) => art.size === 'MEDIUM');
if (!art) art = images.find((art) => art.size === 'SMALL');
return art?.url;
};
export default getLargestImage;