refactor(store): use context api for store access
This commit is contained in:
parent
2a9a02df54
commit
8037bc8711
|
@ -29,17 +29,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { getContext } from 'svelte'
|
||||||
paused,
|
|
||||||
canPrevious,
|
|
||||||
canNext,
|
|
||||||
selectPrevious,
|
|
||||||
selectNext,
|
|
||||||
loading
|
|
||||||
} from '/store.js'
|
|
||||||
import Volume from '/components/Volume'
|
import Volume from '/components/Volume'
|
||||||
import PlayPause from '/components/icons/controls/PlayPause'
|
import PlayPause from '/components/icons/controls/PlayPause'
|
||||||
import Prev from '/components/icons/controls/Prev'
|
import Prev from '/components/icons/controls/Prev'
|
||||||
import Next from '/components/icons/controls/Next'
|
import Next from '/components/icons/controls/Next'
|
||||||
import IconMenu from '/components/icons/Menu'
|
import IconMenu from '/components/icons/Menu'
|
||||||
|
|
||||||
|
const paused = getContext('paused')
|
||||||
|
const canPrevious = getContext('canPrevious')
|
||||||
|
const canNext = getContext('canNext')
|
||||||
|
const selectPrevious = getContext('selectPrevious')
|
||||||
|
const selectNext = getContext('selectNext')
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -28,9 +28,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { queue, next, current, enqueueing, select } from '/store.js'
|
import { getContext } from 'svelte'
|
||||||
import DistanceDate from '/components/DistanceDate.svelte'
|
import DistanceDate from '/components/DistanceDate.svelte'
|
||||||
|
|
||||||
|
const current = getContext('current')
|
||||||
|
const enqueueing = getContext('enqueueing')
|
||||||
|
const next = getContext('next')
|
||||||
|
const queue = getContext('queue')
|
||||||
|
const select = getContext('select')
|
||||||
|
|
||||||
$: history = $queue.filter(x => x !== $next).reverse()
|
$: history = $queue.filter(x => x !== $next).reverse()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,121 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { setContext } from 'svelte'
|
||||||
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 { get, writable, writableLocalStorage, derived, scan, wait, startWith, distinct } from '/services/store.js'
|
||||||
import { queue, next, current, select } from '/store.js'
|
import { radioIterator, radioShareIterator } from '/services/radio.js'
|
||||||
|
import DeepSet from '/services/deep-set.js'
|
||||||
|
|
||||||
export let share
|
export let share
|
||||||
|
|
||||||
|
const cache = new DeepSet()
|
||||||
|
|
||||||
|
const domain = writableLocalStorage('domain', 'eldritch.cafe')
|
||||||
|
|
||||||
|
const hashtags = writableLocalStorage('hashtags', [
|
||||||
|
'np',
|
||||||
|
'nowplaying',
|
||||||
|
'tootradio',
|
||||||
|
'pouetradio'
|
||||||
|
])
|
||||||
|
|
||||||
|
const paused = writable(true)
|
||||||
|
const muted = writableLocalStorage('muted', false)
|
||||||
|
const volume = writableLocalStorage('volume', 100)
|
||||||
|
|
||||||
|
const current = writable(null)
|
||||||
|
const enqueueing = writable(false)
|
||||||
|
const loading = writable(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const iterator = derived([domain, hashtags], ([$domain, $hashtags], set) => {
|
||||||
|
const iterator = share == null
|
||||||
|
? radioIterator($domain, $hashtags, cache)
|
||||||
|
// this is a bit dump because we always requeue the shared track
|
||||||
|
: radioShareIterator(share, $domain, $hashtags, cache)
|
||||||
|
|
||||||
|
set(iterator)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
iterator.return()
|
||||||
|
}
|
||||||
|
}, null)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
|
const queue = next
|
||||||
|
.pipe(scan((a, x) => x == null ? a : [...a, x], []))
|
||||||
|
|
||||||
|
const index = derived([queue, current], ([$queue, $current]) => {
|
||||||
|
const i = $queue.indexOf($current)
|
||||||
|
return i === -1 ? null : i
|
||||||
|
})
|
||||||
|
|
||||||
|
const canPrevious = derived([queue, index], ([$queue, $index]) => $index !== null && $index > 0)
|
||||||
|
const canNext = derived([queue, index], ([$queue, $index]) => $index !== null && $index < $queue.length - 1)
|
||||||
|
|
||||||
|
|
||||||
|
const select = track => {
|
||||||
|
console.log(`Select ${track.title}`)
|
||||||
|
current.set(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectPrevious = () => {
|
||||||
|
if (get(canPrevious)) {
|
||||||
|
const $queue = get(queue)
|
||||||
|
const $index = get(index)
|
||||||
|
select($queue[$index - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectNext = () => {
|
||||||
|
if (get(canNext)) {
|
||||||
|
const $queue = get(queue)
|
||||||
|
const $index = get(index)
|
||||||
|
select($queue[$index + 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext('paused', paused)
|
||||||
|
setContext('muted', muted)
|
||||||
|
setContext('volume', volume)
|
||||||
|
setContext('domain', domain)
|
||||||
|
setContext('hashtags', hashtags)
|
||||||
|
setContext('current', current)
|
||||||
|
setContext('loading', loading)
|
||||||
|
setContext('enqueueing', enqueueing)
|
||||||
|
setContext('iterator', iterator)
|
||||||
|
setContext('next', next)
|
||||||
|
setContext('queue', queue)
|
||||||
|
setContext('canPrevious', canPrevious)
|
||||||
|
setContext('canNext', canNext)
|
||||||
|
setContext('select', select)
|
||||||
|
setContext('selectPrevious', selectPrevious)
|
||||||
|
setContext('selectNext', selectNext)
|
||||||
|
|
||||||
$: if ($queue.length === 1 && $next != null && $current == null) {
|
$: if ($queue.length === 1 && $next != null && $current == null) {
|
||||||
select($next)
|
select($next)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,13 +43,19 @@
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { get } from 'svelte/store'
|
import { getContext } from 'svelte'
|
||||||
import Controls from '/components/Controls.svelte'
|
import Controls from '/components/Controls.svelte'
|
||||||
import IconReduce from '/components/icons/player/Reduce.svelte'
|
import IconReduce from '/components/icons/player/Reduce.svelte'
|
||||||
import IconHeart from '/components/icons/Heart.svelte'
|
import IconHeart from '/components/icons/Heart.svelte'
|
||||||
import YoutubePlayer from '/components/YoutubePlayer'
|
import YoutubePlayer from '/components/YoutubePlayer'
|
||||||
import Progress from '/components/player/Progress'
|
import Progress from '/components/player/Progress'
|
||||||
import { paused, muted, volume, current, selectNext, loading } from '/store.js'
|
|
||||||
|
const paused = getContext('paused')
|
||||||
|
const muted = getContext('muted')
|
||||||
|
const volume = getContext('volume')
|
||||||
|
const current = getContext('current')
|
||||||
|
const loading = getContext('loading')
|
||||||
|
const selectNext = getContext('selectNext')
|
||||||
|
|
||||||
let ready = null
|
let ready = null
|
||||||
let ended = null
|
let ended = null
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { volume } from '/store.js'
|
import { getContext } from 'svelte'
|
||||||
|
|
||||||
|
const volume = getContext('volume')
|
||||||
|
|
||||||
// to fix visual glitch
|
// to fix visual glitch
|
||||||
$: volumePercent = $volume ? $volume -2 : 0
|
$: volumePercent = $volume - 2
|
||||||
</script>
|
</script>
|
|
@ -9,9 +9,9 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { getContext } from 'svelte'
|
||||||
paused,
|
|
||||||
current,
|
const paused = getContext('paused')
|
||||||
loading
|
const current = getContext('current')
|
||||||
} from '/store.js'
|
const loading = getContext('loading')
|
||||||
</script>
|
</script>
|
|
@ -18,6 +18,10 @@ export const fetchStatus = (domain, id) => fetch(`https://${domain}/api/v1/statu
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(status => processStatus(domain, status))
|
.then(status => processStatus(domain, status))
|
||||||
|
|
||||||
|
export async function* statusIterator({ domain, id }) {
|
||||||
|
yield await fetchStatus(domain, id)
|
||||||
|
}
|
||||||
|
|
||||||
// Observable<{ domain : string, hashtag : string, status : Status}>
|
// Observable<{ domain : string, hashtag : string, status : Status}>
|
||||||
export const hashtagStreamingObservable = (domain, hashtag) => {
|
export const hashtagStreamingObservable = (domain, hashtag) => {
|
||||||
return new Observable(observer => {
|
return new Observable(observer => {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { asyncPrepend } from 'iter-tools'
|
import { asyncConcat } from 'iter-tools'
|
||||||
import { hashtagsIterator } from '/services/mastodon.js'
|
import { hashtagsIterator, statusIterator } from '/services/mastodon.js'
|
||||||
import { tracksIterator } from '/services/misc.js'
|
import { tracksIterator } from '/services/misc.js'
|
||||||
|
|
||||||
export const radioIterator = (domain, hashtags, cache) =>
|
export const radioIterator = (domain, hashtags, cache) =>
|
||||||
tracksIterator(hashtagsIterator(domain, hashtags), cache)
|
tracksIterator(hashtagsIterator(domain, hashtags), cache)
|
||||||
|
|
||||||
export const radioShareIterator = (track, domain, hashtags, cache) =>
|
export const radioShareIterator = (refererCredentials, domain, hashtags, cache) =>
|
||||||
tracksIterator(asyncPrepend(track, hashtagsIterator(domain, hashtags)), cache)
|
tracksIterator(asyncConcat(statusIterator(refererCredentials), hashtagsIterator(domain, hashtags)), cache)
|
90
src/store.js
90
src/store.js
|
@ -1,90 +0,0 @@
|
||||||
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')
|
|
||||||
|
|
||||||
export const hashtags = writableLocalStorage('hashtags', [
|
|
||||||
'np',
|
|
||||||
'nowplaying',
|
|
||||||
'tootradio',
|
|
||||||
'pouetradio'
|
|
||||||
])
|
|
||||||
|
|
||||||
export const paused = writable(true)
|
|
||||||
export const muted = writableLocalStorage('muted', false)
|
|
||||||
export const volume = writableLocalStorage('volume', 100)
|
|
||||||
|
|
||||||
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]) => {
|
|
||||||
const i = $queue.indexOf($current)
|
|
||||||
return i === -1 ? null : i
|
|
||||||
})
|
|
||||||
|
|
||||||
export const canPrevious = derived([queue, index], ([$queue, $index]) => $index !== null && $index > 0)
|
|
||||||
export const canNext = derived([queue, index], ([$queue, $index]) => $index !== null && $index < $queue.length - 1)
|
|
||||||
|
|
||||||
|
|
||||||
export const select = track => {
|
|
||||||
console.log(`Select ${track.title}`)
|
|
||||||
current.set(track)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const selectPrevious = () => {
|
|
||||||
if (get(canPrevious)) {
|
|
||||||
const $queue = get(queue)
|
|
||||||
const $index = get(index)
|
|
||||||
select($queue[$index - 1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const selectNext = () => {
|
|
||||||
if (get(canNext)) {
|
|
||||||
const $queue = get(queue)
|
|
||||||
const $index = get(index)
|
|
||||||
select($queue[$index + 1])
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue