2020-01-20 12:23:00 +01:00
|
|
|
import Observable from 'core-js-pure/features/observable'
|
2020-01-20 03:26:18 +01:00
|
|
|
import { observableToAsyncIterator } from '/services/misc.js'
|
|
|
|
|
|
|
|
const LINK_RE = /<(.+?)>; rel="(\w+)"/gi
|
|
|
|
|
|
|
|
function parseLinkHeader(link) {
|
|
|
|
const links = {}
|
|
|
|
|
|
|
|
for (const [ , url, name ] of link.matchAll(LINK_RE)) {
|
|
|
|
links[name] = url
|
|
|
|
}
|
|
|
|
|
|
|
|
return links
|
|
|
|
}
|
|
|
|
|
|
|
|
export const hashtagStreamingObservable = (domain, hashtag) => {
|
|
|
|
return new Observable(observer => {
|
|
|
|
const onStatus = (event) => observer.next(JSON.parse(event.data))
|
|
|
|
const onError = (error) => observer.error(error)
|
|
|
|
|
|
|
|
const eventSource = new EventSource(`https://${domain}/api/v1/streaming/hashtag?tag=${hashtag}`)
|
|
|
|
eventSource.addEventListener('update', onStatus)
|
|
|
|
eventSource.addEventListener('error', onError)
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
eventSource.removeEventListener('update', onStatus)
|
|
|
|
eventSource.removeEventListener('error', onError)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function* hashtagTimelineIterator (domain, hashtag) {
|
|
|
|
let nextLink = `https://${domain}/api/v1/timelines/tag/${hashtag}?limit=40`
|
|
|
|
|
|
|
|
while (nextLink) {
|
|
|
|
const response = await fetch(nextLink)
|
|
|
|
|
|
|
|
nextLink = response.headers.has('link')
|
|
|
|
? parseLinkHeader(response.headers.get('link')).next
|
|
|
|
: null
|
|
|
|
|
|
|
|
yield* await response.json()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function* hashtagIterator(domain, hashtag) {
|
|
|
|
const newerIterator = observableToAsyncIterator(hashtagStreamingObservable(domain, hashtag))
|
|
|
|
const olderIterator = hashtagTimelineIterator(domain, hashtag)
|
|
|
|
|
|
|
|
let newer = newerIterator.next()
|
|
|
|
let older = olderIterator.next()
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
const promises = [newer, older].map((promise, index) => promise.then(result => ({ index, result })))
|
|
|
|
const { index, result: { done, value } } = await Promise.race(promises)
|
|
|
|
|
|
|
|
switch (index) {
|
|
|
|
default:
|
|
|
|
throw new Error()
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
newer = newerIterator.next()
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
older = olderIterator.next()
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
yield value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|