diff --git a/src/background/index.js b/src/background/index.js index 46d5125..33aca7d 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,6 +1,8 @@ import alarm from 'alarm' import i18n from 'i18n' +import { differenceBy, reject } from 'lodash' import * as requests from 'requests' +import { ENDED_LIVES } from 'shared/store/keys' import store from 'store' import { getUnix } from 'utils' import browser from 'webextension-polyfill' @@ -42,6 +44,10 @@ const initOnce = async () => { lastSuccessRequestTime = timestamp }) + store.subscribe(ENDED_LIVES, async (lives, prevLives) => workflows.syncHotnesses( + reject(differenceBy(lives, prevLives, 'id'), 'hotnesses'), + )) + browser.alarms.onAlarm.addListener(handleAlarm) browser.alarms.create(ALARM_NAME, { periodInMinutes: 1 }) } diff --git a/src/background/requests.js b/src/background/requests.js index 9adb530..4b63748 100644 --- a/src/background/requests.js +++ b/src/background/requests.js @@ -29,11 +29,12 @@ const fetchData = async (...args) => { return gatherResponse(response) } -async function* pagedItemsFetcher(endpoint, params = {}) { +async function* pagedItemsFetcher(endpoint, params = {}, paramEntries = []) { const safeParams = mapKeys(params, (_, key) => snakeCase(key)) const { limit = 50 } = safeParams const searchParams = new URLSearchParams({ limit, ...safeParams }) + paramEntries.forEach(entry => searchParams.append(...entry)) let page = 0 let shouldContinue = true @@ -72,6 +73,10 @@ const getEndedLives = async params => { const getOpenLives = params => gatherPagedItems('lives/open', params) +const getHotnessesOfLives = (lives, params) => (lives.length + ? gatherPagedItems('hotnesses', params, lives.map(({ id }) => ['lives[]', id])) + : {}) + const getChannels = () => gatherPagedItems('channels', { limit: 100 }) const getMembers = () => fetchData(`${TARGET}api/v1/members`) @@ -79,6 +84,7 @@ const getMembers = () => fetchData(`${TARGET}api/v1/members`) export { getEndedLives, getOpenLives, + getHotnessesOfLives, getChannels, getMembers, onSuccessRequest, diff --git a/src/background/workflows/index.js b/src/background/workflows/index.js index c01b811..81308c3 100644 --- a/src/background/workflows/index.js +++ b/src/background/workflows/index.js @@ -3,12 +3,19 @@ import { differenceBy, filter, findLastIndex, + groupBy, partition, range, reverse, uniqBy, } from 'lodash' -import { getChannels, getEndedLives, getMembers, getOpenLives } from 'requests' +import { + getChannels, + getEndedLives, + getHotnessesOfLives, + getMembers, + getOpenLives, +} from 'requests' import { APPEARANCE, CHANNELS, @@ -119,6 +126,24 @@ const syncOpenLives = async () => { return [...currentLives, ...scheduledLives] } +const syncHotnesses = async (lives = []) => { + if (lives.length === 0) { + return + } + + const hotnessesByLiveId = groupBy(await getHotnessesOfLives(lives, { limit: 1000 }), 'live_id') + + await store.set({ + [ENDED_LIVES]: (getCachedEndedLives() ?? []).map(live => { + if (live['id'] in hotnessesByLiveId) { + return { ...live, hotnesses: hotnessesByLiveId[live['id']] } + } + + return live + }), + }) +} + const getCachedChannels = () => store.get(CHANNELS) const syncChannels = async () => { @@ -221,6 +246,7 @@ export default { clearCachedEndedLives, getCachedScheduledLives, syncOpenLives, + syncHotnesses, getCachedChannels, syncChannels, getCachedMembers, diff --git a/src/background/workflows/index.test.js b/src/background/workflows/index.test.js index 6d2c0e4..0bbb20e 100644 --- a/src/background/workflows/index.test.js +++ b/src/background/workflows/index.test.js @@ -1,6 +1,12 @@ import dayjs from 'dayjs' import duration from 'dayjs/plugin/duration' -import { getChannels, getEndedLives, getMembers, getOpenLives } from 'requests' +import { + getChannels, + getEndedLives, + getHotnessesOfLives, + getMembers, + getOpenLives, +} from 'requests' import { APPEARANCE, CHANNELS, @@ -201,7 +207,7 @@ test('should get cached scheduled lives', async () => { expect(workflows.getCachedScheduledLives()).toEqual(scheduledLives) }) -test('should sync current lives', async () => { +test('should sync open lives', async () => { Date.now = jest.fn(() => unixTime * 1000) // First run @@ -363,6 +369,31 @@ test('should sync current lives', async () => { expect(returnValueFour).toEqual(openLivesFour) }) +test('should sync hotnesses', async () => { + Date.now = jest.fn(() => unixTime * 1000) + + const endedLivesOne = [ + { id: 1, hotnesses: [{ watching: 1 }] }, { id: 2 }, { id: 3 }, + ] + const hotnessesOne = [ + { live_id: 1, watching: 2 }, + { live_id: 2, watching: 1 }, + { live_id: 2, watching: 1 }, + { live_id: 4, watching: 1 }, + ] + await store.set({ [ENDED_LIVES]: endedLivesOne }) + + getHotnessesOfLives.mockResolvedValueOnce(hotnessesOne) + + await workflows.syncHotnesses(endedLivesOne) + + expect(store.data[ENDED_LIVES]).toEqual([ + { id: 1, hotnesses: [hotnessesOne[0]] }, + { id: 2, hotnesses: [hotnessesOne[1], hotnessesOne[2]] }, + { id: 3 }, + ]) +}) + test('should get cached channels', async () => { const channels = [{ id: 1 }, { id: 2 }] diff --git a/src/popup/components/live-item.vue b/src/popup/components/live-item.vue index 03c042d..e4c732f 100644 --- a/src/popup/components/live-item.vue +++ b/src/popup/components/live-item.vue @@ -1,13 +1,93 @@