import { on } from '../../_utils/eventBus.js' import { updateFiltersForInstance } from '../../_actions/filters.js' import { store } from '../store.js' import { isEqual } from '../../_thirdparty/lodash/objects.js' import { computeFilterContextsForStatusOrNotification } from '../../_utils/computeFilterContextsForStatusOrNotification.js' import { database } from '../../_database/database.js' import { mark, stop } from '../../_utils/marks.js' export function wordFilterObservers () { if (!process.browser) { return } on('wordFiltersChanged', instanceName => { /* no await */ updateFiltersForInstance(instanceName) }) // compute `unexpiredInstanceFilters` based on `now` and `instanceFilters`. `now` updates every 10 seconds. function updateUnexpiredInstanceFiltersIfUnchanged (now, instanceFilters) { const unexpiredInstanceFilters = Object.fromEntries(Object.entries(instanceFilters).map(([instanceName, filters]) => { const unexpiredFilters = filters.filter(filter => ( !filter.expires_at || new Date(filter.expires_at).getTime() >= now )) return [instanceName, unexpiredFilters] })) // don't force an update/recalc if nothing changed if (!isEqual(store.get().unexpiredInstanceFilters, unexpiredInstanceFilters)) { console.log('updated unexpiredInstanceFilters', unexpiredInstanceFilters) store.set({ unexpiredInstanceFilters }) } } store.observe('now', now => { const { instanceFilters } = store.get() updateUnexpiredInstanceFiltersIfUnchanged(now, instanceFilters) }) store.observe('instanceFilters', instanceFilters => { const { now } = store.get() updateUnexpiredInstanceFiltersIfUnchanged(now, instanceFilters) }) store.observe('unexpiredInstanceFilterRegexes', async unexpiredInstanceFilterRegexes => { console.log('unexpiredInstanceFilterRegexes changed, recomputing filterContexts') mark('update timeline item summary filter contexts') // Whenever the filters change, we need to re-compute the filterContexts on the TimelineSummaries. // This is a bit of an odd design, but we do it for perf. See timelineItemToSummary.js for details. let { timelineData_timelineItemSummaries: timelineItemSummaries, timelineData_timelineItemSummariesToAdd: timelineItemSummariesToAdd } = store.get() timelineItemSummaries = timelineItemSummaries || {} timelineItemSummariesToAdd = timelineItemSummariesToAdd || {} let somethingChanged = false await Promise.all(Object.entries(unexpiredInstanceFilterRegexes).map(async ([instanceName, contextsToRegex]) => { const timelinesToSummaries = timelineItemSummaries[instanceName] || {} const timelinesToSummariesToAdd = timelineItemSummariesToAdd[instanceName] || {} const summariesToUpdate = [ ...(Object.values(timelinesToSummaries).flat()), ...(Object.values(timelinesToSummariesToAdd).flat()) ] console.log(`Attempting to update filters for ${summariesToUpdate.length} item summaries`) await Promise.all(summariesToUpdate.map(async summary => { try { const isNotification = summary.type const item = await (isNotification ? database.getNotification(instanceName, summary.id) : database.getStatus(instanceName, summary.id) ) const newFilterContexts = computeFilterContextsForStatusOrNotification(item, contextsToRegex) if (!isEqual(summary.filterContexts, newFilterContexts)) { somethingChanged = true summary.filterContexts = newFilterContexts } } catch (err) { console.error(err) // not stored in the database anymore, just ignore } })) })) // The previous was an async operation, so the timelinesItemSummaries or timelineItemSummariesToAdd // may have changed. But we need to make sure that the filterContexts are updated in the store // So just force an update here. if (somethingChanged) { console.log('Word filters changed, forcing an update') // eslint-disable-next-line camelcase const { timelineData_timelineItemSummaries, timelineData_timelineItemSummariesToAdd } = store.get() // eslint-disable-next-line camelcase store.set({ timelineData_timelineItemSummaries, timelineData_timelineItemSummariesToAdd }) } stop('update timeline item summary filter contexts') }, { init: false }) }