pinafore/src/routes/_store/observers/wordFilterObservers.js

98 wiersze
4.4 KiB
JavaScript

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 })
}