diff options
| author | Fuwn <[email protected]> | 2024-10-09 00:41:20 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-10-09 00:41:43 -0700 |
| commit | 998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch) | |
| tree | 50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/User | |
| parent | feat(graphql): add badgeCount field (diff) | |
| download | due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip | |
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/lib/User')
| -rw-r--r-- | src/lib/User/BadgeWall/AWC.svelte | 166 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/BadgePreview.svelte | 438 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/Badges.svelte | 166 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/FallbackBadge.svelte | 216 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/badge.ts | 2 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/badges.css | 8 |
6 files changed, 498 insertions, 498 deletions
diff --git a/src/lib/User/BadgeWall/AWC.svelte b/src/lib/User/BadgeWall/AWC.svelte index ba8a22ea..1cf82a1b 100644 --- a/src/lib/User/BadgeWall/AWC.svelte +++ b/src/lib/User/BadgeWall/AWC.svelte @@ -1,100 +1,100 @@ <script lang="ts"> - import type { AWCBadgesGroup } from '$lib/Data/awc'; - import { cdn, thumbnail } from '$lib/Utility/image'; - import type { Preferences } from '../../../graphql/$types'; - import FallbackBadge from './FallbackBadge.svelte'; - import './badges.css'; + import type { AWCBadgesGroup } from '$lib/Data/awc'; + import { cdn, thumbnail } from '$lib/Utility/image'; + import type { Preferences } from '../../../graphql/$types'; + import FallbackBadge from './FallbackBadge.svelte'; + import './badges.css'; - export let awcPromise: Promise<Response>; - export let categoryFilter: string | null; - export let isOwner: boolean; - export let preferences: Preferences; + export let awcPromise: Promise<Response>; + export let categoryFilter: string | null; + export let isOwner: boolean; + export let preferences: Preferences; - const awcBadgesGrouped = (awcResponse: string): AWCBadgesGroup[] => { - return Array.from( - new DOMParser().parseFromString(awcResponse, 'text/html').querySelectorAll('.container') - ) - .map((c) => { - const container = c as HTMLDivElement; - const header = container.querySelector('.container-header') as HTMLDivElement; + const awcBadgesGrouped = (awcResponse: string): AWCBadgesGroup[] => { + return Array.from( + new DOMParser().parseFromString(awcResponse, 'text/html').querySelectorAll('.container') + ) + .map((c) => { + const container = c as HTMLDivElement; + const header = container.querySelector('.container-header') as HTMLDivElement; - if (!header) return; + if (!header) return; - if (!['Anime', 'Manga', 'Special'].includes(header.innerText)) return; + if (!['Anime', 'Manga', 'Special'].includes(header.innerText)) return; - if (header.innerText === 'Special') { - return { - group: header.innerText, - badges: Array.from(container.querySelectorAll('.badge-display img')).map((b) => { - const badge = b as HTMLImageElement; + if (header.innerText === 'Special') { + return { + group: header.innerText, + badges: Array.from(container.querySelectorAll('.badge-display img')).map((b) => { + const badge = b as HTMLImageElement; - return { - link: '#', - description: badge.alt, - image: badge.src.includes('placeholder') - ? cdn('https://awc.moe/static/images/badge-placeholder.png') - : badge.src - }; - }) - }; - } + return { + link: '#', + description: badge.alt, + image: badge.src.includes('placeholder') + ? cdn('https://awc.moe/static/images/badge-placeholder.png') + : badge.src + }; + }) + }; + } - return { - group: header.innerText, - badges: Array.from(container.querySelectorAll('.badge-display a')).map((b) => { - const badge = b as HTMLAnchorElement; - const image = badge.querySelector('img') as HTMLImageElement; + return { + group: header.innerText, + badges: Array.from(container.querySelectorAll('.badge-display a')).map((b) => { + const badge = b as HTMLAnchorElement; + const image = badge.querySelector('img') as HTMLImageElement; - return { - link: badge.href, - description: image.alt, - image: image.src.includes('placeholder') - ? cdn('https://awc.moe/static/images/badge-placeholder.png') - : image.src - }; - }) - }; - }) - .filter((b) => b !== undefined) as AWCBadgesGroup[]; - }; + return { + link: badge.href, + description: image.alt, + image: image.src.includes('placeholder') + ? cdn('https://awc.moe/static/images/badge-placeholder.png') + : image.src + }; + }) + }; + }) + .filter((b) => b !== undefined) as AWCBadgesGroup[]; + }; </script> {#await awcPromise then badges} - {#if badges} - {#await badges.clone().text() then text} - {@const parsedBadges = awcBadgesGrouped(text)} + {#if badges} + {#await badges.clone().text() then text} + {@const parsedBadges = awcBadgesGrouped(text)} - {#if parsedBadges.length > 0} - {#each parsedBadges as group} - <details open={categoryFilter || isOwner ? false : true}> - <summary> - Anime Watching Club <span class="opaque">|</span> - {group.group} - </summary> + {#if parsedBadges.length > 0} + {#each parsedBadges as group} + <details open={categoryFilter || isOwner ? false : true}> + <summary> + Anime Watching Club <span class="opaque">|</span> + {group.group} + </summary> - <p /> + <p /> - <div class="badges"> - {#each group.badges as badge, index} - <div id={`badge-${index}`}> - <FallbackBadge - {badge} - source={cdn(thumbnail(badge.image))} - alternative={badge.description} - fallback={thumbnail(badge.image)} - hideOnError={preferences.hide_missing_badges} - awc - {index} - {preferences} - /> - </div> - {/each} - </div> - </details> + <div class="badges"> + {#each group.badges as badge, index} + <div id={`badge-${index}`}> + <FallbackBadge + {badge} + source={cdn(thumbnail(badge.image))} + alternative={badge.description} + fallback={thumbnail(badge.image)} + hideOnError={preferences.hide_missing_badges} + awc + {index} + {preferences} + /> + </div> + {/each} + </div> + </details> - <p /> - {/each} - {/if} - {/await} - {/if} + <p /> + {/each} + {/if} + {/await} + {/if} {/await} diff --git a/src/lib/User/BadgeWall/BadgePreview.svelte b/src/lib/User/BadgeWall/BadgePreview.svelte index 107ed483..7a54cbc4 100644 --- a/src/lib/User/BadgeWall/BadgePreview.svelte +++ b/src/lib/User/BadgeWall/BadgePreview.svelte @@ -1,145 +1,145 @@ <script lang="ts"> - import { thumbnail } from '$lib/Utility/image'; - import type { Badge } from '$lib/Database/SB/User/badges'; - import { cdn } from '$lib/Utility/image'; - import { databaseTimeToDate } from '$lib/Utility/time'; - import locale from '$stores/locale'; - import { onMount } from 'svelte'; - import root from '$lib/Utility/root'; - import ParallaxImage from '$lib/Image/ParallaxImage.svelte'; - - export let selectedBadge: Badge | undefined; - export let onNext: () => void = () => {}; - export let onPrevious: () => void = () => {}; - export let hasNext: boolean; - export let hasPrevious: boolean; - - let source = cdn(thumbnail(selectedBadge?.image || '')) || ''; - let badgeReference: HTMLImageElement; - - $: { - if (selectedBadge && selectedBadge.image) { - const image = new Image(); - - image.src = cdn(selectedBadge.image) || ''; - image.onload = () => { - source = image.src; - }; - } - } - - $: { - if (selectedBadge) - fetch(root(`/api/badges?incrementClickCount=${selectedBadge.id}`), { - method: 'PUT' - }); - } - - onMount(() => { - badgeReference = document.querySelector('.badge-container-image') as HTMLImageElement; - - const handleClickOutside = (event: any) => { - if (event.target.classList.contains('popup')) selectedBadge = undefined; - }; - - document.addEventListener('click', handleClickOutside); - - return () => { - document.removeEventListener('click', handleClickOutside); - }; - }); - - const classifySource = (source: string) => { - let name = source; - const sourceLower = source.toLowerCase(); - - if (sourceLower.includes('pixiv.net')) { - name = 'Pixiv'; - } else if (sourceLower.includes('twitter.com') || sourceLower.includes('x.com')) { - name = 'X (Twitter)'; - } else if (sourceLower.includes('zerochan.net')) { - name = 'Zerochan'; - } else if (sourceLower.includes('imgur.com')) { - name = 'Imgur'; - } else if (sourceLower.includes('lofter.com')) { - name = 'Lofter'; - } - - return `<a href="${source}" target="_blank">${name}</a>`; - }; - - const classifyDesigner = (designer: string) => { - let name = designer; - let userLink = designer; - const designerLower = designer.toLowerCase(); - const anilistUser = designer.match(/https?:\/\/anilist\.co\/user\/([^/]+)\/?/); - - if (anilistUser) { - name = anilistUser[1]; - } else if (designerLower.startsWith('@')) { - name = designer.replace('@', ''); - userLink = `https://anilist.co/user/${name}/`; - } else if (!designerLower.startsWith('http')) { - userLink = `https://anilist.co/user/${name}/`; - } - - return `<a href="${userLink}" target="_blank">@${name}</a>`; - }; - - const onClick = (event: MouseEvent) => { - event.preventDefault(); - - if ( - event.clientX < - badgeReference.getBoundingClientRect().left + badgeReference.getBoundingClientRect().width / 2 - ) { - onPrevious(); - } else { - onNext(); - } - }; + import { thumbnail } from '$lib/Utility/image'; + import type { Badge } from '$lib/Database/SB/User/badges'; + import { cdn } from '$lib/Utility/image'; + import { databaseTimeToDate } from '$lib/Utility/time'; + import locale from '$stores/locale'; + import { onMount } from 'svelte'; + import root from '$lib/Utility/root'; + import ParallaxImage from '$lib/Image/ParallaxImage.svelte'; + + export let selectedBadge: Badge | undefined; + export let onNext: () => void = () => {}; + export let onPrevious: () => void = () => {}; + export let hasNext: boolean; + export let hasPrevious: boolean; + + let source = cdn(thumbnail(selectedBadge?.image || '')) || ''; + let badgeReference: HTMLImageElement; + + $: { + if (selectedBadge && selectedBadge.image) { + const image = new Image(); + + image.src = cdn(selectedBadge.image) || ''; + image.onload = () => { + source = image.src; + }; + } + } + + $: { + if (selectedBadge) + fetch(root(`/api/badges?incrementClickCount=${selectedBadge.id}`), { + method: 'PUT' + }); + } + + onMount(() => { + badgeReference = document.querySelector('.badge-container-image') as HTMLImageElement; + + const handleClickOutside = (event: any) => { + if (event.target.classList.contains('popup')) selectedBadge = undefined; + }; + + document.addEventListener('click', handleClickOutside); + + return () => { + document.removeEventListener('click', handleClickOutside); + }; + }); + + const classifySource = (source: string) => { + let name = source; + const sourceLower = source.toLowerCase(); + + if (sourceLower.includes('pixiv.net')) { + name = 'Pixiv'; + } else if (sourceLower.includes('twitter.com') || sourceLower.includes('x.com')) { + name = 'X (Twitter)'; + } else if (sourceLower.includes('zerochan.net')) { + name = 'Zerochan'; + } else if (sourceLower.includes('imgur.com')) { + name = 'Imgur'; + } else if (sourceLower.includes('lofter.com')) { + name = 'Lofter'; + } + + return `<a href="${source}" target="_blank">${name}</a>`; + }; + + const classifyDesigner = (designer: string) => { + let name = designer; + let userLink = designer; + const designerLower = designer.toLowerCase(); + const anilistUser = designer.match(/https?:\/\/anilist\.co\/user\/([^/]+)\/?/); + + if (anilistUser) { + name = anilistUser[1]; + } else if (designerLower.startsWith('@')) { + name = designer.replace('@', ''); + userLink = `https://anilist.co/user/${name}/`; + } else if (!designerLower.startsWith('http')) { + userLink = `https://anilist.co/user/${name}/`; + } + + return `<a href="${userLink}" target="_blank">@${name}</a>`; + }; + + const onClick = (event: MouseEvent) => { + event.preventDefault(); + + if ( + event.clientX < + badgeReference.getBoundingClientRect().left + badgeReference.getBoundingClientRect().width / 2 + ) { + onPrevious(); + } else { + onNext(); + } + }; </script> {#if selectedBadge} - <div class="badge-preview"> - <div class="badge-preview-badge"> - {#if selectedBadge.image} - <div role="img" class="badge-container"> - <a href={'#'} on:click={onClick} class="badge-container-image"> - <ParallaxImage - {source} - alternativeText="selectedBadge.description" - limit={100} - duration={1500} - /> - </a> - </div> - - <p /> - {/if} - </div> - - <div class="badge-preview-information"> - {#if selectedBadge.time} - {$locale().dateFormatter(databaseTimeToDate(selectedBadge.time))} - - {#if (selectedBadge.designer || selectedBadge.source || selectedBadge.post) && !selectedBadge.description} - <p /> - {:else if selectedBadge.description} - <br /> - {/if} - {/if} - - {#if selectedBadge.description} - {selectedBadge.description} - - <p /> - {/if} - - {#if selectedBadge.designer} - <b>Designer:</b> - - <!-- {#if selectedBadge.designer.startsWith('http')} + <div class="badge-preview"> + <div class="badge-preview-badge"> + {#if selectedBadge.image} + <div role="img" class="badge-container"> + <a href={'#'} on:click={onClick} class="badge-container-image"> + <ParallaxImage + {source} + alternativeText="selectedBadge.description" + limit={100} + duration={1500} + /> + </a> + </div> + + <p /> + {/if} + </div> + + <div class="badge-preview-information"> + {#if selectedBadge.time} + {$locale().dateFormatter(databaseTimeToDate(selectedBadge.time))} + + {#if (selectedBadge.designer || selectedBadge.source || selectedBadge.post) && !selectedBadge.description} + <p /> + {:else if selectedBadge.description} + <br /> + {/if} + {/if} + + {#if selectedBadge.description} + {selectedBadge.description} + + <p /> + {/if} + + {#if selectedBadge.designer} + <b>Designer:</b> + + <!-- {#if selectedBadge.designer.startsWith('http')} <a href={selectedBadge.designer} target="_blank"> {selectedBadge.designer} </a> @@ -150,96 +150,96 @@ {:else} {selectedBadge.designer} {/if} --> - {@html classifyDesigner(selectedBadge.designer)} + {@html classifyDesigner(selectedBadge.designer)} - <br /> - {/if} + <br /> + {/if} - {#if selectedBadge.post && selectedBadge.post !== '#'} - <b>{selectedBadge.post.includes('forum') ? 'Forum' : 'Activity'}:</b> + {#if selectedBadge.post && selectedBadge.post !== '#'} + <b>{selectedBadge.post.includes('forum') ? 'Forum' : 'Activity'}:</b> - <a href={selectedBadge.post} target="_blank"> - {selectedBadge.post} - </a> + <a href={selectedBadge.post} target="_blank"> + {selectedBadge.post} + </a> - <br /> - {/if} + <br /> + {/if} - {#if selectedBadge.source} - <b>Source:</b> + {#if selectedBadge.source} + <b>Source:</b> - {#if selectedBadge.source.startsWith('http')} - <!-- <a href={selectedBadge.source} target="_blank"> + {#if selectedBadge.source.startsWith('http')} + <!-- <a href={selectedBadge.source} target="_blank"> {selectedBadge.source} </a> --> - {@html classifySource(selectedBadge.source)} - {:else} - {selectedBadge.source} - {/if} - - <br /> - {/if} - - {#if selectedBadge.category} - <b>Category:</b> - - <a - href={`?category=${selectedBadge.category}`} - on:click={() => (selectedBadge = undefined)} - > - {selectedBadge.category} - </a> - - <br /> - {/if} - - <b>SauceNAO:</b> - <a href={`https://saucenao.com/search.php?url=${selectedBadge.image}`} target="_blank"> - Search - </a> - - <div class="badge-preview-seek"> - {#if hasPrevious} - <button on:click={onPrevious}>Previous</button> - {/if} - - {#if hasNext} - <button on:click={onNext} style="float: right;">Next</button> - {/if} - </div> - </div> - </div> + {@html classifySource(selectedBadge.source)} + {:else} + {selectedBadge.source} + {/if} + + <br /> + {/if} + + {#if selectedBadge.category} + <b>Category:</b> + + <a + href={`?category=${selectedBadge.category}`} + on:click={() => (selectedBadge = undefined)} + > + {selectedBadge.category} + </a> + + <br /> + {/if} + + <b>SauceNAO:</b> + <a href={`https://saucenao.com/search.php?url=${selectedBadge.image}`} target="_blank"> + Search + </a> + + <div class="badge-preview-seek"> + {#if hasPrevious} + <button on:click={onPrevious}>Previous</button> + {/if} + + {#if hasNext} + <button on:click={onNext} style="float: right;">Next</button> + {/if} + </div> + </div> + </div> {/if} <style> - :global(.badge-preview img) { - border-radius: 8px; - max-height: 50vh; - height: auto; - width: 100%; - } - - .badge-container { - display: flex; - justify-content: center; - align-items: center; - } - - .badge-preview { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - gap: 1rem; - } - - @media (max-width: 768px) { - .badge-preview { - flex-direction: column; - } - } - - .badge-preview-seek { - padding-top: 2rem; - } + :global(.badge-preview img) { + border-radius: 8px; + max-height: 50vh; + height: auto; + width: 100%; + } + + .badge-container { + display: flex; + justify-content: center; + align-items: center; + } + + .badge-preview { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 1rem; + } + + @media (max-width: 768px) { + .badge-preview { + flex-direction: column; + } + } + + .badge-preview-seek { + padding-top: 2rem; + } </style> diff --git a/src/lib/User/BadgeWall/Badges.svelte b/src/lib/User/BadgeWall/Badges.svelte index c4da3118..b233d0c3 100644 --- a/src/lib/User/BadgeWall/Badges.svelte +++ b/src/lib/User/BadgeWall/Badges.svelte @@ -1,98 +1,98 @@ <script lang="ts"> - import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte'; - import tooltip from '$lib/Tooltip/tooltip'; - import locale from '$stores/locale'; - import { databaseTimeToDate } from '$lib/Utility/time'; - import FallbackImage from '$lib/Image/FallbackImage.svelte'; - import { cdn, thumbnail } from '$lib/Utility/image'; - import FallbackBadge from './FallbackBadge.svelte'; - import type { Preferences } from '../../../graphql/$types'; - import type { IndexedBadge } from './badge'; + import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte'; + import tooltip from '$lib/Tooltip/tooltip'; + import locale from '$stores/locale'; + import { databaseTimeToDate } from '$lib/Utility/time'; + import FallbackImage from '$lib/Image/FallbackImage.svelte'; + import { cdn, thumbnail } from '$lib/Utility/image'; + import FallbackBadge from './FallbackBadge.svelte'; + import type { Preferences } from '../../../graphql/$types'; + import type { IndexedBadge } from './badge'; - export let ungroupedBadges: IndexedBadge[]; - export let groupedBadges: [string, IndexedBadge[]][]; - export let categoryFilter: string | null; - export let editMode: boolean; - export let preferences: Preferences | undefined; - export let selectedBadge: IndexedBadge | undefined = undefined; + export let ungroupedBadges: IndexedBadge[]; + export let groupedBadges: [string, IndexedBadge[]][]; + export let categoryFilter: string | null; + export let editMode: boolean; + export let preferences: Preferences | undefined; + export let selectedBadge: IndexedBadge | undefined = undefined; </script> {#if ungroupedBadges.length === 0} - <div class="card"> - No due.moe registered badges found for this user. <a - href={'#'} - on:click={(e) => e.preventDefault()} - title="This alert does not include AWC badges." - use:tooltip>?</a - > - </div> + <div class="card"> + No due.moe registered badges found for this user. <a + href={'#'} + on:click={(e) => e.preventDefault()} + title="This alert does not include AWC badges." + use:tooltip>?</a + > + </div> {/if} {#each groupedBadges as [category, unsortedBadges]} - {@const badges = unsortedBadges.sort( - (a, b) => new Date(b.time || 0).getTime() - new Date(a.time || 0).getTime() - )} + {@const badges = unsortedBadges.sort( + (a, b) => new Date(b.time || 0).getTime() - new Date(a.time || 0).getTime() + )} - <details open={categoryFilter ? categoryFilter === category : true}> - <summary>{category}</summary> + <details open={categoryFilter ? categoryFilter === category : true}> + <summary>{category}</summary> - <p /> + <p /> - <div class="badges"> - {#each badges as badge} - <div id={`badge-${badge.id}`}> - {#if editMode} - <LinkedTooltip - content={`${ - badge.time ? $locale().dateFormatter(databaseTimeToDate(badge.time)) : '' - }${badge.description ? `\n${badge.description}` : ''}${ - badge.designer ? `\nDesigner: ${badge.designer}` : '' - }${badge.source ? `\nSource: ${badge.source}` : ''}`} - pin={`badge-${badge.id}`} - pinPosition="top" - relative - > - <a - href={`#`} - on:click={() => { - selectedBadge = badge; + <div class="badges"> + {#each badges as badge} + <div id={`badge-${badge.id}`}> + {#if editMode} + <LinkedTooltip + content={`${ + badge.time ? $locale().dateFormatter(databaseTimeToDate(badge.time)) : '' + }${badge.description ? `\n${badge.description}` : ''}${ + badge.designer ? `\nDesigner: ${badge.designer}` : '' + }${badge.source ? `\nSource: ${badge.source}` : ''}`} + pin={`badge-${badge.id}`} + pinPosition="top" + relative + > + <a + href={`#`} + on:click={() => { + selectedBadge = badge; - const hiddenInput = document.querySelector('input[name="hidden"]'); + const hiddenInput = document.querySelector('input[name="hidden"]'); - if (hiddenInput instanceof HTMLInputElement) - hiddenInput.value = badge.hidden ? 'Hidden' : 'Shown'; - }} - > - <FallbackImage - source={cdn(thumbnail(badge.image))} - alternative={badge.description} - fallback={thumbnail(badge.image)} - style={badge.hidden || badge.shadow_hidden - ? 'filter: grayscale(100%); opacity: 50%;' - : ''} - /> - </a> - </LinkedTooltip> - {:else} - <FallbackBadge - {badge} - bind:selectedBadge - source={cdn(thumbnail(badge.image))} - alternative={badge.description} - fallback={thumbnail(badge.image)} - hideOnError={preferences ? preferences.hide_missing_badges : true} - style={badge.hidden || badge.shadow_hidden - ? 'filter: grayscale(100%); opacity: 50%;' - : ''} - {preferences} - /> - {/if} - </div> - {/each} - </div> - </details> + if (hiddenInput instanceof HTMLInputElement) + hiddenInput.value = badge.hidden ? 'Hidden' : 'Shown'; + }} + > + <FallbackImage + source={cdn(thumbnail(badge.image))} + alternative={badge.description} + fallback={thumbnail(badge.image)} + style={badge.hidden || badge.shadow_hidden + ? 'filter: grayscale(100%); opacity: 50%;' + : ''} + /> + </a> + </LinkedTooltip> + {:else} + <FallbackBadge + {badge} + bind:selectedBadge + source={cdn(thumbnail(badge.image))} + alternative={badge.description} + fallback={thumbnail(badge.image)} + hideOnError={preferences ? preferences.hide_missing_badges : true} + style={badge.hidden || badge.shadow_hidden + ? 'filter: grayscale(100%); opacity: 50%;' + : ''} + {preferences} + /> + {/if} + </div> + {/each} + </div> + </details> - {#if groupedBadges[groupedBadges.length - 1][0] !== category} - <p /> - {/if} + {#if groupedBadges[groupedBadges.length - 1][0] !== category} + <p /> + {/if} {/each} diff --git a/src/lib/User/BadgeWall/FallbackBadge.svelte b/src/lib/User/BadgeWall/FallbackBadge.svelte index 0e690443..35a50a7d 100644 --- a/src/lib/User/BadgeWall/FallbackBadge.svelte +++ b/src/lib/User/BadgeWall/FallbackBadge.svelte @@ -1,130 +1,130 @@ <script lang="ts"> - import { classifyDesignerName } from './badge'; - import locale from '$stores/locale'; - import { tweened } from 'svelte/motion'; - import type { Badge } from '../../Database/SB/User/badges'; - import Tooltip from '../../Tooltip/LinkedTooltip.svelte'; - import { databaseTimeToDate } from '../../Utility/time'; - import { cubicOut } from 'svelte/easing'; - import { dev } from '$app/environment'; - import type { Preferences } from '../../../graphql/$types'; + import { classifyDesignerName } from './badge'; + import locale from '$stores/locale'; + import { tweened } from 'svelte/motion'; + import type { Badge } from '../../Database/SB/User/badges'; + import Tooltip from '../../Tooltip/LinkedTooltip.svelte'; + import { databaseTimeToDate } from '../../Utility/time'; + import { cubicOut } from 'svelte/easing'; + import { dev } from '$app/environment'; + import type { Preferences } from '../../../graphql/$types'; - export let source: string | null | undefined; - export let alternative: string | null | undefined; - export let fallback: string | null | undefined; - export let maxReplaceCount = 1; - export let replaceDelay = 1000; - export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg'; - export let hideOnError = false; - export let badge: Badge; - export let style = ''; - export let selectedBadge: Badge | null = null; - export let awc = false; - export let index: number | null = null; - export let preferences: Preferences | undefined; + export let source: string | null | undefined; + export let alternative: string | null | undefined; + export let fallback: string | null | undefined; + export let maxReplaceCount = 1; + export let replaceDelay = 1000; + export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg'; + export let hideOnError = false; + export let badge: Badge; + export let style = ''; + export let selectedBadge: Badge | null = null; + export let awc = false; + export let index: number | null = null; + export let preferences: Preferences | undefined; - let replaceCount = 0; - let badgeReference: HTMLImageElement; - const mouse = tweened( - { x: 0, y: 0 }, - { - duration: 75, - easing: cubicOut - } - ); + let replaceCount = 0; + let badgeReference: HTMLImageElement; + const mouse = tweened( + { x: 0, y: 0 }, + { + duration: 75, + easing: cubicOut + } + ); - const delayedReplace = (event: Event, image: string | undefined | null) => { - if (replaceCount >= maxReplaceCount) return; + const delayedReplace = (event: Event, image: string | undefined | null) => { + if (replaceCount >= maxReplaceCount) return; - setTimeout(() => { - (event.target as HTMLImageElement).src = image || ''; + setTimeout(() => { + (event.target as HTMLImageElement).src = image || ''; - replaceCount += 1; - }, replaceDelay); - }; + replaceCount += 1; + }, replaceDelay); + }; - const handleMouseMove = (event: MouseEvent) => { - const boundingRectangle = badgeReference.getBoundingClientRect(); - const factor = 1.25; - const limit = 50; + const handleMouseMove = (event: MouseEvent) => { + const boundingRectangle = badgeReference.getBoundingClientRect(); + const factor = 1.25; + const limit = 50; - if ($mouse.x === 0 && $mouse.y === 0) $mouse = { x: event.clientX, y: event.clientY }; + if ($mouse.x === 0 && $mouse.y === 0) $mouse = { x: event.clientX, y: event.clientY }; - $mouse.x += - (-(event.clientX - boundingRectangle.left - boundingRectangle.width / 2) - $mouse.x) * factor; - $mouse.y += - (-(event.clientY - boundingRectangle.top - boundingRectangle.height / 2) - $mouse.y) * factor; - $mouse.x = Math.max(Math.min($mouse.x, limit), -limit); - $mouse.y = Math.max(Math.min($mouse.y, limit), -limit); - }; + $mouse.x += + (-(event.clientX - boundingRectangle.left - boundingRectangle.width / 2) - $mouse.x) * factor; + $mouse.y += + (-(event.clientY - boundingRectangle.top - boundingRectangle.height / 2) - $mouse.y) * factor; + $mouse.x = Math.max(Math.min($mouse.x, limit), -limit); + $mouse.y = Math.max(Math.min($mouse.y, limit), -limit); + }; - const handleMouseLeave = () => { - $mouse = { x: 0, y: 0 }; - }; + const handleMouseLeave = () => { + $mouse = { x: 0, y: 0 }; + }; - const badgeToAny = (badge: Badge) => badge as any; + const badgeToAny = (badge: Badge) => badge as any; </script> {#if replaceCount < maxReplaceCount} - <Tooltip - content={`${dev && !awc ? `${badge.id} ${badge.click_count}\n` : ''}${ - badge.time ? $locale().dateFormatter(databaseTimeToDate(badge.time)) : '' - }${badge.description ? `${awc ? '' : '\n'}${badge.description}` : ''}${ - badge.designer ? `\nDesigner: ${classifyDesignerName(badge.designer)}` : '' - }`} - pin={`badge-${awc ? index : badge.id}`} - pinPosition="top" - relative={preferences && - preferences.badge_wall_css !== undefined && - preferences.badge_wall_css.includes('backdrop-filter')} - > - <a - href={awc ? badgeToAny(badge).link : '#'} - target="_blank" - class="badge-container badge" - on:mousemove={handleMouseMove} - on:mouseleave={handleMouseLeave} - on:click={(e) => { - if (!awc) { - e.preventDefault(); + <Tooltip + content={`${dev && !awc ? `${badge.id} ${badge.click_count}\n` : ''}${ + badge.time ? $locale().dateFormatter(databaseTimeToDate(badge.time)) : '' + }${badge.description ? `${awc ? '' : '\n'}${badge.description}` : ''}${ + badge.designer ? `\nDesigner: ${classifyDesignerName(badge.designer)}` : '' + }`} + pin={`badge-${awc ? index : badge.id}`} + pinPosition="top" + relative={preferences && + preferences.badge_wall_css !== undefined && + preferences.badge_wall_css.includes('backdrop-filter')} + > + <a + href={awc ? badgeToAny(badge).link : '#'} + target="_blank" + class="badge-container badge" + on:mousemove={handleMouseMove} + on:mouseleave={handleMouseLeave} + on:click={(e) => { + if (!awc) { + e.preventDefault(); - selectedBadge = badge; - } - }} - > - <img - src={source} - alt={alternative} - loading="lazy" - class="badge" - bind:this={badgeReference} - style="transform: perspective(1000px) rotateX({$mouse.y / 10}deg) rotateY({-$mouse.x / - 10}deg); ${style}" - on:error={(e) => delayedReplace(e, fallback)} - /> - </a> - </Tooltip> + selectedBadge = badge; + } + }} + > + <img + src={source} + alt={alternative} + loading="lazy" + class="badge" + bind:this={badgeReference} + style="transform: perspective(1000px) rotateX({$mouse.y / 10}deg) rotateY({-$mouse.x / + 10}deg); ${style}" + on:error={(e) => delayedReplace(e, fallback)} + /> + </a> + </Tooltip> {:else if !hideOnError} - <img src={error} alt="Not found" loading="lazy" class="badge" /> + <img src={error} alt="Not found" loading="lazy" class="badge" /> {/if} <style lang="scss"> - $transition: transform 0.325s ease; + $transition: transform 0.325s ease; - .badge { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - transition: $transition; - box-sizing: border-box; - border-radius: 8px; - } + .badge { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + transition: $transition; + box-sizing: border-box; + border-radius: 8px; + } - .badge:hover { - transform: scale(1.075); - position: relative; - z-index: 2; - transition: $transition; - } + .badge:hover { + transform: scale(1.075); + position: relative; + z-index: 2; + transition: $transition; + } </style> diff --git a/src/lib/User/BadgeWall/badge.ts b/src/lib/User/BadgeWall/badge.ts index 0c15b9ca..a4ba431f 100644 --- a/src/lib/User/BadgeWall/badge.ts +++ b/src/lib/User/BadgeWall/badge.ts @@ -1,4 +1,4 @@ -import type { Badge } from "../../../graphql/user/$types"; +import type { Badge } from '../../../graphql/user/$types'; export interface IndexedBadge extends Badge { index: number; diff --git a/src/lib/User/BadgeWall/badges.css b/src/lib/User/BadgeWall/badges.css index 19f8996f..0dffa860 100644 --- a/src/lib/User/BadgeWall/badges.css +++ b/src/lib/User/BadgeWall/badges.css @@ -6,11 +6,11 @@ } */ .badges { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(8%, 1fr)); - gap: 0.25rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(8%, 1fr)); + gap: 0.25rem; } .edit-row-2 { - margin-top: -1.25rem; + margin-top: -1.25rem; } |