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]) => {