Experiment to rebuild Diffuse using web applets.
0

Configure Feed

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

1import * as Uint8 from "uint8arrays"; 2import { createEndpoint, type MessageEndpoint } from "@remote-ui/rpc"; 3import { xxh32 } from "xxh32"; 4 5import type { Track } from "@applets/core/types"; 6 7// export { SharedWorkerPolyfill as SharedWorker } from "@okikio/sharedworker"; 8export const SharedWorker = globalThis.SharedWorker; 9 10export function arrayShuffle<T>(array: Array<T>): Array<T> { 11 if (array.length === 0) { 12 return []; 13 } 14 15 array = [...array]; 16 17 for (let index = array.length - 1; index > 0; index--) { 18 const randArr = crypto.getRandomValues(new Uint32Array(1)); 19 const randVal = randArr[0] / 2 ** 32; 20 const newIndex = Math.floor(randVal * (index + 1)); 21 [array[index], array[newIndex]] = [array[newIndex], array[index]]; 22 } 23 24 return array; 25} 26 27export function cleanUndefinedValuesForTracks(tracks: Track[]): Track[] { 28 return tracks.map((track) => { 29 const t = { ...track }; 30 31 if (t.tags) { 32 if ("album" in t.tags && t.tags.album === undefined) delete t.tags.album; 33 if ("artist" in t.tags && t.tags.artist === undefined) delete t.tags.artist; 34 if ("genre" in t.tags && t.tags.genre === undefined) delete t.tags.genre; 35 if ("year" in t.tags && t.tags.year === undefined) delete t.tags.year; 36 37 if ("of" in t.tags.disc && t.tags.disc.of === undefined) delete t.tags.disc.of; 38 if ("of" in t.tags.track && t.tags.track.of === undefined) delete t.tags.track.of; 39 } 40 41 return t; 42 }); 43} 44 45export function comparable(value: unknown) { 46 return xxh32(JSON.stringify(value)); 47} 48 49export function endpoint<T extends Record<string, any>>(ini: MessageEndpoint) { 50 const e = createEndpoint<T>(ini); 51 if ("start" in ini && typeof ini.start === "function") ini.start(); 52 return e; 53} 54 55export function expose<T extends Record<string, any>>(actions: T): T { 56 if (globalThis.SharedWorkerGlobalScope && self instanceof SharedWorkerGlobalScope) { 57 self.onconnect = (event: MessageEvent) => { 58 const port = event.ports[0]; 59 createEndpoint<T>(port).expose(actions); 60 port.start(); 61 }; 62 63 (self as any).connected = true; 64 } else { 65 createEndpoint<T>(self).expose(actions); 66 } 67 68 return actions; 69} 70 71export function groupTracksPerScheme( 72 tracks: Track[], 73 initial: Record<string, Track[]> = {}, 74): Record<string, Track[]> { 75 return tracks.reduce((acc: Record<string, Track[]>, track: Track) => { 76 const scheme = track.uri.split(":", 1)[0]; 77 return { ...acc, [scheme]: [...(acc[scheme] || []), track] }; 78 }, initial); 79} 80 81export function inIframe() { 82 return window.self !== window.top; 83} 84 85export function isPrimitive(test: unknown) { 86 return test !== Object(test); 87} 88 89export function jsonDecode<T>(a: any): T { 90 return JSON.parse(new TextDecoder().decode(a)); 91} 92 93export function jsonEncode<T>(a: T): Uint8Array { 94 return new TextEncoder().encode(JSON.stringify(a)); 95} 96 97export async function trackArtworkCacheId(track: Track): Promise<string> { 98 return await crypto.subtle 99 .digest("SHA-256", new TextEncoder().encode(track.uri)) 100 .then((a) => Uint8.toString(new Uint8Array(a), "base64url")); 101}