diff --git a/routes/_actions/deleteStatuses.js b/routes/_actions/deleteStatuses.js index 2939adee..b4617bdf 100644 --- a/routes/_actions/deleteStatuses.js +++ b/routes/_actions/deleteStatuses.js @@ -3,30 +3,46 @@ import { store } from '../_store/store' import { scheduleIdleTask } from '../_utils/scheduleIdleTask' import { database } from '../_database/database' import forEach from 'lodash/forEach' +import isEqual from 'lodash/isEqual' -function deleteStatusIdsFromStore (instanceName, idsToDelete) { - let idsToDeleteSet = new Set(idsToDelete) - let idWasNotDeleted = id => !idsToDeleteSet.has(id) +function filterItemIdsFromTimelines (instanceName, timelineFilter, idFilter) { + let keys = ['timelineItemIds', 'itemIdsToAdd'] - let timelinesToTimelineItemIds = store.getAllTimelineData(instanceName, 'timelineItemIds') - - forEach(timelinesToTimelineItemIds, (timelineItemIds, timelineName) => { - store.setForTimeline(instanceName, timelineName, { - timelineItemIds: timelineItemIds.filter(idWasNotDeleted) - }) - }) - - let timelinesToItemIdsToAdd = store.getAllTimelineData(instanceName, 'itemIdsToAdd') - - forEach(timelinesToItemIdsToAdd, (itemIdsToAdd, timelineName) => { - store.setForTimeline(instanceName, timelineName, { - itemIdsToAdd: itemIdsToAdd.filter(idWasNotDeleted) + keys.forEach(key => { + let timelineData = store.getAllTimelineData(instanceName, key) + forEach(timelineData, (ids, timelineName) => { + if (!timelineFilter(timelineName)) { + return + } + let filteredIds = ids.filter(idFilter) + if (!isEqual(ids, filteredIds)) { + store.setForTimeline(instanceName, timelineName, { + [key]: filteredIds + }) + } }) }) } +function deleteStatusIdsFromStore (instanceName, idsToDelete) { + let idsToDeleteSet = new Set(idsToDelete) + let idWasNotDeleted = id => !idsToDeleteSet.has(id) + let notNotificationTimeline = timelineName => timelineName !== 'notifications' + + filterItemIdsFromTimelines(instanceName, notNotificationTimeline, idWasNotDeleted) +} + +function deleteNotificationIdsFromStore (instanceName, idsToDelete) { + let idsToDeleteSet = new Set(idsToDelete) + let idWasNotDeleted = id => !idsToDeleteSet.has(id) + let isNotificationTimeline = timelineName => timelineName === 'notifications' + + filterItemIdsFromTimelines(instanceName, isNotificationTimeline, idWasNotDeleted) +} + async function deleteStatusesAndNotifications (instanceName, statusIdsToDelete, notificationIdsToDelete) { deleteStatusIdsFromStore(instanceName, statusIdsToDelete) + deleteNotificationIdsFromStore(instanceName, notificationIdsToDelete) await database.deleteStatusesAndNotifications(instanceName, statusIdsToDelete, notificationIdsToDelete) } @@ -34,7 +50,7 @@ async function doDeleteStatus (instanceName, statusId) { console.log('deleting statusId', statusId) let rebloggedIds = await getIdsThatRebloggedThisStatus(instanceName, statusId) let statusIdsToDelete = Array.from(new Set([statusId].concat(rebloggedIds).filter(Boolean))) - let notificationIdsToDelete = new Set(await getNotificationIdsForStatuses(instanceName, statusIdsToDelete)) + let notificationIdsToDelete = Array.from(new Set(await getNotificationIdsForStatuses(instanceName, statusIdsToDelete))) await deleteStatusesAndNotifications(instanceName, statusIdsToDelete, notificationIdsToDelete) } diff --git a/routes/_database/timelines.js b/routes/_database/timelines.js index df358851..83b77697 100644 --- a/routes/_database/timelines.js +++ b/routes/_database/timelines.js @@ -301,13 +301,15 @@ export async function getReblogsForStatus (instanceName, id) { export async function getNotificationIdsForStatuses (instanceName, statusIds) { const db = await getDatabase(instanceName) - await dbPromise(db, NOTIFICATIONS_STORE, 'readonly', (notificationsStore, callback) => { + return dbPromise(db, NOTIFICATIONS_STORE, 'readonly', (notificationsStore, callback) => { let res = [] callback(res) statusIds.forEach(statusId => { let req = notificationsStore.index(STATUS_ID).getAllKeys(IDBKeyRange.only(statusId)) req.onsuccess = e => { - res = res.concat(e.target.result) + for (let id of e.target.result) { + res.push(id) + } } }) }) @@ -371,7 +373,7 @@ export async function deleteStatusesAndNotifications (instanceName, statusIds, n notificationsStore.delete(notificationId) deleteAll( notificationTimelinesStore, - notificationTimelinesStore.index('statusId'), + notificationTimelinesStore.index('notificationId'), IDBKeyRange.only(notificationId) ) } diff --git a/tests/spec/100-favorite-unfavorite.js b/tests/spec/100-favorite-unfavorite.js index cc09b587..1ed7cc87 100644 --- a/tests/spec/100-favorite-unfavorite.js +++ b/tests/spec/100-favorite-unfavorite.js @@ -5,7 +5,7 @@ import { } from '../utils' import { foobarRole } from '../roles' -fixture`130-favorite-unfavorite.js` +fixture`100-favorite-unfavorite.js` .page`http://localhost:4002` test('favorites a status', async t => { diff --git a/tests/spec/105-deletes.js b/tests/spec/105-deletes.js index 1954a1b1..326199f5 100644 --- a/tests/spec/105-deletes.js +++ b/tests/spec/105-deletes.js @@ -1,6 +1,7 @@ import { foobarRole } from '../roles' import { clickToNotificationsAndBackHome, forceOffline, forceOnline, getNthStatus, getUrl, homeNavButton, + notificationsNavButton, sleep } from '../utils' import { deleteAsAdmin, postAsAdmin, postReplyAsAdmin } from '../serverActions' @@ -53,3 +54,15 @@ test('deleted statuses are removed from threads', async t => { .expect(getNthStatus(0).innerText).contains("I won't delete this") await forceOnline() }) + +test('deleted statuses result in deleted notifications', async t => { + await t.useRole(foobarRole) + .hover(getNthStatus(0)) + .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') + let status = await postAsAdmin("@foobar yo yo foobar what's up") + await sleep(2000) + await t.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (1)') + await deleteAsAdmin(status.id) + await sleep(5000) + await t.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') +})