From ef2585e32b546722f2157bd6203701deb495d2e9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 11 Jan 2019 02:40:17 +0300 Subject: [PATCH 01/48] Remove all explicit and implicit conversions of statusId to number, changed explicit ones so that they convert them to string --- src/components/conversation-page/conversation-page.js | 4 ++-- src/components/conversation/conversation.js | 6 +++--- src/components/status/status.js | 4 ++-- src/modules/statuses.js | 10 ++++++---- src/services/notification_utils/notification_utils.js | 4 ++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index beffa5bbc1..bdf84d0c6c 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -1,5 +1,5 @@ import Conversation from '../conversation/conversation.vue' -import { find, toInteger } from 'lodash' +import { find } from 'lodash' const conversationPage = { components: { @@ -7,7 +7,7 @@ const conversationPage = { }, computed: { statusoid () { - const id = toInteger(this.$route.params.id) + const id = String(this.$route.params.id) const statuses = this.$store.state.statuses.allStatuses const status = find(statuses, {id}) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 9d9f7bbe16..95432248fb 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -32,7 +32,7 @@ const conversation = { replies () { let i = 1 return reduce(this.conversation, (result, {id, in_reply_to_status_id}) => { - const irid = Number(in_reply_to_status_id) + const irid = String(in_reply_to_status_id) if (irid) { result[irid] = result[irid] || [] result[irid].push({ @@ -69,7 +69,7 @@ const conversation = { } }, getReplies (id) { - id = Number(id) + id = String(id) return this.replies[id] || [] }, focused (id) { @@ -80,7 +80,7 @@ const conversation = { } }, setHighlight (id) { - this.highlight = Number(id) + this.highlight = String(id) } } } diff --git a/src/components/status/status.js b/src/components/status/status.js index 73d53694e6..7e1e7dabf3 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -270,7 +270,7 @@ const Status = { }, replyEnter (id, event) { this.showPreview = true - const targetId = Number(id) + const targetId = String(id) const statuses = this.$store.state.statuses.allStatuses if (!this.preview) { @@ -295,7 +295,7 @@ const Status = { }, watch: { 'highlight': function (id) { - id = Number(id) + id = String(id) if (this.status.id === id) { let rect = this.$el.getBoundingClientRect() if (rect.top < 100) { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index dccccf7248..fe65d843e8 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { includes, remove, slice, sortBy, toInteger, each, find, flatten, maxBy, minBy, merge, last, isArray } from 'lodash' +import { includes, remove, slice, each, find, flatten, maxBy, minBy, merge, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -122,9 +122,11 @@ const mergeOrAdd = (arr, obj, item) => { } } +const sortById = (a, b) => a.id > b.id ? -1 : 1 + const sortTimeline = (timeline) => { - timeline.visibleStatuses = sortBy(timeline.visibleStatuses, ({id}) => -id) - timeline.statuses = sortBy(timeline.statuses, ({id}) => -id) + timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById) + timeline.statuses = timeline.statuses.sort(sortById) timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id return timeline } @@ -200,7 +202,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us } const favoriteStatus = (favorite, counter) => { - const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) }) + const status = find(allStatuses, { id: String(favorite.in_reply_to_status_id) }) if (status) { // This is our favorite, so the relevant bit. if (favorite.user.id === user.id) { diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index f5ac0d4766..c6782af4fc 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -10,8 +10,8 @@ export const visibleTypes = store => ([ ].filter(_ => _)) export const visibleNotificationsFromStore = store => { - // Don't know why, but sortBy([seen, -action.id]) doesn't work. - let sortedNotifications = sortBy(notificationsFromStore(store), ({action}) => -action.id) + // map is just to clone the array since sort mutates it and it causes some issues + let sortedNotifications = notificationsFromStore(store).map(_ => _).sort((a, b) => a.id > b.id ? -1 : 1) sortedNotifications = sortBy(sortedNotifications, 'seen') return sortedNotifications.filter((notification) => visibleTypes(store).includes(notification.type)) } From 48e811e6edc774affa3526e7a25df6bb07597c9d Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 11 Jan 2019 03:00:11 +0300 Subject: [PATCH 02/48] added some more explicit to string conversion since BE seem to be sending numbers and it could cause an issue. --- src/modules/statuses.js | 8 ++++++++ src/modules/users.js | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index fe65d843e8..05626a024a 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -105,6 +105,9 @@ export const findMaxId = (...args) => { } const mergeOrAdd = (arr, obj, item) => { + // For sequential IDs BE passes numbers as numbers, we want them as strings. + item.id = String(item.id) + const oldItem = obj[item.id] if (oldItem) { @@ -292,8 +295,13 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot each(notifications, (notification) => { const result = mergeOrAdd(allStatuses, allStatusesObject, notification.notice) const action = result.item + // For sequential IDs BE passes numbers as numbers, we want them as strings. + action.id = String(action.id) + // Only add a new notification if we don't have one for the same action + // TODO: this technically works but should be checking for notification.id, not action.id i think if (!find(state.notifications.data, (oldNotification) => oldNotification.action.id === action.id)) { + // TODO: adapt to "what if notification ids are not actually numbers" state.notifications.maxId = Math.max(notification.id, state.notifications.maxId) state.notifications.minId = Math.min(notification.id, state.notifications.minId) diff --git a/src/modules/users.js b/src/modules/users.js index adbd37dd4b..9d00b78287 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -7,6 +7,9 @@ import { humanizeErrors } from './errors' // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { + // For sequential IDs BE passes numbers as numbers, we want them as strings. + item.id = String(item.id) + if (!item) { return false } const oldItem = obj[item.id] if (oldItem) { @@ -64,10 +67,10 @@ export const mutations = { each(users, (user) => mergeOrAdd(state.users, state.usersObject, user)) }, setUserForStatus (state, status) { - status.user = state.usersObject[status.user.id] + status.user = state.usersObject[String(status.user.id)] }, setUserForNotification (state, notification) { - notification.action.user = state.usersObject[notification.action.user.id] + notification.action.user = state.usersObject[String(notification.action.user.id)] }, setColor (state, { user: { id }, highlighted }) { const user = state.usersObject[id] From b18e27c6d4d2b3d681be8dd81dafcdfeaf20fb35 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 11 Jan 2019 03:38:23 +0300 Subject: [PATCH 03/48] fix tests, removed one unused function, fix real problem that tests helped to surface --- src/modules/statuses.js | 4 - .../notification_utils/notification_utils.js | 2 +- test/unit/specs/modules/statuses.spec.js | 105 ++++++++---------- test/unit/specs/modules/users.spec.js | 16 +-- .../notification_utils.spec.js | 16 +-- 5 files changed, 62 insertions(+), 81 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 05626a024a..a931fd9725 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -100,10 +100,6 @@ export const statusType = (status) => { return 'unknown' } -export const findMaxId = (...args) => { - return (maxBy(flatten(args), 'id') || {}).id -} - const mergeOrAdd = (arr, obj, item) => { // For sequential IDs BE passes numbers as numbers, we want them as strings. item.id = String(item.id) diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index c6782af4fc..c38796779e 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -11,7 +11,7 @@ export const visibleTypes = store => ([ export const visibleNotificationsFromStore = store => { // map is just to clone the array since sort mutates it and it causes some issues - let sortedNotifications = notificationsFromStore(store).map(_ => _).sort((a, b) => a.id > b.id ? -1 : 1) + let sortedNotifications = notificationsFromStore(store).map(_ => _).sort((a, b) => a.action.id > b.action.id ? -1 : 1) sortedNotifications = sortBy(sortedNotifications, 'seen') return sortedNotifications.filter((notification) => visibleTypes(store).includes(notification.type)) } diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 7d403312a1..f4d0ddf42d 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -1,11 +1,11 @@ import { cloneDeep } from 'lodash' -import { defaultState, mutations, findMaxId, prepareStatus, statusType } from '../../../../src/modules/statuses.js' +import { defaultState, mutations, prepareStatus, statusType } from '../../../../src/modules/statuses.js' // eslint-disable-next-line camelcase const makeMockStatus = ({id, text, is_post_verb = true}) => { return { id, - user: {id: 0}, + user: {id: '0'}, name: 'status', text: text || `Text number ${id}`, fave_num: 0, @@ -32,45 +32,30 @@ describe('Statuses.statusType', () => { describe('Statuses.prepareStatus', () => { it('sets nsfw for statuses with the #nsfw tag', () => { - const safe = makeMockStatus({id: 1, text: 'Hello oniichan'}) - const nsfw = makeMockStatus({id: 1, text: 'Hello oniichan #nsfw'}) + const safe = makeMockStatus({id: '1', text: 'Hello oniichan'}) + const nsfw = makeMockStatus({id: '1', text: 'Hello oniichan #nsfw'}) expect(prepareStatus(safe).nsfw).to.eq(false) expect(prepareStatus(nsfw).nsfw).to.eq(true) }) it('leaves existing nsfw settings alone', () => { - const nsfw = makeMockStatus({id: 1, text: 'Hello oniichan #nsfw'}) + const nsfw = makeMockStatus({id: '1', text: 'Hello oniichan #nsfw'}) nsfw.nsfw = false expect(prepareStatus(nsfw).nsfw).to.eq(false) }) it('sets deleted flag to false', () => { - const aStatus = makeMockStatus({id: 1, text: 'Hello oniichan'}) + const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) expect(prepareStatus(aStatus).deleted).to.eq(false) }) }) -describe('Statuses.findMaxId', () => { - it('returns the largest id in any of the given arrays', () => { - const statusesOne = [{ id: 100 }, { id: 2 }] - const statusesTwo = [{ id: 3 }] - - const maxId = findMaxId(statusesOne, statusesTwo) - expect(maxId).to.eq(100) - }) - - it('returns undefined for empty arrays', () => { - const maxId = findMaxId([], []) - expect(maxId).to.eq(undefined) - }) -}) - describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -82,7 +67,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 status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) @@ -100,7 +85,7 @@ describe('The Statuses module', () => { it('add the statuses to allStatuses if no timeline is given', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status] }) @@ -112,7 +97,7 @@ describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline, directly visible', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -124,10 +109,10 @@ describe('The Statuses module', () => { it('removes statuses by tag on deletion', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const otherStatus = makeMockStatus({id: 3}) + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) status.uri = 'xxx' - const deletion = makeMockStatus({id: 2, is_post_verb: false}) + const deletion = makeMockStatus({id: '2', is_post_verb: false}) deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' deletion.uri = 'xxx' @@ -137,36 +122,36 @@ describe('The Statuses module', () => { 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) + expect(state.timelines.public.maxId).to.eql('3') }) it('does not update the maxId when the noIdUpdate flag is set', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const secondStatus = makeMockStatus({id: 2}) + const status = makeMockStatus({id: '1'}) + const secondStatus = makeMockStatus({id: '2'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.maxId).to.eql(1) + expect(state.timelines.public.maxId).to.eql('1') 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) + expect(state.timelines.public.maxId).to.eql('1') }) it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { const state = cloneDeep(defaultState) - const nonVisibleStatus = makeMockStatus({id: 1}) - const status = makeMockStatus({id: 3}) - const statusTwo = makeMockStatus({id: 2}) - const statusThree = makeMockStatus({id: 4}) + const nonVisibleStatus = makeMockStatus({id: '1'}) + const status = makeMockStatus({id: '3'}) + const statusTwo = makeMockStatus({id: '2'}) + const statusThree = makeMockStatus({id: '4'}) mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.minVisibleId).to.equal(2) + expect(state.timelines.public.minVisibleId).to.equal('2') mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) @@ -176,9 +161,9 @@ describe('The Statuses module', () => { it('splits retweets from their status and links them', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const retweet = makeMockStatus({id: 2, is_post_verb: false}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) + const status = makeMockStatus({id: '1'}) + const retweet = makeMockStatus({id: '2', is_post_verb: false}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) retweet.retweeted_status = status @@ -187,22 +172,22 @@ describe('The Statuses module', () => { 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) + 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].id).to.equal('1') expect(state.allStatuses[0].text).to.equal(modStatus.text) - expect(state.allStatuses[1].id).to.equal(2) + 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 = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) + 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' }) @@ -218,9 +203,9 @@ describe('The Statuses module', () => { it('replaces existing statuses with the same id, coming from a retweet', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const modStatus = makeMockStatus({id: 1, text: 'something else'}) - const retweet = makeMockStatus({id: 2, is_post_verb: false}) + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + const retweet = makeMockStatus({id: '2', is_post_verb: false}) retweet.retweeted_status = modStatus // Add original status @@ -239,15 +224,15 @@ describe('The Statuses module', () => { it('handles favorite actions', () => { const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) + const status = makeMockStatus({id: '1'}) const favorite = { - id: 2, + id: '2', is_post_verb: false, 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 } + user: { id: '99' } } mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -266,11 +251,11 @@ describe('The Statuses module', () => { // 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 + id: '1' } const ownFavorite = { - id: 3, + id: '3', is_post_verb: false, 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', @@ -287,16 +272,16 @@ describe('The Statuses module', () => { describe('notifications', () => { it('removes a notification when the notice gets removed', () => { - const user = { id: 1 } + const user = { id: '1' } const state = cloneDeep(defaultState) - const status = makeMockStatus({id: 1}) - const otherStatus = makeMockStatus({id: 3}) - const mentionedStatus = makeMockStatus({id: 2}) + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) + const mentionedStatus = makeMockStatus({id: '2'}) mentionedStatus.attentions = [user] mentionedStatus.uri = 'xxx' otherStatus.attentions = [user] - const deletion = makeMockStatus({id: 4, is_post_verb: false}) + const deletion = makeMockStatus({id: '4', is_post_verb: false}) deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' deletion.uri = 'xxx' diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js index af60c9b399..b0f3c51eeb 100644 --- a/test/unit/specs/modules/users.spec.js +++ b/test/unit/specs/modules/users.spec.js @@ -6,8 +6,8 @@ describe('The users module', () => { describe('mutations', () => { it('adds new users to the set, merging in new information for old users', () => { const state = cloneDeep(defaultState) - const user = { id: 1, name: 'Guy' } - const modUser = { id: 1, name: 'Dude' } + const user = { id: '1', name: 'Guy' } + const modUser = { id: '1', name: 'Dude' } mutations.addNewUsers(state, [user]) expect(state.users).to.have.length(1) @@ -21,7 +21,7 @@ describe('The users module', () => { it('sets a mute bit on users', () => { const state = cloneDeep(defaultState) - const user = { id: 1, name: 'Guy' } + const user = { id: '1', name: 'Guy' } mutations.addNewUsers(state, [user]) mutations.setMuted(state, {user, muted: true}) @@ -38,11 +38,11 @@ describe('The users module', () => { it('returns user with matching screen_name', () => { const state = { users: [ - { screen_name: 'Guy', id: 1 } + { screen_name: 'Guy', id: '1' } ] } const name = 'Guy' - const expected = { screen_name: 'Guy', id: 1 } + const expected = { screen_name: 'Guy', id: '1' } expect(getters.userByName(state)(name)).to.eql(expected) }) }) @@ -51,11 +51,11 @@ describe('The users module', () => { it('returns user with matching id', () => { const state = { users: [ - { screen_name: 'Guy', id: 1 } + { screen_name: 'Guy', id: '1' } ] } - const id = 1 - const expected = { screen_name: 'Guy', id: 1 } + const id = '1' + const expected = { screen_name: 'Guy', id: '1' } expect(getters.userById(state)(id)).to.eql(expected) }) }) diff --git a/test/unit/specs/services/notification_utils/notification_utils.spec.js b/test/unit/specs/services/notification_utils/notification_utils.spec.js index c44b8c9e0d..e945459edd 100644 --- a/test/unit/specs/services/notification_utils/notification_utils.spec.js +++ b/test/unit/specs/services/notification_utils/notification_utils.spec.js @@ -9,15 +9,15 @@ describe('NotificationUtils', () => { notifications: { data: [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like' }, { - action: { id: 2 }, + action: { id: '2' }, type: 'mention' }, { - action: { id: 3 }, + action: { id: '3' }, type: 'repeat' } ] @@ -34,11 +34,11 @@ describe('NotificationUtils', () => { } const expected = [ { - action: { id: 3 }, + action: { id: '3' }, type: 'repeat' }, { - action: { id: 1 }, + action: { id: '1' }, type: 'like' } ] @@ -54,12 +54,12 @@ describe('NotificationUtils', () => { notifications: { data: [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like', seen: false }, { - action: { id: 2 }, + action: { id: '2' }, type: 'mention', seen: true } @@ -77,7 +77,7 @@ describe('NotificationUtils', () => { } const expected = [ { - action: { id: 1 }, + action: { id: '1' }, type: 'like', seen: false } From 22e0686395de023eb4c26b94be11954fc4803331 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 11 Jan 2019 03:41:57 +0300 Subject: [PATCH 04/48] fix lint --- src/modules/statuses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index a931fd9725..f90b0ed3bf 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { includes, remove, slice, each, find, flatten, maxBy, minBy, merge, last, isArray } from 'lodash' +import { includes, 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' From 039a4074006fb91ac9031b41b4e9af4a15766dfa Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sat, 12 Jan 2019 23:33:45 +0300 Subject: [PATCH 05/48] some initial work to make it possible to use "unregistered" timelines, i.e. not reserving a timeline by name, instead just passing timeline object itself. --- src/components/timeline/timeline.js | 17 +++++----- src/components/user_profile/user_profile.js | 6 ++++ src/components/user_profile/user_profile.vue | 1 + src/modules/api.js | 10 +++--- src/modules/statuses.js | 31 ++++++++++--------- src/services/api/api.service.js | 5 ++- .../timeline_fetcher.service.js | 4 +-- 7 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 98da86607b..23d2c1e817 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -7,7 +7,6 @@ import { throttle } from 'lodash' const Timeline = { props: [ 'timeline', - 'timelineName', 'title', 'userId', 'tag', @@ -55,7 +54,7 @@ const Timeline = { timelineFetcher.fetchAndUpdate({ store, credentials, - timeline: this.timelineName, + timeline: this.timeline, showImmediately, userId: this.userId, tag: this.tag @@ -70,32 +69,32 @@ const Timeline = { destroyed () { window.removeEventListener('scroll', this.scrollLoad) if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false) - this.$store.commit('setLoading', { timeline: this.timelineName, value: false }) + this.$store.commit('setLoading', { timeline: this.timeline, value: false }) }, methods: { showNewStatuses () { if (this.timeline.flushMarker !== 0) { - this.$store.commit('clearTimeline', { timeline: this.timelineName }) - this.$store.commit('queueFlush', { timeline: this.timelineName, id: 0 }) + this.$store.commit('clearTimeline', { timeline: this.timeline }) + this.$store.commit('queueFlush', { timeline: this.timeline, id: 0 }) this.fetchOlderStatuses() } else { - this.$store.commit('showNewStatuses', { timeline: this.timelineName }) + this.$store.commit('showNewStatuses', { timeline: this.timeline }) this.paused = false } }, fetchOlderStatuses: throttle(function () { const store = this.$store const credentials = store.state.users.currentUser.credentials - store.commit('setLoading', { timeline: this.timelineName, value: true }) + store.commit('setLoading', { timeline: this.timeline, value: true }) timelineFetcher.fetchAndUpdate({ store, credentials, - timeline: this.timelineName, + timeline: this.timeline, older: true, showImmediately: true, userId: this.userId, tag: this.tag - }).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false })) + }).then(() => store.commit('setLoading', { timeline: this.timeline, value: false })) }, 1000, this), scrollLoad (e) { const bodyBRect = document.body.getBoundingClientRect() diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 2ca098171d..d6edd25aaf 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -1,6 +1,7 @@ import UserCardContent from '../user_card_content/user_card_content.vue' import UserCard from '../user_card/user_card.vue' import Timeline from '../timeline/timeline.vue' +import { emptyTl } from '../../modules/statuses.js' const UserProfile = { created () { @@ -13,6 +14,11 @@ const UserProfile = { destroyed () { this.$store.dispatch('stopFetching', 'user') }, + data () { + return { + favorites: emptyTl({ type: 'favorites', userId: this.userId }) + } + }, computed: { timeline () { return this.$store.state.statuses.timelines.user diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index a46befa53d..15f7d44f6e 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -20,6 +20,7 @@ + - + - + - + From 66dc72deb2b4b518ba09b984d792be57451c4bfc Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 22 Jan 2019 23:57:51 +0300 Subject: [PATCH 36/48] fix reply-to tooltip being somewhat unreliable --- src/components/status/status.js | 20 ++++++++++++++++++- src/components/status/status.vue | 12 +++++------ .../entity_normalizer.service.js | 8 ++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/components/status/status.js b/src/components/status/status.js index 105a736b41..44a6d5c505 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -73,6 +73,16 @@ const Status = { return (this.$store.state.config.hideAttachments && !this.inConversation) || (this.$store.state.config.hideAttachmentsInConv && this.inConversation) }, + userProfileLink () { + return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name) + }, + replyProfileLink () { + if (this.isReply) { + return this.generateUserProfileLink(this.status.in_reply_to_status_id, this.replyToName) + } else { + return '' + } + }, retweet () { return !!this.statusoid.retweeted_status }, retweeter () { return this.statusoid.user.name }, retweeterHtml () { return this.statusoid.user.name_html }, @@ -119,6 +129,14 @@ const Status = { isReply () { return !!this.status.in_reply_to_status_id }, + replyToName () { + const user = this.$store.state.users.usersObject[this.status.in_reply_to_user_id] + if (user) { + return user.screen_name + } else { + return this.status.in_reply_to_screen_name || '' + } + }, hideReply () { if (this.$store.state.config.replyVisibility === 'all') { return false @@ -277,7 +295,7 @@ const Status = { replyLeave () { this.showPreview = false }, - userProfileLink (id, name) { + generateUserProfileLink (id, name) { return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) } }, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 4a1aef8fc4..5c956467e2 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -3,7 +3,7 @@