From a17948cf99f6cb726670712eff09de822860fbd6 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sat, 25 May 2019 13:21:36 -0700 Subject: [PATCH] feat: add home/notification filter settings (#1226) Fixes #1223 Fixes #1224 --- .../instance/GenericInstanceSettings.html | 48 +++++++++++++ .../instance/HomeTimelineFilterSettings.html | 29 ++++++++ .../instance/NotificationFilterSettings.html | 50 ++++++++++++++ src/routes/_components/timeline/Timeline.html | 69 ++++++++++++++++++- .../settings/instances/[instanceName].html | 16 +++-- src/routes/_static/instanceSettings.js | 15 ++++ src/routes/_store/mixins/instanceMixins.js | 16 +++++ src/routes/_store/store.js | 1 + ...ilters.js => 033-notification-mentions.js} | 10 +-- tests/spec/034-home-timeline-filters.js | 23 +++++++ .../spec/035-notification-timeline-filters.js | 48 +++++++++++++ ...ilters.js => 123-notification-mentions.js} | 12 ++-- tests/spec/124-home-timeline-filters.js | 29 ++++++++ tests/utils.js | 10 ++- 14 files changed, 356 insertions(+), 20 deletions(-) create mode 100644 src/routes/_components/settings/instance/GenericInstanceSettings.html create mode 100644 src/routes/_components/settings/instance/HomeTimelineFilterSettings.html create mode 100644 src/routes/_components/settings/instance/NotificationFilterSettings.html create mode 100644 src/routes/_static/instanceSettings.js rename tests/spec/{033-notification-filters.js => 033-notification-mentions.js} (70%) create mode 100644 tests/spec/034-home-timeline-filters.js create mode 100644 tests/spec/035-notification-timeline-filters.js rename tests/spec/{123-notification-filters.js => 123-notification-mentions.js} (91%) create mode 100644 tests/spec/124-home-timeline-filters.js diff --git a/src/routes/_components/settings/instance/GenericInstanceSettings.html b/src/routes/_components/settings/instance/GenericInstanceSettings.html new file mode 100644 index 00000000..d7c220e3 --- /dev/null +++ b/src/routes/_components/settings/instance/GenericInstanceSettings.html @@ -0,0 +1,48 @@ +
+
+ {#each options as option, i (option.key) } + {#if i > 0} +
+ {/if} + + + {/each} +
+
+ + diff --git a/src/routes/_components/settings/instance/HomeTimelineFilterSettings.html b/src/routes/_components/settings/instance/HomeTimelineFilterSettings.html new file mode 100644 index 00000000..52f7c9b8 --- /dev/null +++ b/src/routes/_components/settings/instance/HomeTimelineFilterSettings.html @@ -0,0 +1,29 @@ + + diff --git a/src/routes/_components/settings/instance/NotificationFilterSettings.html b/src/routes/_components/settings/instance/NotificationFilterSettings.html new file mode 100644 index 00000000..c4d2cbd3 --- /dev/null +++ b/src/routes/_components/settings/instance/NotificationFilterSettings.html @@ -0,0 +1,50 @@ + + diff --git a/src/routes/_components/timeline/Timeline.html b/src/routes/_components/timeline/Timeline.html index 6fb2d34d..7efd9cb9 100644 --- a/src/routes/_components/timeline/Timeline.html +++ b/src/routes/_components/timeline/Timeline.html @@ -59,6 +59,22 @@ import { observe } from 'svelte-extras' import { createMakeProps } from '../../_actions/createMakeProps' import { showMoreAndScrollToTop } from '../../_actions/showMoreAndScrollToTop' + import { get } from '../../_utils/lodash-lite' + import { + HOME_REBLOGS, + HOME_REPLIES, + NOTIFICATION_REBLOGS, + NOTIFICATION_FOLLOWS, + NOTIFICATION_FAVORITES, + NOTIFICATION_POLLS, + NOTIFICATION_MENTIONS, + FILTER_FAVORITE, + FILTER_FOLLOW, + FILTER_MENTION, + FILTER_POLL, + FILTER_REBLOG, + FILTER_REPLY + } from '../../_static/instanceSettings' export default { oncreate () { @@ -127,9 +143,56 @@ timelineValue !== $firstTimelineItemId && timelineValue ), - itemIds: ({ $timelineItemSummaries }) => ( - // TODO: filter - $timelineItemSummaries && $timelineItemSummaries.map(_ => _.id) + currentInstanceSettings: ({ $currentInstance, $instanceSettings }) => ( + $instanceSettings[$currentInstance] || {} + ), + timelineFilters: ({ currentInstanceSettings, timeline }) => { + if (timeline === 'home') { + return { + [FILTER_REBLOG]: get(currentInstanceSettings, [HOME_REBLOGS], true), + [FILTER_REPLY]: get(currentInstanceSettings, [HOME_REPLIES], true) + } + } else if (timeline === 'notifications') { + return { + [FILTER_REBLOG]: get(currentInstanceSettings, [NOTIFICATION_REBLOGS], true), + [FILTER_FOLLOW]: get(currentInstanceSettings, [NOTIFICATION_FOLLOWS], true), + [FILTER_FAVORITE]: get(currentInstanceSettings, [NOTIFICATION_FAVORITES], true), + [FILTER_MENTION]: get(currentInstanceSettings, [NOTIFICATION_MENTIONS], true), + [FILTER_POLL]: get(currentInstanceSettings, [NOTIFICATION_POLLS], true) + } + } + }, + showReblogs: ({ timelineFilters }) => get(timelineFilters, [FILTER_REBLOG], true), + showReplies: ({ timelineFilters }) => get(timelineFilters, [FILTER_REPLY], true), + showFollows: ({ timelineFilters }) => get(timelineFilters, [FILTER_FOLLOW], true), + showMentions: ({ timelineFilters }) => get(timelineFilters, [FILTER_MENTION], true), + showPolls: ({ timelineFilters }) => get(timelineFilters, [FILTER_POLL], true), + showFavs: ({ timelineFilters }) => get(timelineFilters, [FILTER_FAVORITE], true), + itemIds: ({ + $timelineItemSummaries, showReblogs, showReplies, showFollows, showMentions, + showPolls, showFavs + }) => ( + $timelineItemSummaries && $timelineItemSummaries.filter(item => { + switch (item.type) { + case 'poll': + return showPolls + case 'favourite': + return showFavs + case 'reblog': + return showReblogs + case 'mention': + return showMentions + case 'follow': + return showFollows + } + if (item.reblogId) { + return showReblogs + } else if (item.replyId) { + return showReplies + } else { + return true + } + }).map(_ => _.id) ), itemIdsToAdd: ({ $timelineItemSummariesToAdd }) => ( // TODO: filter diff --git a/src/routes/_pages/settings/instances/[instanceName].html b/src/routes/_pages/settings/instances/[instanceName].html index 71790597..0d3a84c5 100644 --- a/src/routes/_pages/settings/instances/[instanceName].html +++ b/src/routes/_pages/settings/instances/[instanceName].html @@ -4,9 +4,13 @@ {#if verifyCredentials}

Logged in as:

-

Push notifications:

+

Home timeline filters

+ +

Notification filters

+ +

Push notifications

-

Theme:

+

Theme

@@ -23,6 +27,8 @@ import { store } from '../../../_store/store' import SettingsLayout from '../../../_components/settings/SettingsLayout.html' import InstanceUserProfile from '../../../_components/settings/instance/InstanceUserProfile.html' + import HomeTimelineFilterSettings from '../../../_components/settings/instance/HomeTimelineFilterSettings.html' + import NotificationFilterSettings from '../../../_components/settings/instance/NotificationFilterSettings.html' import PushNotificationSettings from '../../../_components/settings/instance/PushNotificationSettings.html' import ThemeSettings from '../../../_components/settings/instance/ThemeSettings.html' import InstanceActions from '../../../_components/settings/instance/InstanceActions.html' @@ -43,7 +49,9 @@ InstanceUserProfile, PushNotificationSettings, ThemeSettings, - InstanceActions + InstanceActions, + HomeTimelineFilterSettings, + NotificationFilterSettings } } - \ No newline at end of file + diff --git a/src/routes/_static/instanceSettings.js b/src/routes/_static/instanceSettings.js new file mode 100644 index 00000000..db45398b --- /dev/null +++ b/src/routes/_static/instanceSettings.js @@ -0,0 +1,15 @@ +export const HOME_REBLOGS = 'homeReblogs' +export const HOME_REPLIES = 'homeReplies' + +export const NOTIFICATION_REBLOGS = 'notificationReblogs' +export const NOTIFICATION_FAVORITES = 'notificationFavs' +export const NOTIFICATION_FOLLOWS = 'notificationFollows' +export const NOTIFICATION_MENTIONS = 'notificationMentions' +export const NOTIFICATION_POLLS = 'notificationPolls' + +export const FILTER_REBLOG = 'reblog' +export const FILTER_REPLY = 'reply' +export const FILTER_MENTION = 'mention' +export const FILTER_FOLLOW = 'follow' +export const FILTER_FAVORITE = 'fav' +export const FILTER_POLL = 'poll' diff --git a/src/routes/_store/mixins/instanceMixins.js b/src/routes/_store/mixins/instanceMixins.js index 67d50120..7016e213 100644 --- a/src/routes/_store/mixins/instanceMixins.js +++ b/src/routes/_store/mixins/instanceMixins.js @@ -1,3 +1,5 @@ +import { get } from '../../_utils/lodash-lite' + export function instanceMixins (Store) { Store.prototype.setComposeData = function (realm, obj) { let { composeData, currentInstance } = this.get() @@ -20,4 +22,18 @@ export function instanceMixins (Store) { } this.set({ composeData }) } + + Store.prototype.getInstanceSetting = function (instanceName, settingName, defaultValue) { + let { instanceSettings } = this.get() + return get(instanceSettings, [instanceName, settingName], defaultValue) + } + + Store.prototype.setInstanceSetting = function (instanceName, settingName, value) { + let { instanceSettings } = this.get() + if (!instanceSettings[instanceName]) { + instanceSettings[instanceName] = {} + } + instanceSettings[instanceName][settingName] = value + this.set({ instanceSettings }) + } } diff --git a/src/routes/_store/store.js b/src/routes/_store/store.js index 940438b0..60e28c3d 100644 --- a/src/routes/_store/store.js +++ b/src/routes/_store/store.js @@ -19,6 +19,7 @@ const persistedState = { largeInlineMedia: false, instanceNameInSearch: '', instanceThemes: {}, + instanceSettings: {}, loggedInInstances: {}, loggedInInstancesInOrder: [], markMediaAsSensitive: false, diff --git a/tests/spec/033-notification-filters.js b/tests/spec/033-notification-mentions.js similarity index 70% rename from tests/spec/033-notification-filters.js rename to tests/spec/033-notification-mentions.js index 3f831f0d..dc00d2e4 100644 --- a/tests/spec/033-notification-filters.js +++ b/tests/spec/033-notification-mentions.js @@ -1,22 +1,22 @@ import { - getUrl, notificationFiltersAll, notificationFiltersMention, + getUrl, notificationsTabAll, notificationsTabMentions, notificationsNavButton, validateTimeline } from '../utils' import { loginAsFoobar } from '../roles' import { notificationsMentions, notifications } from '../fixtures' -fixture`033-notification-filters.js` +fixture`033-notification-mentions.js` .page`http://localhost:4002` -test('Shows notification filters', async t => { +test('Shows notification mentions', async t => { await loginAsFoobar(t) await t .click(notificationsNavButton) .expect(getUrl()).match(/\/notifications$/) - .click(notificationFiltersMention) + .click(notificationsTabMentions) .expect(getUrl()).match(/\/notifications\/mentions$/) await validateTimeline(t, notificationsMentions) - await t.click(notificationFiltersAll) + await t.click(notificationsTabAll) .expect(getUrl()).match(/\/notifications$/) await validateTimeline(t, notifications) }) diff --git a/tests/spec/034-home-timeline-filters.js b/tests/spec/034-home-timeline-filters.js new file mode 100644 index 00000000..3de08bf0 --- /dev/null +++ b/tests/spec/034-home-timeline-filters.js @@ -0,0 +1,23 @@ +import { + validateTimeline, settingsNavButton, instanceSettingHomeReblogs, homeNavButton +} from '../utils' +import { loginAsFoobar } from '../roles' +import { homeTimeline } from '../fixtures' +import { Selector as $ } from 'testcafe' + +fixture`034-home-timeline-filters.js` + .page`http://localhost:4002` + +test('Filters reblogs from home timeline', async t => { + await loginAsFoobar(t) + await t + .click(settingsNavButton) + .click($('a').withText('Instances')) + .click($('a').withText('localhost:3000')) + .click(instanceSettingHomeReblogs) + .expect(instanceSettingHomeReblogs.checked).notOk() + .click(homeNavButton) + await validateTimeline(t, homeTimeline.filter(({ content }) => { + return content !== 'pinned toot 1' + })) +}) diff --git a/tests/spec/035-notification-timeline-filters.js b/tests/spec/035-notification-timeline-filters.js new file mode 100644 index 00000000..f7888c5c --- /dev/null +++ b/tests/spec/035-notification-timeline-filters.js @@ -0,0 +1,48 @@ +import { + validateTimeline, + settingsNavButton, + instanceSettingNotificationReblogs, + notificationsNavButton, + instanceSettingNotificationFavs, + instanceSettingNotificationFollows, + instanceSettingNotificationMentions +} from '../utils' +import { loginAsFoobar } from '../roles' +import { notifications } from '../fixtures' +import { Selector as $ } from 'testcafe' + +fixture`035-notification-timeline-filters.js` + .page`http://localhost:4002` + +function setSettingAndGoToNotifications (t, setting) { + return t.click(settingsNavButton) + .click($('a').withText('Instances')) + .click($('a').withText('localhost:3000')) + .click(setting) + .expect(setting.checked).notOk() + .click(notificationsNavButton) +} + +test('Filters reblogs from notification timeline', async t => { + await loginAsFoobar(t) + await setSettingAndGoToNotifications(t, instanceSettingNotificationReblogs) + await validateTimeline(t, notifications.filter(_ => !_.rebloggedBy)) +}) + +test('Filters favs from notification timeline', async t => { + await loginAsFoobar(t) + await setSettingAndGoToNotifications(t, instanceSettingNotificationFavs) + await validateTimeline(t, notifications.filter(_ => !_.favoritedBy)) +}) + +test('Filters follows from notification timeline', async t => { + await loginAsFoobar(t) + await setSettingAndGoToNotifications(t, instanceSettingNotificationFollows) + await validateTimeline(t, notifications.filter(_ => !_.followedBy)) +}) + +test('Filters mentions from notification timeline', async t => { + await loginAsFoobar(t) + await setSettingAndGoToNotifications(t, instanceSettingNotificationMentions) + await validateTimeline(t, notifications.filter(_ => !_.content)) +}) diff --git a/tests/spec/123-notification-filters.js b/tests/spec/123-notification-mentions.js similarity index 91% rename from tests/spec/123-notification-filters.js rename to tests/spec/123-notification-mentions.js index ba9304b8..1bf8d5c5 100644 --- a/tests/spec/123-notification-filters.js +++ b/tests/spec/123-notification-mentions.js @@ -1,12 +1,12 @@ import { getNthStatusContent, - getUrl, notificationFiltersAll, notificationFiltersMention, + getUrl, notificationsTabAll, notificationsTabMentions, notificationsNavButton, sleep } from '../utils' import { loginAsFoobar } from '../roles' import { favoriteStatusAs, postAs } from '../serverActions' -fixture`123-notification-filters.js` +fixture`123-notification-mentions.js` .page`http://localhost:4002` // maybe in the "mentions" view it should prevent the notification icon from showing (1), (2) etc @@ -18,7 +18,7 @@ test('Handles incoming notifications that are mentions', async t => { await t .click(notificationsNavButton) .expect(getUrl()).match(/\/notifications$/) - .click(notificationFiltersMention) + .click(notificationsTabMentions) .expect(getUrl()).match(/\/notifications\/mentions$/) await sleep(2000) await postAs('admin', 'hey @foobar I am mentioning you') @@ -27,7 +27,7 @@ test('Handles incoming notifications that are mentions', async t => { timeout }) .expect(getNthStatusContent(1).innerText).contains('hey @foobar I am mentioning you') - .click(notificationFiltersAll) + .click(notificationsTabAll) .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)', { timeout }) }) @@ -39,7 +39,7 @@ test('Handles incoming notifications that are not mentions', async t => { await t .click(notificationsNavButton) .expect(getUrl()).match(/\/notifications$/) - .click(notificationFiltersMention) + .click(notificationsTabMentions) .expect(getUrl()).match(/\/notifications\/mentions$/) await sleep(2000) await postAs('admin', 'woot I am mentioning you again @foobar') @@ -57,7 +57,7 @@ test('Handles incoming notifications that are not mentions', async t => { await sleep(2000) await t .expect(getNthStatusContent(1).innerText).contains('woot I am mentioning you again @foobar') - .click(notificationFiltersAll) + .click(notificationsTabAll) .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)', { timeout }) await t .expect(getNthStatusContent(1).innerText).contains('this is a post that I hope somebody will favorite') diff --git a/tests/spec/124-home-timeline-filters.js b/tests/spec/124-home-timeline-filters.js new file mode 100644 index 00000000..63234d12 --- /dev/null +++ b/tests/spec/124-home-timeline-filters.js @@ -0,0 +1,29 @@ +import { + settingsNavButton, instanceSettingHomeReblogs, homeNavButton, sleep, getNthStatusContent +} from '../utils' +import { loginAsFoobar } from '../roles' +import { Selector as $ } from 'testcafe' +import { postAs, reblogStatusAs } from '../serverActions' + +fixture`124-home-timeline-filters.js` + .page`http://localhost:4002` + +test('Filters favs from home timeline', async t => { + await postAs('foobar', 'Nobody should boost this') + await sleep(1000) + let { id: statusId } = await postAs('quux', 'I hope someone cool boosts this') + await reblogStatusAs('admin', statusId) + await sleep(2000) + await loginAsFoobar(t) + await t + .expect(getNthStatusContent(1).innerText).contains('I hope someone cool boosts this') + .expect(getNthStatusContent(2).innerText).contains('Nobody should boost this') + .click(settingsNavButton) + .click($('a').withText('Instances')) + .click($('a').withText('localhost:3000')) + .click(instanceSettingHomeReblogs) + .expect(instanceSettingHomeReblogs.checked).notOk() + .click(homeNavButton) + await t + .expect(getNthStatusContent(1).innerText).contains('Nobody should boost this') +}) diff --git a/tests/utils.js b/tests/utils.js index d5962112..601c90e4 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -64,8 +64,14 @@ export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-c export const accountProfileFilterStatusesAndReplies = $('.account-profile-filters li:nth-child(2)') export const accountProfileFilterMedia = $('.account-profile-filters li:nth-child(3)') -export const notificationFiltersAll = $('.notification-filters li:nth-child(1)') -export const notificationFiltersMention = $('.notification-filters li:nth-child(2)') +export const notificationsTabAll = $('.notification-filters li:nth-child(1)') +export const notificationsTabMentions = $('.notification-filters li:nth-child(2)') + +export const instanceSettingHomeReblogs = $('#instance-option-homeReblogs') +export const instanceSettingNotificationFollows = $('#instance-option-notificationFollows') +export const instanceSettingNotificationFavs = $('#instance-option-notificationFavs') +export const instanceSettingNotificationReblogs = $('#instance-option-notificationReblogs') +export const instanceSettingNotificationMentions = $('#instance-option-notificationMentions') export function getComposeModalNthMediaAltInput (n) { return $(`.modal-dialog .compose-media:nth-child(${n}) .compose-media-alt input`)