aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorHenry Jameson <me@hjkos.com>2019-04-27 12:26:17 +0300
committerHenry Jameson <me@hjkos.com>2019-04-27 12:26:17 +0300
commite0247e21f6fad72481d8f04271d3a15e0d827acc (patch)
treeacc8a11074865a9cbdc50e9b52da918c48f7235e /src/modules
parent2b5cf61a8f9a84467495721f170df7ae1288959b (diff)
parent9e2fa50b74eb83e3c3eadb9a68d24ddaa1d9da48 (diff)
Merge remote-tracking branch 'upstream/develop' into webpack-4-dart-sass
* upstream/develop: (126 commits) entity normalizer: hook up in_reply_to_account_acct add BBCode strings fix follow button not updating bug in follow-card refer searched user objects from the global user rep set max-width of textarea in settings page Remove space in the timeline setting copy user_card.vue: Fix .emoji to apply to img Update oc.json Update oc.json Update oc.json Update oc.json replace pencil with wrench icon rebuild fontello with wrench icon added fetch all friends using pagination stop fetching user relationship when user is unauthorized Revert "recover border between basic-user-card using list component" remove extra spacing code readability fix typos clean up ...
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/api.js27
-rw-r--r--src/modules/interface.js1
-rw-r--r--src/modules/statuses.js130
-rw-r--r--src/modules/users.js189
4 files changed, 236 insertions, 111 deletions
diff --git a/src/modules/api.js b/src/modules/api.js
index 31cb55c6..7ed3edac 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -13,11 +13,11 @@ const api = {
setBackendInteractor (state, backendInteractor) {
state.backendInteractor = backendInteractor
},
- addFetcher (state, {timeline, fetcher}) {
- state.fetchers[timeline] = fetcher
+ addFetcher (state, { fetcherName, fetcher }) {
+ state.fetchers[fetcherName] = fetcher
},
- removeFetcher (state, {timeline}) {
- delete state.fetchers[timeline]
+ removeFetcher (state, { fetcherName }) {
+ delete state.fetchers[fetcherName]
},
setWsToken (state, token) {
state.wsToken = token
@@ -33,17 +33,24 @@ const api = {
}
},
actions: {
- startFetching (store, {timeline = 'friends', tag = false, userId = false}) {
+ startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) {
// Don't start fetching if we already are.
if (store.state.fetchers[timeline]) return
- const fetcher = store.state.backendInteractor.startFetching({ timeline, store, userId, tag })
- store.commit('addFetcher', { timeline, fetcher })
+ const fetcher = store.state.backendInteractor.startFetchingTimeline({ timeline, store, userId, tag })
+ store.commit('addFetcher', { fetcherName: timeline, fetcher })
},
- stopFetching (store, timeline) {
- const fetcher = store.state.fetchers[timeline]
+ startFetchingNotifications (store) {
+ // Don't start fetching if we already are.
+ if (store.state.fetchers['notifications']) return
+
+ const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })
+ store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
+ },
+ stopFetching (store, fetcherName) {
+ const fetcher = store.state.fetchers[fetcherName]
window.clearInterval(fetcher)
- store.commit('removeFetcher', {timeline})
+ store.commit('removeFetcher', { fetcherName })
},
setWsToken (store, token) {
store.commit('setWsToken', token)
diff --git a/src/modules/interface.js b/src/modules/interface.js
index 71554787..5b2762e5 100644
--- a/src/modules/interface.js
+++ b/src/modules/interface.js
@@ -48,7 +48,6 @@ const interfaceMod = {
commit('setNotificationPermission', permission)
},
setMobileLayout ({ commit }, value) {
- console.log('setMobileLayout called')
commit('setMobileLayout', value)
}
}
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 8e0203e3..e70c2400 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -20,20 +20,22 @@ const emptyTl = (userId = 0) => ({
flushMarker: 0
})
+const emptyNotifications = () => ({
+ desktopNotificationSilence: true,
+ maxId: 0,
+ minId: Number.POSITIVE_INFINITY,
+ data: [],
+ idStore: {},
+ loading: false,
+ error: false
+})
+
export const defaultState = () => ({
allStatuses: [],
allStatusesObject: {},
+ conversationsObject: {},
maxId: 0,
- notifications: {
- desktopNotificationSilence: true,
- maxId: 0,
- minId: Number.POSITIVE_INFINITY,
- data: [],
- idStore: {},
- loading: false,
- error: false,
- fetcherId: null
- },
+ notifications: emptyNotifications(),
favorites: new Set(),
error: false,
timelines: {
@@ -111,6 +113,39 @@ const sortTimeline = (timeline) => {
return timeline
}
+// Add status to the global storages (arrays and objects maintaining statuses) except timelines
+const addStatusToGlobalStorage = (state, data) => {
+ const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)
+ if (result.new) {
+ // Add to conversation
+ const status = result.item
+ const conversationsObject = state.conversationsObject
+ const conversationId = status.statusnet_conversation_id
+ if (conversationsObject[conversationId]) {
+ conversationsObject[conversationId].push(status)
+ } else {
+ set(conversationsObject, conversationId, [status])
+ }
+ }
+ return result
+}
+
+// Remove status from the global storages (arrays and objects maintaining statuses) except timelines
+const removeStatusFromGlobalStorage = (state, status) => {
+ remove(state.allStatuses, { id: status.id })
+
+ // TODO: Need to remove from allStatusesObject?
+
+ // Remove possible notification
+ remove(state.notifications.data, ({action: {id}}) => id === status.id)
+
+ // Remove from conversation
+ const conversationId = status.statusnet_conversation_id
+ if (state.conversationsObject[conversationId]) {
+ remove(state.conversationsObject[conversationId], { id: status.id })
+ }
+}
+
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId }) => {
// Sanity check
if (!isArray(statuses)) {
@@ -118,7 +153,6 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
const allStatuses = state.allStatuses
- const allStatusesObject = state.allStatusesObject
const timelineObject = state.timelines[timeline]
const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0
@@ -141,7 +175,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
const addStatus = (data, showImmediately, addToTimeline = true) => {
- const result = mergeOrAdd(allStatuses, allStatusesObject, data)
+ const result = addStatusToGlobalStorage(state, data)
const status = result.item
if (result.new) {
@@ -235,16 +269,13 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
},
'deletion': (deletion) => {
const uri = deletion.uri
-
- // Remove possible notification
const status = find(allStatuses, {uri})
if (!status) {
return
}
- remove(state.notifications.data, ({action: {id}}) => id === status.id)
+ removeStatusFromGlobalStorage(state, status)
- remove(allStatuses, { uri })
if (timeline) {
remove(timelineObject.statuses, { uri })
remove(timelineObject.visibleStatuses, { uri })
@@ -271,12 +302,12 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
}
-const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => {
- const allStatuses = state.allStatuses
- const allStatusesObject = state.allStatusesObject
+const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters }) => {
each(notifications, (notification) => {
- notification.action = mergeOrAdd(allStatuses, allStatusesObject, notification.action).item
- notification.status = notification.status && mergeOrAdd(allStatuses, allStatusesObject, notification.status).item
+ if (notification.type !== 'follow') {
+ notification.action = addStatusToGlobalStorage(state, notification.action).item
+ notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
+ }
// Only add a new notification if we don't have one for the same action
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
@@ -292,15 +323,32 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
if ('Notification' in window && window.Notification.permission === 'granted') {
const notifObj = {}
- const action = notification.action
- const title = action.user.name
- notifObj.icon = action.user.profile_image_url
- notifObj.body = action.text // there's a problem that it doesn't put a space before links tho
+ const status = notification.status
+ const title = notification.from_profile.name
+ notifObj.icon = notification.from_profile.profile_image_url
+ let i18nString
+ switch (notification.type) {
+ case 'like':
+ i18nString = 'favorited_you'
+ break
+ case 'repeat':
+ i18nString = 'repeated_you'
+ break
+ case 'follow':
+ i18nString = 'followed_you'
+ break
+ }
+
+ if (i18nString) {
+ notifObj.body = rootGetters.i18n.t('notifications.' + i18nString)
+ } else {
+ notifObj.body = notification.status.text
+ }
// Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
- if (action.attachments && action.attachments.length > 0 && !action.nsfw &&
- action.attachments[0].mimetype.startsWith('image/')) {
- notifObj.image = action.attachments[0].url
+ if (status && status.attachments && status.attachments.length > 0 && !status.nsfw &&
+ status.attachments[0].mimetype.startsWith('image/')) {
+ notifObj.image = status.attachments[0].url
}
if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) {
@@ -340,9 +388,6 @@ export const mutations = {
oldTimeline.visibleStatusesObject = {}
each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })
},
- setNotificationFetcher (state, { fetcherId }) {
- state.notifications.fetcherId = fetcherId
- },
resetStatuses (state) {
const emptyState = defaultState()
Object.entries(emptyState).forEach(([key, value]) => {
@@ -352,6 +397,9 @@ export const mutations = {
clearTimeline (state, { timeline }) {
state.timelines[timeline] = emptyTl(state.timelines[timeline].userId)
},
+ clearNotifications (state) {
+ state.notifications = emptyNotifications()
+ },
setFavorited (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
newStatus.favorited = value
@@ -378,6 +426,13 @@ export const mutations = {
const newStatus = state.allStatusesObject[status.id]
newStatus.deleted = true
},
+ setManyDeleted (state, condition) {
+ Object.values(state.allStatusesObject).forEach(status => {
+ if (condition(status)) {
+ status.deleted = true
+ }
+ })
+ },
setLoading (state, { timeline, value }) {
state.timelines[timeline].loading = value
},
@@ -413,8 +468,8 @@ const statuses = {
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })
},
- addNewNotifications ({ rootState, commit, dispatch }, { notifications, older }) {
- commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older })
+ addNewNotifications ({ rootState, commit, dispatch, rootGetters }, { notifications, older }) {
+ commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older, rootGetters })
},
setError ({ rootState, commit }, { value }) {
commit('setError', { value })
@@ -428,16 +483,13 @@ 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 })
},
+ markStatusesAsDeleted ({ commit }, condition) {
+ commit('setManyDeleted', condition)
+ },
favorite ({ rootState, commit }, status) {
// Optimistic favoriting...
commit('setFavorited', { status, value: true })
diff --git a/src/modules/users.js b/src/modules/users.js
index 1a507d31..adcab233 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -1,5 +1,6 @@
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
-import { compact, map, each, merge, find, last } from 'lodash'
+import userSearchApi from '../services/new_api/user_search.js'
+import { compact, map, each, merge, last, concat, uniq } from 'lodash'
import { set } from 'vue'
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
import oauthApi from '../services/new_api/oauth'
@@ -32,11 +33,62 @@ const getNotificationPermission = () => {
return Promise.resolve(Notification.permission)
}
+const blockUser = (store, id) => {
+ return store.rootState.api.backendInteractor.blockUser(id)
+ .then((relationship) => {
+ store.commit('updateUserRelationship', [relationship])
+ store.commit('addBlockId', id)
+ store.commit('removeStatus', { timeline: 'friends', userId: id })
+ store.commit('removeStatus', { timeline: 'public', userId: id })
+ store.commit('removeStatus', { timeline: 'publicAndExternal', userId: id })
+ })
+}
+
+const unblockUser = (store, id) => {
+ return store.rootState.api.backendInteractor.unblockUser(id)
+ .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+}
+
+const muteUser = (store, id) => {
+ return store.rootState.api.backendInteractor.muteUser(id)
+ .then((relationship) => {
+ store.commit('updateUserRelationship', [relationship])
+ store.commit('addMuteId', id)
+ })
+}
+
+const unmuteUser = (store, id) => {
+ return store.rootState.api.backendInteractor.unmuteUser(id)
+ .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+}
+
export const mutations = {
setMuted (state, { user: { id }, muted }) {
const user = state.usersObject[id]
set(user, 'muted', muted)
},
+ tagUser (state, { user: { id }, tag }) {
+ const user = state.usersObject[id]
+ const tags = user.tags || []
+ const newTags = tags.concat([tag])
+ set(user, 'tags', newTags)
+ },
+ untagUser (state, { user: { id }, tag }) {
+ const user = state.usersObject[id]
+ const tags = user.tags || []
+ const newTags = tags.filter(t => t !== tag)
+ set(user, 'tags', newTags)
+ },
+ updateRight (state, { user: { id }, right, value }) {
+ const user = state.usersObject[id]
+ let newRights = user.rights
+ newRights[right] = value
+ set(user, 'rights', newRights)
+ },
+ updateActivationStatus (state, { user: { id }, status }) {
+ const user = state.usersObject[id]
+ set(user, 'deactivated', !status)
+ },
setCurrentUser (state, user) {
state.lastLoginName = user.screen_name
state.currentUser = merge(state.currentUser || {}, user)
@@ -51,42 +103,27 @@ export const mutations = {
endLogin (state) {
state.loggingIn = false
},
- // TODO Clean after ourselves?
- addFriends (state, { id, friends }) {
+ saveFriendIds (state, { id, friendIds }) {
const user = state.usersObject[id]
- each(friends, friend => {
- if (!find(user.friends, { id: friend.id })) {
- user.friends.push(friend)
- }
- })
- user.lastFriendId = last(friends).id
+ user.friendIds = uniq(concat(user.friendIds, friendIds))
},
- addFollowers (state, { id, followers }) {
+ saveFollowerIds (state, { id, followerIds }) {
const user = state.usersObject[id]
- each(followers, follower => {
- if (!find(user.followers, { id: follower.id })) {
- user.followers.push(follower)
- }
- })
- user.lastFollowerId = last(followers).id
+ user.followerIds = uniq(concat(user.followerIds, followerIds))
},
// Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile.
clearFriends (state, userId) {
const user = state.usersObject[userId]
- if (!user) {
- return
+ if (user) {
+ set(user, 'friendIds', [])
}
- user.friends = []
- user.lastFriendId = null
},
clearFollowers (state, userId) {
const user = state.usersObject[userId]
- if (!user) {
- return
+ if (user) {
+ set(user, 'followerIds', [])
}
- user.followers = []
- user.lastFollowerId = null
},
addNewUsers (state, users) {
each(users, (user) => mergeOrAdd(state.users, state.usersObject, user))
@@ -110,6 +147,11 @@ export const mutations = {
saveBlockIds (state, blockIds) {
state.currentUser.blockIds = blockIds
},
+ addBlockId (state, blockId) {
+ if (state.currentUser.blockIds.indexOf(blockId) === -1) {
+ state.currentUser.blockIds.push(blockId)
+ }
+ },
updateMutes (state, mutedUsers) {
// Reset muted of all fetched users
each(state.users, (user) => { user.muted = false })
@@ -118,12 +160,19 @@ export const mutations = {
saveMuteIds (state, muteIds) {
state.currentUser.muteIds = muteIds
},
+ addMuteId (state, muteId) {
+ if (state.currentUser.muteIds.indexOf(muteId) === -1) {
+ state.currentUser.muteIds.push(muteId)
+ }
+ },
setUserForStatus (state, status) {
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]
+ if (notification.type !== 'follow') {
+ notification.action.user = state.usersObject[notification.action.user.id]
+ }
+ notification.from_profile = state.usersObject[notification.from_profile.id]
},
setColor (state, { user: { id }, highlighted }) {
const user = state.usersObject[id]
@@ -176,8 +225,10 @@ const users = {
})
},
fetchUserRelationship (store, id) {
- return store.rootState.api.backendInteractor.fetchUserRelationship({ id })
- .then((relationships) => store.commit('updateUserRelationship', relationships))
+ if (store.state.currentUser) {
+ store.rootState.api.backendInteractor.fetchUserRelationship({ id })
+ .then((relationships) => store.commit('updateUserRelationship', relationships))
+ }
},
fetchBlocks (store) {
return store.rootState.api.backendInteractor.fetchBlocks()
@@ -187,18 +238,17 @@ const users = {
return blocks
})
},
- blockUser (store, userId) {
- return store.rootState.api.backendInteractor.blockUser(userId)
- .then((relationship) => {
- store.commit('updateUserRelationship', [relationship])
- store.commit('removeStatus', { timeline: 'friends', userId })
- store.commit('removeStatus', { timeline: 'public', userId })
- store.commit('removeStatus', { timeline: 'publicAndExternal', userId })
- })
+ blockUser (store, id) {
+ return blockUser(store, id)
},
unblockUser (store, id) {
- return store.rootState.api.backendInteractor.unblockUser(id)
- .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+ return unblockUser(store, id)
+ },
+ blockUsers (store, ids = []) {
+ return Promise.all(ids.map(id => blockUser(store, id)))
+ },
+ unblockUsers (store, ids = []) {
+ return Promise.all(ids.map(id => unblockUser(store, id)))
},
fetchMutes (store) {
return store.rootState.api.backendInteractor.fetchMutes()
@@ -209,32 +259,34 @@ const users = {
})
},
muteUser (store, id) {
- return store.rootState.api.backendInteractor.muteUser(id)
- .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+ return muteUser(store, id)
},
unmuteUser (store, id) {
- return store.rootState.api.backendInteractor.unmuteUser(id)
- .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+ return unmuteUser(store, id)
},
- addFriends ({ rootState, commit }, fetchBy) {
- return new Promise((resolve, reject) => {
- const user = rootState.users.usersObject[fetchBy]
- const maxId = user.lastFriendId
- rootState.api.backendInteractor.fetchFriends({ id: user.id, maxId })
- .then((friends) => {
- commit('addFriends', { id: user.id, friends })
- resolve(friends)
- }).catch(() => {
- reject()
- })
- })
+ muteUsers (store, ids = []) {
+ return Promise.all(ids.map(id => muteUser(store, id)))
+ },
+ unmuteUsers (store, ids = []) {
+ return Promise.all(ids.map(id => unmuteUser(store, id)))
+ },
+ fetchFriends ({ rootState, commit }, id) {
+ const user = rootState.users.usersObject[id]
+ const maxId = last(user.friendIds)
+ return rootState.api.backendInteractor.fetchFriends({ id, maxId })
+ .then((friends) => {
+ commit('addNewUsers', friends)
+ commit('saveFriendIds', { id, friendIds: map(friends, 'id') })
+ return friends
+ })
},
- addFollowers ({ rootState, commit }, fetchBy) {
- const user = rootState.users.usersObject[fetchBy]
- const maxId = user.lastFollowerId
- return rootState.api.backendInteractor.fetchFollowers({ id: user.id, maxId })
+ fetchFollowers ({ rootState, commit }, id) {
+ const user = rootState.users.usersObject[id]
+ const maxId = last(user.followerIds)
+ return rootState.api.backendInteractor.fetchFollowers({ id, maxId })
.then((followers) => {
- commit('addFollowers', { id: user.id, followers })
+ commit('addNewUsers', followers)
+ commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })
return followers
})
},
@@ -257,6 +309,9 @@ const users = {
unregisterPushNotifications(token)
},
+ addNewUsers ({ commit }, users) {
+ commit('addNewUsers', users)
+ },
addNewStatuses (store, { statuses }) {
const users = map(statuses, 'user')
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
@@ -287,6 +342,14 @@ const users = {
store.commit('setUserForNotification', notification)
})
},
+ searchUsers (store, query) {
+ // TODO: Move userSearch api into api.service
+ return userSearchApi.search({query, store: { state: store.rootState }})
+ .then((users) => {
+ store.commit('addNewUsers', users)
+ return users
+ })
+ },
async signUp (store, userInfo) {
store.commit('signUpPending')
@@ -331,7 +394,8 @@ const users = {
store.commit('setToken', false)
store.dispatch('stopFetching', 'friends')
store.commit('setBackendInteractor', backendInteractorService())
- store.dispatch('stopFetchingNotifications')
+ store.dispatch('stopFetching', 'notifications')
+ store.commit('clearNotifications')
store.commit('resetStatuses')
},
loginUser (store, accessToken) {
@@ -363,7 +427,10 @@ const users = {
}
// Start getting fresh posts.
- store.dispatch('startFetching', { timeline: 'friends' })
+ store.dispatch('startFetchingTimeline', { timeline: 'friends' })
+
+ // Start fetching notifications
+ store.dispatch('startFetchingNotifications')
// Get user mutes
store.dispatch('fetchMutes')