refact(store): use fp transformations
This commit is contained in:
parent
7efd05ef3d
commit
2a9a02df54
|
@ -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",
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -6,10 +6,14 @@
|
|||
<div class="title">{$next.title}</div>
|
||||
<div class="user">shared by {$next.referer.username} <DistanceDate date={$next.date} /></div>
|
||||
</div>
|
||||
{:else}
|
||||
NO NEXT TRACK
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
{#if $enqueueing}
|
||||
LOADING NEXT
|
||||
ENQUEING
|
||||
{/if}
|
||||
|
||||
|
||||
|
|
|
@ -17,79 +17,16 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import { derived, get } from 'svelte/store'
|
||||
|
||||
import Header from '/components/layout/Header.svelte'
|
||||
import Footer from '/components/layout/Footer.svelte'
|
||||
import Queue from '/components/Queue.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 DeepSet from '/services/deep-set.js'
|
||||
import { queue, next, current, select } from '/store.js'
|
||||
|
||||
export let share
|
||||
|
||||
const cache = new DeepSet()
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
})
|
||||
$: if ($queue.length === 1 && $next != null && $current == null) {
|
||||
select($next)
|
||||
}
|
||||
</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 { 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]) => {
|
||||
|
|
Loading…
Reference in New Issue