your personal website on atproto - mirror blento.app
26
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #300 from polijn/chore/update-contrail

soundcloud card

authored by

Florian and committed by
GitHub
a4fe224a f75e2fce

+131
+2
src/lib/cards/index.ts
··· 37 37 import { CountdownCardDefinition } from './utilities/CountdownCard'; 38 38 import { SpotifyCardDefinition } from './media/SpotifyCard'; 39 39 import { AppleMusicCardDefinition } from './media/AppleMusicCard'; 40 + import { SoundCloudCardDefinition } from './media/SoundCloudCard'; 40 41 import { ButtonCardDefinition } from './utilities/ButtonCard'; 41 42 import { GuestbookCardDefinition } from './social/GuestbookCard'; 42 43 import { FriendsCardDefinition } from './social/FriendsCard'; ··· 105 106 CountdownCardDefinition, 106 107 SpotifyCardDefinition, 107 108 AppleMusicCardDefinition, 109 + SoundCloudCardDefinition, 108 110 // Model3DCardDefinition 109 111 FriendsCardDefinition, 110 112 GitHubContributorsCardDefinition,
+47
src/lib/cards/media/SoundCloudCard/CreateSoundCloudCardModal.svelte
··· 1 + <script lang="ts"> 2 + import { Alert, Button, Input, Subheading } from '@foxui/core'; 3 + import Modal from '$lib/components/modal/Modal.svelte'; 4 + import type { CreationModalComponentProps } from '../../types'; 5 + 6 + let { item = $bindable(), oncreate, oncancel }: CreationModalComponentProps = $props(); 7 + 8 + let errorMessage = $state(''); 9 + 10 + function checkUrl() { 11 + errorMessage = ''; 12 + 13 + const pattern = /^https?:\/\/(www\.)?soundcloud\.com\/[\w-]+(\/[\w-]+)*\/?(\?.*)?$/; 14 + if (!item.cardData.href || !pattern.test(item.cardData.href)) { 15 + errorMessage = 'Please enter a valid SoundCloud URL'; 16 + return false; 17 + } 18 + 19 + return true; 20 + } 21 + </script> 22 + 23 + <Modal open={true} closeButton={false}> 24 + <Subheading>Enter a SoundCloud track, playlist, or profile URL</Subheading> 25 + <Input 26 + bind:value={item.cardData.href} 27 + placeholder="https://soundcloud.com/artist/track" 28 + onkeydown={(e) => { 29 + if (e.key === 'Enter' && checkUrl()) oncreate(); 30 + }} 31 + /> 32 + 33 + {#if errorMessage} 34 + <Alert type="error" title="Invalid URL"><span>{errorMessage}</span></Alert> 35 + {/if} 36 + 37 + <div class="mt-4 flex justify-end gap-2"> 38 + <Button onclick={oncancel} variant="ghost">Cancel</Button> 39 + <Button 40 + onclick={() => { 41 + if (checkUrl()) oncreate(); 42 + }} 43 + > 44 + Create 45 + </Button> 46 + </div> 47 + </Modal>
+31
src/lib/cards/media/SoundCloudCard/SoundCloudCard.svelte
··· 1 + <script lang="ts"> 2 + import type { ContentComponentProps } from '../../types'; 3 + 4 + let { item, isEditing }: ContentComponentProps = $props(); 5 + 6 + let src = $derived( 7 + item.cardData?.href 8 + ? `https://w.soundcloud.com/player/?url=${encodeURIComponent( 9 + item.cardData.href 10 + )}&color=%23ff5500&auto_play=false&hide_related=false&show_comments=false&show_user=true&show_reposts=false&show_teaser=false&visual=true` 11 + : null 12 + ); 13 + </script> 14 + 15 + {#if src} 16 + <div class="absolute inset-0 p-2"> 17 + <iframe 18 + class={['h-full w-full rounded-2xl', isEditing && 'pointer-events-none']} 19 + {src} 20 + frameborder="0" 21 + allow="autoplay" 22 + loading="lazy" 23 + scrolling="no" 24 + title="SoundCloud player" 25 + ></iframe> 26 + </div> 27 + {:else} 28 + <div class="flex h-full items-center justify-center p-4 text-center opacity-50"> 29 + Missing SoundCloud URL 30 + </div> 31 + {/if}
+51
src/lib/cards/media/SoundCloudCard/index.ts
··· 1 + import type { CardDefinition } from '../../types'; 2 + import CreateSoundCloudCardModal from './CreateSoundCloudCardModal.svelte'; 3 + import SoundCloudCard from './SoundCloudCard.svelte'; 4 + 5 + const cardType = 'soundcloud-embed'; 6 + 7 + export const SoundCloudCardDefinition = { 8 + type: cardType, 9 + contentComponent: SoundCloudCard, 10 + creationModalComponent: CreateSoundCloudCardModal, 11 + createNew: (item) => { 12 + item.cardType = cardType; 13 + item.cardData = {}; 14 + item.w = 4; 15 + item.mobileW = 8; 16 + item.h = 5; 17 + item.mobileH = 10; 18 + }, 19 + 20 + onUrlHandler: (url, item) => { 21 + if (!matchSoundCloudUrl(url)) return null; 22 + 23 + item.cardData.href = url; 24 + 25 + item.w = 4; 26 + item.mobileW = 8; 27 + item.h = 5; 28 + item.mobileH = 10; 29 + 30 + return item; 31 + }, 32 + 33 + urlHandlerPriority: 2, 34 + 35 + canChange: (item) => matchSoundCloudUrl(item.cardData?.href), 36 + change: (item) => item, 37 + 38 + name: 'SoundCloud Embed', 39 + canResize: true, 40 + minW: 4, 41 + minH: 4, 42 + 43 + keywords: ['music', 'song', 'playlist', 'track', 'soundcloud', 'audio'], 44 + groups: ['Media'], 45 + icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"><path d="M1.175 12.225c-.051 0-.094.046-.101.1l-.233 2.154.233 2.105c.007.058.05.098.101.098.05 0 .09-.04.099-.098l.255-2.105-.27-2.154c-.009-.054-.045-.1-.094-.1zm-.899.828c-.05 0-.092.04-.1.099L0 14.479l.176 1.291c.008.057.049.097.1.097.048 0 .089-.04.099-.097l.207-1.291-.207-1.327c-.01-.058-.051-.099-.099-.099zm1.83-1.275c-.061 0-.111.049-.119.118l-.219 2.583.219 2.493c.008.07.058.117.119.117.06 0 .11-.047.119-.117l.247-2.493-.247-2.583c-.009-.069-.059-.118-.119-.118zm.945-.563c-.07 0-.124.057-.13.137l-.207 3.127.207 2.531c.006.077.06.135.13.135.069 0 .124-.058.131-.135l.234-2.531-.234-3.127c-.007-.08-.062-.137-.131-.137zm1.027-.244c-.078 0-.142.064-.149.151l-.193 3.357.193 2.547c.007.083.071.147.149.147.078 0 .141-.064.15-.147l.219-2.547-.219-3.357c-.009-.087-.072-.151-.15-.151zm1.04-.069c-.085 0-.157.07-.163.162l-.181 3.357.181 2.547c.006.087.078.158.163.158.083 0 .156-.071.165-.158l.205-2.547-.205-3.357c-.009-.092-.082-.162-.165-.162zm1.046.227c-.092 0-.171.078-.179.176l-.165 3.13.165 2.519c.008.094.087.172.179.172.092 0 .17-.078.179-.172l.187-2.519-.187-3.13c-.009-.098-.087-.176-.179-.176zm1.04-.422c-.099 0-.184.084-.193.189l-.149 3.337.149 2.488c.009.103.094.187.193.187.099 0 .183-.084.192-.187l.169-2.488-.169-3.337c-.009-.105-.094-.189-.192-.189zm1.034-.467c-.106 0-.196.089-.205.198l-.135 3.797.135 2.451c.009.107.099.196.205.196.107 0 .197-.089.207-.196l.151-2.451-.151-3.797c-.01-.109-.1-.198-.207-.198zm1.027-.119c-.114 0-.21.097-.219.213l-.119 3.917.12 2.41c.009.115.105.21.218.21.114 0 .21-.095.22-.21l.135-2.41-.135-3.917c-.009-.116-.105-.213-.219-.213zm1.121.209c-.005-.124-.105-.221-.226-.221-.121 0-.221.097-.226.221l-.108 3.806.109 2.366c.005.122.105.219.226.219.12 0 .22-.097.225-.219l.121-2.366-.121-3.806zm.85-.357c-.128 0-.232.105-.237.236l-.097 4.196.099 2.32c.005.128.108.232.236.232.127 0 .231-.104.237-.232l.108-2.32-.108-4.196c-.006-.131-.11-.236-.237-.236zm.964.018c-.006-.135-.114-.244-.247-.244-.134 0-.241.109-.247.244l-.083 4.293.084 2.276c.005.133.113.241.246.241.133 0 .241-.108.247-.241l.094-2.276-.094-4.293zm.842-.39c-.005-.142-.118-.255-.258-.255-.139 0-.252.113-.257.255l-.083 4.69.084 2.227c.005.139.118.252.257.252.14 0 .253-.113.258-.252l.094-2.227-.094-4.69zm.95-.398c-.005-.145-.124-.262-.269-.262-.146 0-.265.117-.269.262l-.071 5.085.072 2.179c.004.145.123.262.268.262.145 0 .264-.117.269-.262l.083-2.179-.083-5.085zm.886-.498c-.005-.151-.13-.272-.281-.272-.151 0-.276.121-.28.272l-.061 5.578.061 2.13c.005.149.129.272.28.272.151 0 .276-.123.281-.272l.071-2.13-.071-5.578zm.991-.108c-.005-.157-.135-.28-.293-.28-.157 0-.287.123-.292.28l-.06 5.689.061 2.084c.005.155.135.281.292.281.158 0 .288-.126.293-.281l.071-2.084-.072-5.689zm1.142-.165c-.171 0-.31.135-.314.305l-.05 5.541.054 2.04c.004.169.144.305.314.305.169 0 .309-.136.314-.306l.061-2.039-.061-5.541c-.005-.169-.144-.305-.314-.305zm5.738 4.116c-.428 0-.836.087-1.207.243-.247-2.795-2.583-4.991-5.444-4.991-.7 0-1.382.143-1.972.378-.227.092-.291.184-.293.367v9.486c.002.193.146.357.336.376h8.581c1.78 0 3.221-1.453 3.221-3.236 0-1.781-1.441-3.234-3.221-3.234z"/></svg>` 46 + } as CardDefinition & { type: typeof cardType }; 47 + 48 + function matchSoundCloudUrl(url: string | undefined): boolean { 49 + if (!url) return false; 50 + return /^https?:\/\/(www\.)?soundcloud\.com\/[\w-]+(\/[\w-]+)*\/?(\?.*)?$/.test(url); 51 + }