From 24d7f9917b1a4a147b92bd31ec65511c1d528c6f Mon Sep 17 00:00:00 2001 From: jasper Date: Mon, 18 Feb 2019 11:39:35 -0800 Subject: Remove posts by blocking or following --- src/modules/statuses.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 46117fd7..57095f37 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -307,9 +307,32 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot }) } +const removeStatus = (state, { timeline, userId }) => { + const timelineObject = state.timelines[timeline] + if (userId) { + remove(timelineObject.statuses, { user: { id: userId } }) + remove(timelineObject.visibleStatuses, { user: { id: userId } }) + const statusesObject = timelineObject.statusesObject + const visibleStatusesObject = timelineObject.visibleStatusesObject + each(statusesObject, (status, key) => { + if (status.user.id === userId) { + delete statusesObject[key] + } + }) + each(visibleStatusesObject, (status, key) => { + if (status.user.id === userId) { + delete visibleStatusesObject[key] + } + }) + timelineObject.minVisibleId = (last(timeline.visibleStatuses) || {}).id + timelineObject.maxId = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 + } +} + export const mutations = { addNewStatuses, addNewNotifications, + removeStatus, showNewStatuses (state, { timeline }) { const oldTimeline = (state.timelines[timeline]) -- cgit v1.2.3-70-g09d2 From 4e79300232288fd9b7c4456e8b461f7c1cbac6fa Mon Sep 17 00:00:00 2001 From: jasper Date: Mon, 25 Feb 2019 14:35:47 -0800 Subject: Remove posts by blocking or unfollowing --- .../user_card_content/user_card_content.js | 51 ---------------------- src/modules/statuses.js | 18 ++------ src/services/api/api.service.js | 6 +-- 3 files changed, 5 insertions(+), 70 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 8bc01941..d2a97a84 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,5 +1,4 @@ import UserAvatar from '../user_avatar/user_avatar.vue' -import apiService from '../../services/api/api.service.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -99,25 +98,6 @@ export default { requestFollow(this.user, store).then(({sent}) => { this.followRequestInProgress = false this.followRequestSent = sent - - const rootState = store.rootState || store.state - const credentials = store.state.users.currentUser.credentials - const timelineData = rootState.statuses.timelines['friends'] - apiService.fetchTimeline({ - store, - credentials, - userId: this.user.id, - timeline: 'user', - between: true, - until: timelineData.maxId, - since: timelineData.minVisibleId - }).then((statuses) => { - store.dispatch('addNewStatuses', { - timeline: 'friends', - statuses, - showImmediately: true - }) - }, () => store.dispatch('setError', { value: true })) }) }, unfollowUser () { @@ -145,37 +125,6 @@ export default { store.state.api.backendInteractor.unblockUser(this.user.id) .then((unblockedUser) => { store.commit('addNewUsers', [unblockedUser]) - - const rootState = store.rootState || store.state - const credentials = store.state.users.currentUser.credentials - const timelineData = rootState.statuses.timelines['friends'] - apiService.fetchTimeline({ - store, - credentials, - userId: this.user.id, - timeline: 'user', - between: true, - until: timelineData.maxId, - since: timelineData.minVisibleId - }).then((statuses) => { - store.dispatch('addNewStatuses', { - timeline: 'public', - statuses, - showImmediately: true - }) - store.dispatch('addNewStatuses', { - timeline: 'publicAndExternal', - statuses, - showImmediately: true - }) - if (this.user.follows_you) { - store.dispatch('addNewStatuses', { - timeline: 'friends', - statuses, - showImmediately: true - }) - } - }, () => store.dispatch('setError', { value: true })) }) }, toggleMute () { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 57095f37..d144702e 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -312,20 +312,8 @@ const removeStatus = (state, { timeline, userId }) => { if (userId) { remove(timelineObject.statuses, { user: { id: userId } }) remove(timelineObject.visibleStatuses, { user: { id: userId } }) - const statusesObject = timelineObject.statusesObject - const visibleStatusesObject = timelineObject.visibleStatusesObject - each(statusesObject, (status, key) => { - if (status.user.id === userId) { - delete statusesObject[key] - } - }) - each(visibleStatusesObject, (status, key) => { - if (status.user.id === userId) { - delete visibleStatusesObject[key] - } - }) - timelineObject.minVisibleId = (last(timeline.visibleStatuses) || {}).id - timelineObject.maxId = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 + timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0 + timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0 } } diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index d35c72b1..c5fdcb72 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -329,7 +329,7 @@ const setUserMute = ({id, credentials, muted = true}) => { }) } -const fetchTimeline = ({timeline, credentials, since = false, until = false, between = false, count = 20, userId = false, tag = false}) => { +const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, friends: FRIENDS_TIMELINE_URL, @@ -362,9 +362,7 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, bet if (timeline === 'media') { params.push(['only_media', 1]) } - if (!between) { - params.push(['count', count]) - } + params.push(['count', 20]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` -- cgit v1.2.3-70-g09d2 From 9aec49bacb033cdcc118f821706c419d384bf5fe Mon Sep 17 00:00:00 2001 From: jasper Date: Wed, 27 Feb 2019 17:45:08 -0800 Subject: Fetch activites by last id --- src/modules/statuses.js | 19 ++++++++++--------- .../timeline_fetcher/timeline_fetcher.service.js | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 826b544c..4002f282 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -10,6 +10,7 @@ const emptyTl = (userId = 0) => ({ visibleStatusesObject: {}, newStatusCount: 0, maxId: 0, + minId: 0, minVisibleId: 0, loading: false, followers: [], @@ -117,11 +118,16 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us const timelineObject = state.timelines[timeline] const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 - const older = timeline && maxNew < timelineObject.maxId + const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0 + const newer = timeline && maxNew > timelineObject.maxId && statuses.length > 0 + const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0 - if (timeline && !noIdUpdate && statuses.length > 0 && !older) { + if (!noIdUpdate && newer) { timelineObject.maxId = maxNew } + if (!noIdUpdate && older) { + timelineObject.minId = minNew + } // This makes sure that user timeline won't get data meant for other // user. I.e. opening different user profiles makes request which could @@ -255,13 +261,8 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us processor(status) }) - // Keep the visible statuses sorted - if (timeline) { - sortTimeline(timelineObject) - if ((older || timelineObject.minVisibleId <= 0) && statuses.length > 0) { - timelineObject.minVisibleId = minBy(statuses, 'id').id - } - } + // Keep the visible statuses sorted + if (timeline) sortTimeline(timelineObject) } const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => { diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 64f8f468..6f99616f 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -21,7 +21,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false const timelineData = rootState.statuses.timelines[camelCase(timeline)] if (older) { - args['until'] = until || timelineData.minVisibleId + args['until'] = until || timelineData.minId } else { args['since'] = timelineData.maxId } -- cgit v1.2.3-70-g09d2 From ba2e05bc63e0b36d5a126b422c9f87a7ace43305 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 28 Feb 2019 14:03:44 -0500 Subject: #392: stale data served to new user account --- src/components/notifications/notifications.js | 3 ++- src/modules/statuses.js | 15 ++++++++++++--- src/modules/users.js | 4 ++++ 3 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 5e95631a..9fc5e38a 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -11,7 +11,8 @@ const Notifications = { const store = this.$store const credentials = store.state.users.currentUser.credentials - notificationsFetcher.startFetching({ store, credentials }) + const fetcherId = notificationsFetcher.startFetching({ store, credentials }) + this.$store.commit('setNotificationFetcher', { fetcherId }) }, data () { return { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 826b544c..2d1313a5 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, last, isArray, cloneDeep } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -29,7 +29,8 @@ export const defaultState = { data: [], idStore: {}, loading: false, - error: false + error: false, + fetcherId: null }, favorites: new Set(), error: false, @@ -321,6 +322,14 @@ export const mutations = { oldTimeline.visibleStatusesObject = {} each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, + setNotificationFetcher (state, { fetcherId }) { + state.notifications.fetcherId = fetcherId + }, + resetStatuses (state) { + Object.keys(state).forEach(key => { + state[key] = cloneDeep(defaultState[key]) + }) + }, clearTimeline (state, { timeline }) { state.timelines[timeline] = emptyTl(state.timelines[timeline].userId) }, @@ -371,7 +380,7 @@ export const mutations = { } const statuses = { - state: defaultState, + state: cloneDeep(defaultState), actions: { addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) diff --git a/src/modules/users.js b/src/modules/users.js index 77df7168..84fe039b 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -290,6 +290,10 @@ const users = { store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) + if (store.rootState.statuses.notifications.fetcherId) { + window.clearInterval(store.rootState.statuses.notifications.fetcherId) + } + store.commit('resetStatuses') }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { -- cgit v1.2.3-70-g09d2 From f5adb62e2e6efdf6bd9a9c7a8c2677c618de53f9 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 28 Feb 2019 14:27:47 -0500 Subject: #392: update defaultState into a function --- src/modules/statuses.js | 11 ++++++----- test/unit/specs/modules/statuses.spec.js | 27 +++++++++++++-------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2d1313a5..2af23a9d 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray, cloneDeep } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -18,7 +18,7 @@ const emptyTl = (userId = 0) => ({ flushMarker: 0 }) -export const defaultState = { +export const defaultState = () => ({ allStatuses: [], allStatusesObject: {}, maxId: 0, @@ -45,7 +45,7 @@ export const defaultState = { tag: emptyTl(), dms: emptyTl() } -} +}) export const prepareStatus = (status) => { // Set deleted flag @@ -326,8 +326,9 @@ export const mutations = { state.notifications.fetcherId = fetcherId }, resetStatuses (state) { + const emptyState = defaultState() Object.keys(state).forEach(key => { - state[key] = cloneDeep(defaultState[key]) + state[key] = emptyState[key] }) }, clearTimeline (state, { timeline }) { @@ -380,7 +381,7 @@ export const mutations = { } const statuses = { - state: cloneDeep(defaultState), + state: defaultState(), actions: { addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 01d2ce06..864b798d 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -1,4 +1,3 @@ -import { cloneDeep } from 'lodash' import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js' // eslint-disable-next-line camelcase @@ -24,7 +23,7 @@ describe('Statuses.prepareStatus', () => { describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -36,7 +35,7 @@ describe('The Statuses module', () => { }) it('counts the status as new if it has not been seen on this timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -54,7 +53,7 @@ describe('The Statuses module', () => { }) it('add the statuses to allStatuses if no timeline is given', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status] }) @@ -66,7 +65,7 @@ describe('The Statuses module', () => { }) it('adds the status to allStatuses and to the given timeline, directly visible', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -78,7 +77,7 @@ describe('The Statuses module', () => { }) it('removes statuses by tag on deletion', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const otherStatus = makeMockStatus({id: '3'}) status.uri = 'xxx' @@ -96,7 +95,7 @@ describe('The Statuses module', () => { }) it('does not update the maxId when the noIdUpdate flag is set', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const secondStatus = makeMockStatus({id: '2'}) @@ -110,7 +109,7 @@ describe('The Statuses module', () => { }) it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const nonVisibleStatus = makeMockStatus({id: '1'}) const status = makeMockStatus({id: '3'}) const statusTwo = makeMockStatus({id: '2'}) @@ -130,7 +129,7 @@ describe('The Statuses module', () => { }) it('splits retweets from their status and links them', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const retweet = makeMockStatus({id: '2', type: 'retweet'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) @@ -155,7 +154,7 @@ describe('The Statuses module', () => { }) it('replaces existing statuses with the same id', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) @@ -172,7 +171,7 @@ describe('The Statuses module', () => { }) it('replaces existing statuses with the same id, coming from a retweet', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) const retweet = makeMockStatus({id: '2', type: 'retweet'}) @@ -193,7 +192,7 @@ describe('The Statuses module', () => { }) it('handles favorite actions', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const favorite = { @@ -241,7 +240,7 @@ describe('The Statuses module', () => { }) it('keeps userId when clearing user timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() state.timelines.user.userId = 123 mutations.clearTimeline(state, { timeline: 'user' }) @@ -252,7 +251,7 @@ describe('The Statuses module', () => { describe('notifications', () => { it('removes a notification when the notice gets removed', () => { const user = { id: '1' } - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const otherStatus = makeMockStatus({id: '3'}) const mentionedStatus = makeMockStatus({id: '2'}) -- cgit v1.2.3-70-g09d2 From bbe1821be7c098f3a23bd0dd54b4ec89a159e9c1 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 28 Feb 2019 14:44:43 -0500 Subject: #392: update object.entries --- src/modules/statuses.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2af23a9d..028053cf 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -327,8 +327,8 @@ export const mutations = { }, resetStatuses (state) { const emptyState = defaultState() - Object.keys(state).forEach(key => { - state[key] = emptyState[key] + Object.entries(emptyState).forEach(([key, value]) => { + state[key] = value }) }, clearTimeline (state, { timeline }) { -- cgit v1.2.3-70-g09d2 From f3f9fbe3027bf5284adc05387e89d904884490b2 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 1 Mar 2019 11:59:50 -0500 Subject: #392: clean up notification stopping section --- src/modules/statuses.js | 6 ++++++ src/modules/users.js | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 028053cf..77a8ec72 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -401,6 +401,12 @@ const statuses = { setNotificationsSilence ({ rootState, commit }, { value }) { commit('setNotificationsSilence', { value }) }, + stopFetchingNotifications ({ rootState, commit }) { + if (rootState.statuses.notifications.fetcherId) { + window.clearInterval(rootState.statuses.notifications.fetcherId) + } + commit('setNotificationFetcher', { fetcherId: null }) + }, deleteStatus ({ rootState, commit }, status) { commit('setDeleted', { status }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) diff --git a/src/modules/users.js b/src/modules/users.js index 84fe039b..0e1dd166 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -290,9 +290,7 @@ const users = { store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) - if (store.rootState.statuses.notifications.fetcherId) { - window.clearInterval(store.rootState.statuses.notifications.fetcherId) - } + store.dispatch('stopFetchingNotifications') store.commit('resetStatuses') }, loginUser (store, accessToken) { -- cgit v1.2.3-70-g09d2 From 81e89fed3d6cc247346611e1b58aae9d87667934 Mon Sep 17 00:00:00 2001 From: jasper Date: Fri, 1 Mar 2019 12:53:24 -0800 Subject: Fetch activites by last id --- src/modules/statuses.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 4002f282..96a3549d 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -262,7 +262,9 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us }) // Keep the visible statuses sorted - if (timeline) sortTimeline(timelineObject) + if (timeline) { + sortTimeline(timelineObject) + } } const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => { -- cgit v1.2.3-70-g09d2 From ee49409049430dedf042ddbeb73898f605664cd2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 8 Mar 2019 00:35:30 +0200 Subject: Partially transitioned user data to MastoAPI. Added support for fetching relationship data. Upgraded code to be more resilient to nulls caused by missing data in either APIs --- src/components/user_card/user_card.js | 3 ++ src/components/user_profile/user_profile.js | 3 ++ src/modules/statuses.js | 6 ++-- src/modules/users.js | 32 ++++++++++++++++++---- src/services/api/api.service.js | 20 ++++++++++++-- .../backend_interactor_service.js | 5 ++++ 6 files changed, 59 insertions(+), 10 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 80d15a27..43a77f45 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -15,6 +15,9 @@ export default { betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, + created () { + this.$store.dispatch('fetchUserRelationship', this.user.id) + }, computed: { classes () { return [{ diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 54126514..345e7035 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -43,6 +43,7 @@ const UserProfile = { this.startFetchFavorites() if (!this.user.id) { this.$store.dispatch('fetchUser', this.fetchBy) + .then(() => this.$store.dispatch('fetchUserRelationship', this.fetchBy)) .catch((reason) => { const errorMessage = get(reason, 'error.error') if (errorMessage === 'No user with such user_id') { // Known error @@ -53,6 +54,8 @@ const UserProfile = { this.error = this.$t('user_profile.profile_loading_error') } }) + } else if (typeof this.user.following === 'undefined' || this.user.following === null) { + this.$store.dispatch('fetchUserRelationship', this.fetchBy) } }, destroyed () { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7571b62a..2b0215f0 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -72,7 +72,9 @@ const mergeOrAdd = (arr, obj, item) => { if (oldItem) { // We already have this, so only merge the new info. - merge(oldItem, item) + // We ignore null values to avoid overwriting existing properties with missing data + // we also skip 'used' because that is handled by users module + merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user')) // Reactivity fix. oldItem.attachments.splice(oldItem.attachments.length) return {item: oldItem, new: false} diff --git a/src/modules/users.js b/src/modules/users.js index 4159964c..a81ed964 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,5 +1,5 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' -import { compact, map, each, merge, find } from 'lodash' +import { compact, map, each, merge, find, omitBy } from 'lodash' import { set } from 'vue' import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' import oauthApi from '../services/new_api/oauth' @@ -11,7 +11,7 @@ export const mergeOrAdd = (arr, obj, item) => { const oldItem = obj[item.id] if (oldItem) { // We already have this, so only merge the new info. - merge(oldItem, item) + merge(oldItem, omitBy(item, _ => _ === null)) return { item: oldItem, new: false } } else { // This is a new item, prepare it @@ -39,7 +39,7 @@ export const mutations = { }, setCurrentUser (state, user) { state.lastLoginName = user.screen_name - state.currentUser = merge(state.currentUser || {}, user) + state.currentUser = merge(state.currentUser || {}, omitBy(user, _ => _ === null)) }, clearCurrentUser (state) { state.currentUser = false @@ -91,6 +91,16 @@ export const mutations = { addNewUsers (state, users) { each(users, (user) => mergeOrAdd(state.users, state.usersObject, user)) }, + updateUserRelationship (state, relationships) { + relationships.forEach((relationship) => { + const user = state.usersObject[relationship.id] + + user.follows_you = relationship.followed_by + user.following = relationship.following + user.muted = relationship.muting + user.statusnet_blocking = relationship.blocking + }) + }, saveBlocks (state, blockIds) { state.currentUser.blockIds = blockIds }, @@ -98,11 +108,17 @@ export const mutations = { state.currentUser.muteIds = muteIds }, setUserForStatus (state, status) { - status.user = state.usersObject[status.user.id] + // Not setting it again since it's already reactive if it has getters + if (!Object.getOwnPropertyDescriptor(status.user, 'id').get) { + status.user = state.usersObject[status.user.id] + } }, setUserForNotification (state, notification) { - notification.action.user = state.usersObject[notification.action.user.id] - notification.from_profile = state.usersObject[notification.action.user.id] + // Not setting it again since it's already reactive if it has getters + if (!Object.getOwnPropertyDescriptor(notification.action.user, 'id').get) { + notification.action.user = state.usersObject[notification.action.user.id] + notification.from_profile = state.usersObject[notification.action.user.id] + } }, setColor (state, { user: { id }, highlighted }) { const user = state.usersObject[id] @@ -149,6 +165,10 @@ const users = { return store.rootState.api.backendInteractor.fetchUser({ id }) .then((user) => store.commit('addNewUsers', [user])) }, + fetchUserRelationship (store, id) { + return store.rootState.api.backendInteractor.fetchUserRelationship({ id }) + .then((relationships) => store.commit('updateUserRelationship', relationships)) + }, fetchBlocks (store) { return store.rootState.api.backendInteractor.fetchBlocks() .then((blocks) => { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026..d512b120 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -33,7 +33,6 @@ const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKING_URL = '/api/blocks/create.json' const UNBLOCKING_URL = '/api/blocks/destroy.json' -const USER_URL = '/api/users/show.json' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' @@ -43,6 +42,8 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_USER_URL = '/api/v1/accounts/' +const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -243,7 +244,7 @@ const denyUser = ({id, credentials}) => { } const fetchUser = ({id, credentials}) => { - let url = `${USER_URL}?user_id=${id}` + let url = `${MASTODON_USER_URL}/${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((response) => { return new Promise((resolve, reject) => response.json() @@ -257,6 +258,20 @@ const fetchUser = ({id, credentials}) => { .then((data) => parseUser(data)) } +const fetchUserRelationship = ({id, credentials}) => { + let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` + return fetch(url, { headers: authHeaders(credentials) }) + .then((response) => { + return new Promise((resolve, reject) => response.json() + .then((json) => { + if (!response.ok) { + return reject(new StatusCodeError(response.status, json, { url }, response)) + } + return resolve(json) + })) + }) +} + const fetchFriends = ({id, page, credentials}) => { let url = `${FRIENDS_URL}?user_id=${id}` if (page) { @@ -588,6 +603,7 @@ const apiService = { blockUser, unblockUser, fetchUser, + fetchUserRelationship, favorite, unfavorite, retweet, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 7e972d7b..cbd0b733 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -30,6 +30,10 @@ const backendInteractorService = (credentials) => { return apiService.fetchUser({id, credentials}) } + const fetchUserRelationship = ({id}) => { + return apiService.fetchUserRelationship({id, credentials}) + } + const followUser = (id) => { return apiService.followUser({credentials, id}) } @@ -92,6 +96,7 @@ const backendInteractorService = (credentials) => { blockUser, unblockUser, fetchUser, + fetchUserRelationship, fetchAllFollowing, verifyCredentials: apiService.verifyCredentials, startFetching, -- cgit v1.2.3-70-g09d2 From 4f3a220487c3c8b3596e5a8de7b65cc7c4f0c981 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 8 Mar 2019 22:40:57 +0200 Subject: Since BE doesn't support fetching user by screen name over MastoAPI we'll gonna just fetching it over QvitterAPI real quick :DDDDDDDDD --- src/components/block_card/block_card.js | 2 +- src/components/mute_card/mute_card.js | 2 +- src/components/user_profile/user_profile.js | 63 +++++++++------------- src/components/user_profile/user_profile.vue | 4 +- src/modules/statuses.js | 1 + src/modules/users.js | 12 ++--- src/services/api/api.service.js | 23 ++++++-- .../backend_interactor_service.js | 5 ++ test/unit/specs/components/user_profile.spec.js | 3 +- test/unit/specs/modules/users.spec.js | 6 +-- 10 files changed, 65 insertions(+), 56 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js index 11fa27b4..c459ff1b 100644 --- a/src/components/block_card/block_card.js +++ b/src/components/block_card/block_card.js @@ -9,7 +9,7 @@ const BlockCard = { }, computed: { user () { - return this.$store.getters.userById(this.userId) + return this.$store.getters.findUser(this.userId) }, blocked () { return this.user.statusnet_blocking diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js index 5dd0a9e5..65c9cfb5 100644 --- a/src/components/mute_card/mute_card.js +++ b/src/components/mute_card/mute_card.js @@ -9,7 +9,7 @@ const MuteCard = { }, computed: { user () { - return this.$store.getters.userById(this.userId) + return this.$store.getters.findUser(this.userId) }, muted () { return this.user.muted diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 345e7035..4f920ae2 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -9,7 +9,7 @@ import withList from '../../hocs/with_list/with_list' const FollowerList = compose( withLoadMore({ fetch: (props, $store) => $store.dispatch('addFollowers', props.userId), - select: (props, $store) => get($store.getters.userById(props.userId), 'followers', []), + select: (props, $store) => get($store.getters.findUser(props.userId), 'followers', []), destory: (props, $store) => $store.dispatch('clearFollowers', props.userId), childPropName: 'entries', additionalPropNames: ['userId'] @@ -20,7 +20,7 @@ const FollowerList = compose( const FriendList = compose( withLoadMore({ fetch: (props, $store) => $store.dispatch('addFriends', props.userId), - select: (props, $store) => get($store.getters.userById(props.userId), 'friends', []), + select: (props, $store) => get($store.getters.findUser(props.userId), 'friends', []), destory: (props, $store) => $store.dispatch('clearFriends', props.userId), childPropName: 'entries', additionalPropNames: ['userId'] @@ -31,19 +31,22 @@ const FriendList = compose( const UserProfile = { data () { return { - error: false + error: false, + fetchedUserId: null } }, created () { - this.$store.commit('clearTimeline', { timeline: 'user' }) - this.$store.commit('clearTimeline', { timeline: 'favorites' }) - this.$store.commit('clearTimeline', { timeline: 'media' }) - this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy }) - this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy }) - this.startFetchFavorites() if (!this.user.id) { - this.$store.dispatch('fetchUser', this.fetchBy) - .then(() => this.$store.dispatch('fetchUserRelationship', this.fetchBy)) + let fetchPromise + if (this.userId) { + fetchPromise = this.$store.dispatch('fetchUser', this.userId) + } else { + fetchPromise = this.$store.dispatch('fetchUserByScreenName', this.userName) + .then(userId => { + this.fetchedUserId = userId + }) + } + fetchPromise .catch((reason) => { const errorMessage = get(reason, 'error.error') if (errorMessage === 'No user with such user_id') { // Known error @@ -54,8 +57,7 @@ const UserProfile = { this.error = this.$t('user_profile.profile_loading_error') } }) - } else if (typeof this.user.following === 'undefined' || this.user.following === null) { - this.$store.dispatch('fetchUserRelationship', this.fetchBy) + .then(() => this.startUp()) } }, destroyed () { @@ -72,7 +74,7 @@ const UserProfile = { return this.$store.state.statuses.timelines.media }, userId () { - return this.$route.params.id || this.user.id + return this.$route.params.id || this.user.id || this.fetchedUserId }, userName () { return this.$route.params.name || this.user.screen_name @@ -82,10 +84,8 @@ const UserProfile = { this.userId === this.$store.state.users.currentUser.id }, userInStore () { - if (this.isExternal) { - return this.$store.getters.userById(this.userId) - } - return this.$store.getters.userByName(this.userName) + const routeParams = this.$route.params + return this.$store.getters.findUser(routeParams.name || routeParams.iid) }, user () { if (this.timeline.statuses[0]) { @@ -96,9 +96,6 @@ const UserProfile = { } return {} }, - fetchBy () { - return this.isExternal ? this.userId : this.userName - }, isExternal () { return this.$route.name === 'external-user-profile' }, @@ -112,13 +109,13 @@ const UserProfile = { methods: { startFetchFavorites () { if (this.isUs) { - this.$store.dispatch('startFetching', { timeline: 'favorites', userId: this.fetchBy }) + this.$store.dispatch('startFetching', { timeline: 'favorites', userId: this.userId }) } }, startUp () { - this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy }) - this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy }) - + this.$store.dispatch('fetchUserRelationship', this.userId) + this.$store.dispatch('startFetching', { timeline: 'user', userId: this.userId }) + this.$store.dispatch('startFetching', { timeline: 'media', userId: this.userId }) this.startFetchFavorites() }, cleanUp () { @@ -131,19 +128,11 @@ const UserProfile = { } }, watch: { - userName () { - if (this.isExternal) { - return - } - this.cleanUp() - this.startUp() - }, - userId () { - if (!this.isExternal) { - return + userId (newVal, oldVal) { + if (newVal) { + this.cleanUp() + this.startUp() } - this.cleanUp() - this.startUp() }, $route () { this.$refs.tabSwitcher.activateTab(0)() diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 7d4a8b1f..d449eb85 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -11,7 +11,7 @@ :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" - :user-id="fetchBy" + :user-id="userId" />
@@ -25,7 +25,7 @@ :embedded="true" :title="$t('user_card.media')" timeline-name="media" :timeline="media" - :user-id="fetchBy" + :user-id="userId" /> id => - state.users.find(user => user.id === id), - userByName: state => name => - state.users.find(user => user.screen_name && - (user.screen_name.toLowerCase() === name.toLowerCase()) - ) + findUser: state => query => state.usersObject[query] } export const defaultState = { @@ -165,6 +160,11 @@ const users = { return store.rootState.api.backendInteractor.fetchUser({ id }) .then((user) => store.commit('addNewUsers', [user])) }, + fetchUserByScreenName (store, screenName) { + return store.rootState.api.backendInteractor.figureOutUserId({ screenName }) + .then((qvitterUserData) => store.rootState.api.backendInteractor.fetchUser({ id: qvitterUserData.id })) + .then((user) => store.commit('addNewUsers', [user]) || user.id) + }, fetchUserRelationship (store, id) { return store.rootState.api.backendInteractor.fetchUserRelationship({ id }) .then((relationships) => store.commit('updateUserRelationship', relationships)) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 744c2f64..5a0aa2de 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -32,6 +32,7 @@ const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKING_URL = '/api/blocks/create.json' const UNBLOCKING_URL = '/api/blocks/destroy.json' +const USER_URL = '/api/users/show.json' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' @@ -41,7 +42,7 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' -const MASTODON_USER_URL = '/api/v1/accounts/' +const MASTODON_USER_URL = '/api/v1/accounts' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` @@ -272,6 +273,22 @@ const fetchUserRelationship = ({id, credentials}) => { }) } +// TODO remove once MastoAPI supports screen_name in fetchUser one +const figureOutUserId = ({screenName, credentials}) => { + let url = `${USER_URL}/?user_id=${screenName}` + return fetch(url, { headers: authHeaders(credentials) }) + .then((response) => { + return new Promise((resolve, reject) => response.json() + .then((json) => { + if (!response.ok) { + return reject(new StatusCodeError(response.status, json, { url }, response)) + } + return resolve(json) + })) + }) + .then((data) => parseUser(data)) +} + const fetchFriends = ({id, page, credentials}) => { let url = `${FRIENDS_URL}?user_id=${id}` if (page) { @@ -382,9 +399,6 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use if (until) { params.push(['max_id', until]) } - if (userId) { - params.push(['user_id', userId]) - } if (tag) { url += `/${tag}.json` } @@ -608,6 +622,7 @@ const apiService = { unblockUser, fetchUser, fetchUserRelationship, + figureOutUserId, favorite, unfavorite, retweet, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index cbd0b733..48689167 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -26,6 +26,10 @@ const backendInteractorService = (credentials) => { return apiService.fetchAllFollowing({username, credentials}) } + const figureOutUserId = ({screenName}) => { + return apiService.figureOutUserId({screenName, credentials}) + } + const fetchUser = ({id}) => { return apiService.fetchUser({id, credentials}) } @@ -95,6 +99,7 @@ const backendInteractorService = (credentials) => { unfollowUser, blockUser, unblockUser, + figureOutUserId, fetchUser, fetchUserRelationship, fetchAllFollowing, diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js index 41fd9cd0..1524c4eb 100644 --- a/test/unit/specs/components/user_profile.spec.js +++ b/test/unit/specs/components/user_profile.spec.js @@ -13,8 +13,7 @@ const mutations = { } const testGetters = { - userByName: state => getters.userByName(state.users), - userById: state => getters.userById(state.users) + findUser: state => getters.findUser(state.users) } const localUser = { diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js index 4d49ee24..dae7e580 100644 --- a/test/unit/specs/modules/users.spec.js +++ b/test/unit/specs/modules/users.spec.js @@ -43,7 +43,7 @@ describe('The users module', () => { } const name = 'Guy' const expected = { screen_name: 'Guy', id: '1' } - expect(getters.userByName(state)(name)).to.eql(expected) + expect(getters.findUser(state)(name)).to.eql(expected) }) it('returns user with matching screen_name with different case', () => { @@ -54,7 +54,7 @@ describe('The users module', () => { } const name = 'Guy' const expected = { screen_name: 'guy', id: '1' } - expect(getters.userByName(state)(name)).to.eql(expected) + expect(getters.findUser(state)(name)).to.eql(expected) }) }) @@ -67,7 +67,7 @@ describe('The users module', () => { } const id = '1' const expected = { screen_name: 'Guy', id: '1' } - expect(getters.userById(state)(id)).to.eql(expected) + expect(getters.findUser(state)(id)).to.eql(expected) }) }) }) -- cgit v1.2.3-70-g09d2 From fe624f6114220320e1528981af83fb7ab39c0e67 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sat, 9 Mar 2019 01:34:15 +0200 Subject: fix reply-to marker, also whoops console log --- src/components/status/status.js | 8 ++++---- src/modules/statuses.js | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 9e18fe15..43572fe8 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -145,11 +145,11 @@ const Status = { return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id) }, replyToName () { - const user = this.$store.state.users.usersObject[this.status.in_reply_to_user_id] - if (user) { - return user.screen_name - } else { + const user = this.$store.getters.findUser(this.status.in_reply_to_user_id) + if (this.status.in_reply_to_screen_name) { return this.status.in_reply_to_screen_name + } else { + return user.screen_name } }, hideReply () { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 4ee75d48..2b0215f0 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -135,7 +135,6 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us // This makes sure that user timeline won't get data meant for other // user. I.e. opening different user profiles makes request which could // return data late after user already viewing different user profile - console.log('TIMEINLINE', timelineObject.userId) if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) { return } -- cgit v1.2.3-70-g09d2 From 4a5aef8883d3e92a58e272857cdfb303da61feb2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 10 Mar 2019 19:15:07 +0200 Subject: basic user and status actions implemented --- src/modules/statuses.js | 14 ------- src/services/api/api.service.js | 86 +++++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 44 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7571b62a..a1ba87c4 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -429,13 +429,6 @@ const statuses = { // Optimistic favoriting... commit('setFavorited', { status, value: true }) apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) - .then(response => { - if (response.ok) { - return response.json() - } else { - return {} - } - }) .then(status => { commit('setFavoritedConfirm', { status }) }) @@ -444,13 +437,6 @@ const statuses = { // Optimistic favoriting... commit('setFavorited', { status, value: false }) apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) - .then(response => { - if (response.ok) { - return response.json() - } else { - return {} - } - }) .then(status => { commit('setFavoritedConfirm', { status }) }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2de87026..e2f44a2b 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -5,12 +5,7 @@ const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' const TAG_TIMELINE_URL = '/api/statusnet/tags/timeline' -const FAVORITE_URL = '/api/favorites/create' -const UNFAVORITE_URL = '/api/favorites/destroy' -const RETWEET_URL = '/api/statuses/retweet' -const UNRETWEET_URL = '/api/statuses/unretweet' const STATUS_UPDATE_URL = '/api/statuses/update.json' -const STATUS_DELETE_URL = '/api/statuses/destroy' const STATUS_URL = '/api/statuses/show' const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' const CONVERSATION_URL = '/api/statusnet/conversation' @@ -19,9 +14,6 @@ const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' const FRIENDS_URL = '/api/statuses/friends.json' const BLOCKS_URL = '/api/statuses/blocks.json' -const FOLLOWING_URL = '/api/friendships/create.json' -const UNFOLLOWING_URL = '/api/friendships/destroy.json' -const QVITTER_USER_PREF_URL = '/api/qvitter/set_profile_pref.json' const REGISTRATION_URL = '/api/account/register.json' const AVATAR_UPDATE_URL = '/api/qvitter/update_avatar.json' const BG_UPDATE_URL = '/api/qvitter/update_background_image.json' @@ -31,8 +23,6 @@ const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json' const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json' const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json' const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' -const BLOCKING_URL = '/api/blocks/create.json' -const UNBLOCKING_URL = '/api/blocks/destroy.json' const USER_URL = '/api/users/show.json' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' @@ -43,6 +33,17 @@ const DENY_USER_URL = '/api/pleroma/friendships/deny' const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' +const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` +const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` +const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` +const MASTODON_UNRETWEET_URL = id => `/api/v1/statuses/${id}/unreblog` +const MASTODON_DELETE_URL = id => `/api/v1/statuses/${id}` +const MASTODON_FOLLOW_URL = id => `/api/v1/accounts/${id}/follow` +const MASTODON_UNFOLLOW_URL = id => `/api/v1/accounts/${id}/unfollow` +const MASTODON_BLOCK_URL = id => `/api/v1/accounts/${id}/block` +const MASTODON_UNBLOCK_URL = id => `/api/v1/accounts/${id}/unblock` +const MASTODON_MUTE_URL = id => `/api/v1/accounts/${id}/mute` +const MASTODON_UNMUTE_URL = id => `/api/v1/accounts/${id}/unmute` import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -195,7 +196,7 @@ const externalProfile = ({profileUrl, credentials}) => { } const followUser = ({id, credentials}) => { - let url = `${FOLLOWING_URL}?user_id=${id}` + let url = MASTODON_FOLLOW_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -203,7 +204,7 @@ const followUser = ({id, credentials}) => { } const unfollowUser = ({id, credentials}) => { - let url = `${UNFOLLOWING_URL}?user_id=${id}` + let url = MASTODON_UNFOLLOW_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -211,7 +212,7 @@ const unfollowUser = ({id, credentials}) => { } const blockUser = ({id, credentials}) => { - let url = `${BLOCKING_URL}?user_id=${id}` + let url = MASTODON_BLOCK_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -219,7 +220,7 @@ const blockUser = ({id, credentials}) => { } const unblockUser = ({id, credentials}) => { - let url = `${UNBLOCKING_URL}?user_id=${id}` + let url = MASTODON_UNBLOCK_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -324,18 +325,11 @@ const fetchStatus = ({id, credentials}) => { } const setUserMute = ({id, credentials, muted = true}) => { - const form = new FormData() - - const muteInteger = muted ? 1 : 0 - - form.append('namespace', 'qvitter') - form.append('data', muteInteger) - form.append('topic', `mute:${id}`) + const url = muted ? MASTODON_MUTE_URL(id) : MASTODON_UNMUTE_URL(id) - return fetch(QVITTER_USER_PREF_URL, { + return fetch(url, { method: 'POST', - headers: authHeaders(credentials), - body: form + headers: authHeaders(credentials) }) } @@ -407,31 +401,63 @@ const verifyCredentials = (user) => { } const favorite = ({ id, credentials }) => { - return fetch(`${FAVORITE_URL}/${id}.json`, { + return fetch(MASTODON_FAVORITE_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const unfavorite = ({ id, credentials }) => { - return fetch(`${UNFAVORITE_URL}/${id}.json`, { + return fetch(MASTODON_UNFAVORITE_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const retweet = ({ id, credentials }) => { - return fetch(`${RETWEET_URL}/${id}.json`, { + return fetch(MASTODON_RETWEET_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const unretweet = ({ id, credentials }) => { - return fetch(`${UNRETWEET_URL}/${id}.json`, { + return fetch(MASTODON_UNRETWEET_URL(id), { headers: authHeaders(credentials), method: 'POST' }) + .then(response => { + if (response.ok) { + return response.json() + } else { + return {} + } + }) + .then((data) => parseStatus(data)) } const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks}) => { @@ -468,9 +494,9 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me } const deleteStatus = ({ id, credentials }) => { - return fetch(`${STATUS_DELETE_URL}/${id}.json`, { + return fetch(MASTODON_DELETE_URL(id), { headers: authHeaders(credentials), - method: 'POST' + method: 'DELETE' }) } -- cgit v1.2.3-70-g09d2 From 70d7ed36076081f22368bceaa42dd0548fd1c89a Mon Sep 17 00:00:00 2001 From: shpuld Date: Sun, 10 Mar 2019 22:40:48 +0200 Subject: Make minId reset with minVisibleId to prevent gaps when showing new --- src/modules/statuses.js | 1 + test/unit/specs/modules/statuses.spec.js | 418 ++++++++++++++++--------------- 2 files changed, 220 insertions(+), 199 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7571b62a..6b512fa3 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -333,6 +333,7 @@ export const mutations = { oldTimeline.newStatusCount = 0 oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50) oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id + oldTimeline.minId = oldTimeline.minVisibleId oldTimeline.visibleStatusesObject = {} each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 864b798d..0bbcb25a 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -14,238 +14,258 @@ const makeMockStatus = ({id, text, type = 'status'}) => { } } -describe('Statuses.prepareStatus', () => { - it('sets deleted flag to false', () => { - const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) - expect(prepareStatus(aStatus).deleted).to.eq(false) +describe('Statuses module', () => { + describe('prepareStatus', () => { + it('sets deleted flag to false', () => { + const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) + expect(prepareStatus(aStatus).deleted).to.eq(false) + }) }) -}) -describe('The Statuses module', () => { - it('adds the status to allStatuses and to the given timeline', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + describe('addNewStatuses', () => { + it('adds the status to allStatuses and to the given timeline', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) - mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(1) - }) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(1) + }) - it('counts the status as new if it has not been seen on this timeline', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + it('counts the status as new if it has not been seen on this timeline', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) - mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) + mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(1) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(1) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.friends.statuses).to.eql([status]) - expect(state.timelines.friends.visibleStatuses).to.eql([]) - expect(state.timelines.friends.newStatusCount).to.equal(1) - }) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.friends.statuses).to.eql([status]) + expect(state.timelines.friends.visibleStatuses).to.eql([]) + expect(state.timelines.friends.newStatusCount).to.equal(1) + }) - it('add the statuses to allStatuses if no timeline is given', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + it('add the statuses to allStatuses if no timeline is given', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) - mutations.addNewStatuses(state, { statuses: [status] }) + mutations.addNewStatuses(state, { statuses: [status] }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(0) - }) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(0) + }) - it('adds the status to allStatuses and to the given timeline, directly visible', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + it('adds the status to allStatuses and to the given timeline, directly visible', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([status]) - expect(state.timelines.public.newStatusCount).to.equal(0) - }) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([status]) + expect(state.timelines.public.newStatusCount).to.equal(0) + }) - it('removes statuses by tag on deletion', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const otherStatus = makeMockStatus({id: '3'}) - status.uri = 'xxx' - const deletion = makeMockStatus({id: '2', type: 'deletion'}) - deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' - deletion.uri = 'xxx' - - mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' }) - - expect(state.allStatuses).to.eql([otherStatus]) - expect(state.timelines.public.statuses).to.eql([otherStatus]) - expect(state.timelines.public.visibleStatuses).to.eql([otherStatus]) - expect(state.timelines.public.maxId).to.eql('3') - }) + it('removes statuses by tag on deletion', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) + status.uri = 'xxx' + const deletion = makeMockStatus({id: '2', type: 'deletion'}) + deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' + deletion.uri = 'xxx' - it('does not update the maxId when the noIdUpdate flag is set', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const secondStatus = makeMockStatus({id: '2'}) + mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.maxId).to.eql('1') + expect(state.allStatuses).to.eql([otherStatus]) + expect(state.timelines.public.statuses).to.eql([otherStatus]) + expect(state.timelines.public.visibleStatuses).to.eql([otherStatus]) + expect(state.timelines.public.maxId).to.eql('3') + }) - mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true }) - expect(state.timelines.public.statuses).to.eql([secondStatus, status]) - expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status]) - expect(state.timelines.public.maxId).to.eql('1') - }) + it('does not update the maxId when the noIdUpdate flag is set', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const secondStatus = makeMockStatus({id: '2'}) - it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { - const state = defaultState() - const nonVisibleStatus = makeMockStatus({id: '1'}) - const status = makeMockStatus({id: '3'}) - const statusTwo = makeMockStatus({id: '2'}) - const statusThree = makeMockStatus({id: '4'}) + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.maxId).to.eql('1') - mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true }) + expect(state.timelines.public.statuses).to.eql([secondStatus, status]) + expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status]) + expect(state.timelines.public.maxId).to.eql('1') + }) - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) + it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { + const state = defaultState() + const nonVisibleStatus = makeMockStatus({id: '1'}) + const status = makeMockStatus({id: '3'}) + const statusTwo = makeMockStatus({id: '2'}) + const statusThree = makeMockStatus({id: '4'}) - expect(state.timelines.public.minVisibleId).to.equal('2') + mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus]) - expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo]) - }) + expect(state.timelines.public.minVisibleId).to.equal('2') - it('splits retweets from their status and links them', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const retweet = makeMockStatus({id: '2', type: 'retweet'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - - retweet.retweeted_status = status - - // It adds both statuses, but only the retweet to visible. - mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.timelines.public.statuses).to.have.length(1) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal('1') - expect(state.allStatuses[1].id).to.equal('2') - - // It refers to the modified status. - mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' }) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal('1') - expect(state.allStatuses[0].text).to.equal(modStatus.text) - expect(state.allStatuses[1].id).to.equal('2') - expect(retweet.retweeted_status.text).to.eql(modStatus.text) - }) + mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) - it('replaces existing statuses with the same id', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - - // Add original status - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - - // Add new version of status - mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - expect(state.allStatuses[0].text).to.eql(modStatus.text) - }) + expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus]) + expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo]) + }) - it('replaces existing statuses with the same id, coming from a retweet', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - const retweet = makeMockStatus({id: '2', type: 'retweet'}) - retweet.retweeted_status = modStatus - - // Add original status - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - - // Add new version of status - mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - // Don't add the retweet itself if the tweet is visible - expect(state.timelines.public.statuses).to.have.length(1) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].text).to.eql(modStatus.text) + it('splits retweets from their status and links them', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + + retweet.retweeted_status = status + + // It adds both statuses, but only the retweet to visible. + mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.timelines.public.statuses).to.have.length(1) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].id).to.equal('1') + expect(state.allStatuses[1].id).to.equal('2') + + // It refers to the modified status. + mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' }) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].id).to.equal('1') + expect(state.allStatuses[0].text).to.equal(modStatus.text) + expect(state.allStatuses[1].id).to.equal('2') + expect(retweet.retweeted_status.text).to.eql(modStatus.text) + }) + + it('replaces existing statuses with the same id', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + + // Add original status + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + + // Add new version of status + mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + expect(state.allStatuses[0].text).to.eql(modStatus.text) + }) + + it('replaces existing statuses with the same id, coming from a retweet', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) + retweet.retweeted_status = modStatus + + // Add original status + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + + // Add new version of status + mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + // Don't add the retweet itself if the tweet is visible + expect(state.timelines.public.statuses).to.have.length(1) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].text).to.eql(modStatus.text) + }) + + it('handles favorite actions', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + + const favorite = { + id: '2', + type: 'favorite', + in_reply_to_status_id: '1', // The API uses strings here... + uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', + text: 'a favorited something by b', + user: { id: '99' } + } + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.maxId).to.eq(favorite.id) + + // Adding it again does nothing + mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.maxId).to.eq(favorite.id) + + // If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request. + const user = { + id: '1' + } + + const ownFavorite = { + id: '3', + type: 'favorite', + in_reply_to_status_id: '1', // The API uses strings here... + uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', + text: 'a favorited something by b', + user + } + + mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true) + }) }) - it('handles favorite actions', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - - const favorite = { - id: '2', - type: 'favorite', - in_reply_to_status_id: '1', // The API uses strings here... - uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', - text: 'a favorited something by b', - user: { id: '99' } - } - - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.maxId).to.eq(favorite.id) - - // Adding it again does nothing - mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.maxId).to.eq(favorite.id) - - // If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request. - const user = { - id: '1' - } - - const ownFavorite = { - id: '3', - type: 'favorite', - in_reply_to_status_id: '1', // The API uses strings here... - uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', - text: 'a favorited something by b', - user - } - - mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true) + describe('showNewStatuses', () => { + it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => { + const state = defaultState() + const status = makeMockStatus({ id: '10' }) + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + const newStatus = makeMockStatus({ id: '20' }) + mutations.addNewStatuses(state, { statuses: [newStatus], showImmediately: false, timeline: 'public' }) + state.timelines.public.minId = '5' + mutations.showNewStatuses(state, { timeline: 'public' }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(2) + expect(state.timelines.public.minVisibleId).to.eql('10') + expect(state.timelines.public.minId).to.eql('10') + }) }) - it('keeps userId when clearing user timeline', () => { - const state = defaultState() - state.timelines.user.userId = 123 + describe('clearTimeline', () => { + it('keeps userId when clearing user timeline', () => { + const state = defaultState() + state.timelines.user.userId = 123 - mutations.clearTimeline(state, { timeline: 'user' }) + mutations.clearTimeline(state, { timeline: 'user' }) - expect(state.timelines.user.userId).to.eql(123) + expect(state.timelines.user.userId).to.eql(123) + }) }) describe('notifications', () => { -- cgit v1.2.3-70-g09d2 From 06d39b62a8358c911b18f5acc378047035840465 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 11 Mar 2019 02:17:49 +0200 Subject: fixed tests, review fixes, now storing local users with downcase screen name for better compatibility --- src/components/user_profile/user_profile.js | 7 ++++--- src/modules/statuses.js | 2 +- src/modules/users.js | 4 ++-- test/unit/specs/components/user_profile.spec.js | 14 ++++++++++---- test/unit/specs/modules/users.spec.js | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 2d186bc5..a8dfce2f 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -68,7 +68,7 @@ const UserProfile = { }, userInStore () { const routeParams = this.$route.params - return this.$store.getters.findUser(routeParams.name || routeParams.iid) + return this.$store.getters.findUser(routeParams.name || routeParams.id) }, user () { if (this.timeline.statuses[0]) { @@ -135,13 +135,14 @@ const UserProfile = { } }, watch: { - userId (newVal, oldVal) { + // userId can be undefined if we don't know it yet + userId (newVal) { if (newVal) { this.cleanUp() this.startUp() } }, - userName (newVal, oldVal) { + userName () { if (this.$route.params.name) { this.fetchUserId() this.cleanUp() diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2b0215f0..ea1b2de0 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -73,7 +73,7 @@ const mergeOrAdd = (arr, obj, item) => { if (oldItem) { // We already have this, so only merge the new info. // We ignore null values to avoid overwriting existing properties with missing data - // we also skip 'used' because that is handled by users module + // we also skip 'user' because that is handled by users module merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user')) // Reactivity fix. oldItem.attachments.splice(oldItem.attachments.length) diff --git a/src/modules/users.js b/src/modules/users.js index e4146c31..5eabb1ec 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -18,7 +18,7 @@ export const mergeOrAdd = (arr, obj, item) => { arr.push(item) obj[item.id] = item if (item.screen_name && !item.screen_name.includes('@')) { - obj[item.screen_name] = item + obj[item.screen_name.toLowerCase()] = item } return { item, new: true } } @@ -132,7 +132,7 @@ export const mutations = { } export const getters = { - findUser: state => query => state.usersObject[query] + findUser: state => query => state.usersObject[typeof query === 'string' ? query.toLowerCase() : query] } export const defaultState = { diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js index 23e5ce20..847481f3 100644 --- a/test/unit/specs/components/user_profile.spec.js +++ b/test/unit/specs/components/user_profile.spec.js @@ -12,6 +12,11 @@ const mutations = { setError: () => {} } +const actions = { + fetchUser: () => {}, + fetchUserByScreenName: () => {} +} + const testGetters = { findUser: state => getters.findUser(state.users) } @@ -30,6 +35,7 @@ const extUser = { const externalProfileStore = new Vuex.Store({ mutations, + actions, getters: testGetters, state: { api: { @@ -88,7 +94,7 @@ const externalProfileStore = new Vuex.Store({ currentUser: { credentials: '' }, - usersObject: [extUser], + usersObject: { 100: extUser }, users: [extUser] } } @@ -96,6 +102,7 @@ const externalProfileStore = new Vuex.Store({ const localProfileStore = new Vuex.Store({ mutations, + actions, getters: testGetters, state: { api: { @@ -154,14 +161,13 @@ const localProfileStore = new Vuex.Store({ currentUser: { credentials: '' }, - usersObject: [localUser], + usersObject: { 100: localUser, 'testuser': localUser }, users: [localUser] } } }) -// It's a little bit more complicated now -describe.skip('UserProfile', () => { +describe('UserProfile', () => { it('renders external profile', () => { const wrapper = mount(UserProfile, { localVue, diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js index a7f18dce..c8bc0ae7 100644 --- a/test/unit/specs/modules/users.spec.js +++ b/test/unit/specs/modules/users.spec.js @@ -40,7 +40,7 @@ describe('The users module', () => { const state = { usersObject: { 1: user, - Guy: user + guy: user } } const name = 'Guy' @@ -53,7 +53,7 @@ describe('The users module', () => { const state = { usersObject: { 1: user, - Guy: user + guy: user } } const id = '1' -- cgit v1.2.3-70-g09d2 From e617ed285e8ecb13829558197bdf04ffb9583ea9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 22 Mar 2019 00:05:20 +0200 Subject: reactivity fixes --- src/modules/statuses.js | 3 ++- src/modules/users.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index f14b8703..a16342e0 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,5 @@ import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash' +import { set } from 'vue' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -82,7 +83,7 @@ const mergeOrAdd = (arr, obj, item) => { // This is a new item, prepare it prepareStatus(item) arr.push(item) - obj[item.id] = item + set(obj, item.id, item) return {item, new: true} } } diff --git a/src/modules/users.js b/src/modules/users.js index 1fe12fc8..2950971f 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -16,9 +16,9 @@ export const mergeOrAdd = (arr, obj, item) => { } else { // This is a new item, prepare it arr.push(item) - obj[item.id] = item + set(obj, item.id, item) if (item.screen_name && !item.screen_name.includes('@')) { - obj[item.screen_name.toLowerCase()] = item + set(obj, item.screen_name.toLowerCase(), item) } return { item, new: true } } -- cgit v1.2.3-70-g09d2 From 8e4ddcb843e9ba6422ae1e053a84a7148031f995 Mon Sep 17 00:00:00 2001 From: Brenden Bice Date: Thu, 28 Mar 2019 13:20:09 -0400 Subject: update store according to retweeted status --- src/modules/statuses.js | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/modules/statuses.js') diff --git a/src/modules/statuses.js b/src/modules/statuses.js index a16342e0..95262183 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -363,6 +363,15 @@ export const mutations = { }, setRetweeted (state, { status, value }) { const newStatus = state.allStatusesObject[status.id] + + if (newStatus.repeated !== value) { + if (value) { + newStatus.repeat_num++ + } else { + newStatus.repeat_num-- + } + } + newStatus.repeated = value }, setDeleted (state, { status }) { -- cgit v1.2.3-70-g09d2