diff --git a/package-lock.json b/package-lock.json index 2c5d1ff..7455361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6293,8 +6293,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.clone": { "version": "4.5.0", @@ -8675,6 +8674,14 @@ "integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=", "dev": true }, + "svelte-pipeable-store": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/svelte-pipeable-store/-/svelte-pipeable-store-1.0.3.tgz", + "integrity": "sha512-VmsVQHPPASqTls53uT0HszKbdn6FTJLdwQvHPA1AJU1U2POYoGnG36+zK0tQzF9F/DHeR2ufvoHV2ozqDiaJ4w==", + "requires": { + "lodash": "^4.17.15" + } + }, "svelte-routing": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.4.0.tgz", diff --git a/package.json b/package.json index de60b62..a285328 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "get-urls": "^9.2.0", "iter-tools": "^7.0.0-rc.0", "route-parser": "0.0.5", + "svelte-pipeable-store": "^1.0.3", "svelte-routing": "^1.4.0" }, "browserslist": [ diff --git a/src/components/Queue.svelte b/src/components/Queue.svelte index d98b358..30dae0e 100644 --- a/src/components/Queue.svelte +++ b/src/components/Queue.svelte @@ -6,10 +6,14 @@
{$next.title}
shared by {$next.referer.username}
+ {:else} + NO NEXT TRACK {/if} + + {#if $enqueueing} - LOADING NEXT + ENQUEING {/if} diff --git a/src/components/Radio.svelte b/src/components/Radio.svelte index cf4200b..62f7a0a 100644 --- a/src/components/Radio.svelte +++ b/src/components/Radio.svelte @@ -17,79 +17,16 @@ \ No newline at end of file diff --git a/src/services/store.js b/src/services/store.js new file mode 100644 index 0000000..5b952a2 --- /dev/null +++ b/src/services/store.js @@ -0,0 +1,26 @@ +import { writable, readable } from 'svelte-pipeable-store' + +export { get } from 'svelte/store' +export * from 'svelte-pipeable-store' + +export const writableLocalStorage = (key, value) => { + const item = JSON.parse(localStorage.getItem(key)) + const store = writable(item === null ? value : item) + + store.subscribe(x => localStorage.setItem(key, JSON.stringify(x))) + + return store +} + +export const distinct = () => { + return ({ subscribe }) => readable(undefined, set => { + let last + + return subscribe(v => { + if (last !== v) { + set(v) + last = v + } + }) + }) +} \ No newline at end of file diff --git a/src/services/svelte.js b/src/services/svelte.js deleted file mode 100644 index cbfb285..0000000 --- a/src/services/svelte.js +++ /dev/null @@ -1,10 +0,0 @@ -import { writable } from 'svelte/store' - -export const writableLocalStorage = (key, value) => { - const item = JSON.parse(localStorage.getItem(key)) - const store = writable(item === null ? value : item) - - store.subscribe(x => localStorage.setItem(key, JSON.stringify(x))) - - return store -} \ No newline at end of file diff --git a/src/store.js b/src/store.js index 0459860..3a59b68 100644 --- a/src/store.js +++ b/src/store.js @@ -1,5 +1,10 @@ -import { writable, derived, get } from 'svelte/store' -import { writableLocalStorage } from '/services/svelte.js' +import { get, writable, derived, scan, wait, startWith, writableLocalStorage } from '/services/store.js' +import { radioIterator, radioShareIterator } from '/services/radio.js' +import DeepSet from '/services/deep-set.js' +import { distinct } from './services/store' + + +const cache = new DeepSet() export const domain = writableLocalStorage('domain', 'eldritch.cafe') @@ -14,11 +19,44 @@ export const paused = writable(true) export const muted = writableLocalStorage('muted', false) export const volume = writableLocalStorage('volume', 100) -export const queue = writable([]) -export const next = writable(null) export const current = writable(null) export const enqueueing = writable(false) + +export const iterator = derived([domain, hashtags], ([$domain, $hashtags], set) => { + const iterator = radioIterator($domain, $hashtags, cache) + set(iterator) + + return () => { + iterator.return() + } +}, null) + + +export const next = derived([iterator, current], ([$iterator, $current]) => ({ $iterator, $current })) + .pipe(scan(($nextPromise, { $iterator, $current }) => { + return $nextPromise.then($next => { + if ($next == null || $next === $current) { + enqueueing.set(true) + return $iterator.next().then(({ done, value }) => { + enqueueing.set(false) + return value + }) + } else { + return $nextPromise + } + }) + }, Promise.resolve(null))) + .pipe(wait(x => x)) + // distinct but with strict check + .pipe(distinct()) + .pipe(startWith(null)) + + +export const queue = next + .pipe(scan((a, x) => x == null ? a : [...a, x], [])) + + export const loading = writable(false) const index = derived([queue, current], ([$queue, $current]) => {