refact(store): use fp transformations
This commit is contained in:
parent
7efd05ef3d
commit
2a9a02df54
|
@ -6293,8 +6293,7 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.clone": {
|
"lodash.clone": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
|
@ -8675,6 +8674,14 @@
|
||||||
"integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=",
|
"integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=",
|
||||||
"dev": true
|
"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": {
|
"svelte-routing": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.4.0.tgz",
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"get-urls": "^9.2.0",
|
"get-urls": "^9.2.0",
|
||||||
"iter-tools": "^7.0.0-rc.0",
|
"iter-tools": "^7.0.0-rc.0",
|
||||||
"route-parser": "0.0.5",
|
"route-parser": "0.0.5",
|
||||||
|
"svelte-pipeable-store": "^1.0.3",
|
||||||
"svelte-routing": "^1.4.0"
|
"svelte-routing": "^1.4.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|
|
@ -6,10 +6,14 @@
|
||||||
<div class="title">{$next.title}</div>
|
<div class="title">{$next.title}</div>
|
||||||
<div class="user">shared by {$next.referer.username} <DistanceDate date={$next.date} /></div>
|
<div class="user">shared by {$next.referer.username} <DistanceDate date={$next.date} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
|
NO NEXT TRACK
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{#if $enqueueing}
|
{#if $enqueueing}
|
||||||
LOADING NEXT
|
ENQUEING
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,79 +17,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { onMount, onDestroy } from 'svelte'
|
|
||||||
import { derived, get } from 'svelte/store'
|
|
||||||
|
|
||||||
import Header from '/components/layout/Header.svelte'
|
import Header from '/components/layout/Header.svelte'
|
||||||
import Footer from '/components/layout/Footer.svelte'
|
import Footer from '/components/layout/Footer.svelte'
|
||||||
import Queue from '/components/Queue.svelte'
|
import Queue from '/components/Queue.svelte'
|
||||||
import Viewer from '/components/Viewer.svelte'
|
import Viewer from '/components/Viewer.svelte'
|
||||||
import { radioIterator, radioShareIterator } from '/services/radio.js'
|
|
||||||
import { fetchStatus } from '/services/mastodon.js'
|
|
||||||
|
|
||||||
import { domain, hashtags, queue, next, current, enqueueing, select } from '/store.js'
|
import { queue, next, current, select } from '/store.js'
|
||||||
import DeepSet from '/services/deep-set.js'
|
|
||||||
|
|
||||||
export let share
|
export let share
|
||||||
|
|
||||||
const cache = new DeepSet()
|
$: if ($queue.length === 1 && $next != null && $current == null) {
|
||||||
|
select($next)
|
||||||
let nextUnsubcribe = null
|
}
|
||||||
let currentUnsubcribe = null
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
let iterator
|
|
||||||
|
|
||||||
if (share != null) {
|
|
||||||
const track = await fetchStatus(share.domain, share.id)
|
|
||||||
iterator = radioShareIterator(track, get(domain), get(hashtags), cache)
|
|
||||||
} else {
|
|
||||||
iterator = radioIterator(get(domain), get(hashtags), cache)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generated multiples times cannot usable and don't free resources
|
|
||||||
// const iterator = derived([domain, hashtags], ([$domain, $hashtags]) => radioIterator($domain, $hashtags))
|
|
||||||
|
|
||||||
const { value: first } = await iterator.next()
|
|
||||||
|
|
||||||
queue.set([first])
|
|
||||||
select(first)
|
|
||||||
|
|
||||||
nextUnsubcribe = next.subscribe(async nextValue => {
|
|
||||||
if (nextValue === null) {
|
|
||||||
if (!get(enqueueing)) {
|
|
||||||
enqueueing.set(true)
|
|
||||||
|
|
||||||
const { value: newTrack } = await iterator.next()
|
|
||||||
|
|
||||||
if (newTrack) {
|
|
||||||
queue.update(queueValue => [...queueValue, newTrack])
|
|
||||||
next.set(newTrack)
|
|
||||||
}
|
|
||||||
|
|
||||||
enqueueing.set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
currentUnsubcribe = current.subscribe(currentValue => {
|
|
||||||
if (currentValue !== null) {
|
|
||||||
next.update(nextValue => {
|
|
||||||
if (nextValue === currentValue) {
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
return nextValue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
for (const unsubcribe of [nextUnsubcribe, currentUnsubcribe]) {
|
|
||||||
if (unsubcribe) {
|
|
||||||
unsubcribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
46
src/store.js
46
src/store.js
|
@ -1,5 +1,10 @@
|
||||||
import { writable, derived, get } from 'svelte/store'
|
import { get, writable, derived, scan, wait, startWith, writableLocalStorage } from '/services/store.js'
|
||||||
import { writableLocalStorage } from '/services/svelte.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')
|
export const domain = writableLocalStorage('domain', 'eldritch.cafe')
|
||||||
|
|
||||||
|
@ -14,11 +19,44 @@ export const paused = writable(true)
|
||||||
export const muted = writableLocalStorage('muted', false)
|
export const muted = writableLocalStorage('muted', false)
|
||||||
export const volume = writableLocalStorage('volume', 100)
|
export const volume = writableLocalStorage('volume', 100)
|
||||||
|
|
||||||
export const queue = writable([])
|
|
||||||
export const next = writable(null)
|
|
||||||
export const current = writable(null)
|
export const current = writable(null)
|
||||||
export const enqueueing = writable(false)
|
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)
|
export const loading = writable(false)
|
||||||
|
|
||||||
const index = derived([queue, current], ([$queue, $current]) => {
|
const index = derived([queue, current], ([$queue, $current]) => {
|
||||||
|
|
Loading…
Reference in New Issue