diff options
Diffstat (limited to 'src/modules')
| -rw-r--r-- | src/modules/api.js | 21 | ||||
| -rw-r--r-- | src/modules/auth_flow.js | 5 | ||||
| -rw-r--r-- | src/modules/chat.js | 12 | ||||
| -rw-r--r-- | src/modules/config.js | 4 | ||||
| -rw-r--r-- | src/modules/errors.js | 1 | ||||
| -rw-r--r-- | src/modules/instance.js | 18 | ||||
| -rw-r--r-- | src/modules/oauth.js | 8 | ||||
| -rw-r--r-- | src/modules/oauth_tokens.js | 4 | ||||
| -rw-r--r-- | src/modules/polls.js | 70 | ||||
| -rw-r--r-- | src/modules/statuses.js | 70 | ||||
| -rw-r--r-- | src/modules/users.js | 87 |
11 files changed, 222 insertions, 78 deletions
diff --git a/src/modules/api.js b/src/modules/api.js index 7ed3edac..eb6a7980 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -6,7 +6,6 @@ const api = { backendInteractor: backendInteractorService(), fetchers: {}, socket: null, - chatDisabled: false, followRequests: [] }, mutations: { @@ -25,9 +24,6 @@ const api = { setSocket (state, socket) { state.socket = socket }, - setChatDisabled (state, value) { - state.chatDisabled = value - }, setFollowRequests (state, value) { state.followRequests = value } @@ -55,17 +51,20 @@ const api = { setWsToken (store, token) { store.commit('setWsToken', token) }, - initializeSocket (store) { + initializeSocket ({ dispatch, commit, state, rootState }) { // Set up websocket connection - if (!store.state.chatDisabled) { - const token = store.state.wsToken - const socket = new Socket('/socket', {params: {token}}) + const token = state.wsToken + if (rootState.instance.chatAvailable && typeof token !== 'undefined' && state.socket === null) { + const socket = new Socket('/socket', { params: { token } }) socket.connect() - store.dispatch('initializeChat', socket) + + commit('setSocket', socket) + dispatch('initializeChat', socket) } }, - disableChat (store) { - store.commit('setChatDisabled', true) + disconnectFromSocket ({ commit, state }) { + state.socket && state.socket.disconnect() + commit('setSocket', null) }, removeFollowRequest (store, request) { let requests = store.state.followRequests.filter((it) => it !== request) diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js index 86328cf3..d0a90feb 100644 --- a/src/modules/auth_flow.js +++ b/src/modules/auth_flow.js @@ -55,7 +55,7 @@ const mutations = { requireToken (state) { state.strategy = TOKEN_STRATEGY }, - requireMFA (state, {app, settings}) { + requireMFA (state, { app, settings }) { state.settings = settings state.app = app state.strategy = TOTP_STRATEGY // default strategy of MFA @@ -73,7 +73,8 @@ const mutations = { // actions const actions = { - async login ({state, dispatch, commit}, {access_token}) { + // eslint-disable-next-line camelcase + async login ({ state, dispatch, commit }, { access_token }) { commit('setToken', access_token, { root: true }) await dispatch('loginUser', access_token, { root: true }) resetState(state) diff --git a/src/modules/chat.js b/src/modules/chat.js index 2804e577..c798549d 100644 --- a/src/modules/chat.js +++ b/src/modules/chat.js @@ -1,16 +1,12 @@ const chat = { state: { messages: [], - channel: {state: ''}, - socket: null + channel: { state: '' } }, mutations: { setChannel (state, channel) { state.channel = channel }, - setSocket (state, socket) { - state.socket = socket - }, addMessage (state, message) { state.messages.push(message) state.messages = state.messages.slice(-19, 20) @@ -20,16 +16,12 @@ const chat = { } }, actions: { - disconnectFromChat (store) { - store.state.socket.disconnect() - }, initializeChat (store, socket) { const channel = socket.channel('chat:public') - store.commit('setSocket', socket) channel.on('new_msg', (msg) => { store.commit('addMessage', msg) }) - channel.on('messages', ({messages}) => { + channel.on('messages', ({ messages }) => { store.commit('setMessages', messages) }) channel.join() diff --git a/src/modules/config.js b/src/modules/config.js index a8da525a..2bfad8f6 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -56,10 +56,10 @@ const config = { }, actions: { setHighlight ({ commit, dispatch }, { user, color, type }) { - commit('setHighlight', {user, color, type}) + commit('setHighlight', { user, color, type }) }, setOption ({ commit, dispatch }, { name, value }) { - commit('setOption', {name, value}) + commit('setOption', { name, value }) switch (name) { case 'theme': setPreset(value, commit) diff --git a/src/modules/errors.js b/src/modules/errors.js index c809e1b5..ca89dc0f 100644 --- a/src/modules/errors.js +++ b/src/modules/errors.js @@ -9,4 +9,3 @@ export function humanizeErrors (errors) { return [...errs, message] }, []) } - diff --git a/src/modules/instance.js b/src/modules/instance.js index fc4578ed..7d602aa1 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -16,7 +16,6 @@ const defaultState = { redirectRootNoLogin: '/main/all', redirectRootLogin: '/main/friends', showInstanceSpecificPanel: false, - formattingOptionsEnabled: false, alwaysShowSubjectInput: true, hideMutedPosts: false, collapseMessageWithSubject: false, @@ -53,7 +52,15 @@ const defaultState = { // Version Information backendVersion: '', - frontendVersion: '' + frontendVersion: '', + + pollsAvailable: false, + pollLimits: { + max_options: 4, + max_option_chars: 255, + min_expiration: 60, + max_expiration: 60 * 60 * 24 + } } const instance = { @@ -67,11 +74,16 @@ const instance = { }, actions: { setInstanceOption ({ commit, dispatch }, { name, value }) { - commit('setInstanceOption', {name, value}) + commit('setInstanceOption', { name, value }) switch (name) { case 'name': dispatch('setPageTitle') break + case 'chatAvailable': + if (value) { + dispatch('initializeSocket') + } + break } }, setTheme ({ commit }, themeName) { diff --git a/src/modules/oauth.js b/src/modules/oauth.js index 11cb10fe..a2a83450 100644 --- a/src/modules/oauth.js +++ b/src/modules/oauth.js @@ -1,3 +1,5 @@ +import { delete as del } from 'vue' + const oauth = { state: { clientId: false, @@ -22,6 +24,12 @@ const oauth = { }, setToken (state, token) { state.userToken = token + }, + clearToken (state) { + state.userToken = false + // state.token is userToken with older name, coming from persistent state + // let's clear it as well, since it is being used as a fallback of state.userToken + del(state, 'token') } }, getters: { diff --git a/src/modules/oauth_tokens.js b/src/modules/oauth_tokens.js index 00ac1431..0159a3f1 100644 --- a/src/modules/oauth_tokens.js +++ b/src/modules/oauth_tokens.js @@ -3,12 +3,12 @@ const oauthTokens = { tokens: [] }, actions: { - fetchTokens ({rootState, commit}) { + fetchTokens ({ rootState, commit }) { rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => { commit('swapTokens', tokens) }) }, - revokeToken ({rootState, commit, state}, id) { + revokeToken ({ rootState, commit, state }, id) { rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => { if (response.status === 201) { commit('swapTokens', state.tokens.filter(token => token.id !== id)) diff --git a/src/modules/polls.js b/src/modules/polls.js new file mode 100644 index 00000000..e6158b63 --- /dev/null +++ b/src/modules/polls.js @@ -0,0 +1,70 @@ +import { merge } from 'lodash' +import { set } from 'vue' + +const polls = { + state: { + // Contains key = id, value = number of trackers for this poll + trackedPolls: {}, + pollsObject: {} + }, + mutations: { + mergeOrAddPoll (state, poll) { + const existingPoll = state.pollsObject[poll.id] + // Make expired-state change trigger re-renders properly + poll.expired = Date.now() > Date.parse(poll.expires_at) + if (existingPoll) { + set(state.pollsObject, poll.id, merge(existingPoll, poll)) + } else { + set(state.pollsObject, poll.id, poll) + } + }, + trackPoll (state, pollId) { + const currentValue = state.trackedPolls[pollId] + if (currentValue) { + set(state.trackedPolls, pollId, currentValue + 1) + } else { + set(state.trackedPolls, pollId, 1) + } + }, + untrackPoll (state, pollId) { + const currentValue = state.trackedPolls[pollId] + if (currentValue) { + set(state.trackedPolls, pollId, currentValue - 1) + } else { + set(state.trackedPolls, pollId, 0) + } + } + }, + actions: { + mergeOrAddPoll ({ commit }, poll) { + commit('mergeOrAddPoll', poll) + }, + updateTrackedPoll ({ rootState, dispatch, commit }, pollId) { + rootState.api.backendInteractor.fetchPoll(pollId).then(poll => { + setTimeout(() => { + if (rootState.polls.trackedPolls[pollId]) { + dispatch('updateTrackedPoll', pollId) + } + }, 30 * 1000) + commit('mergeOrAddPoll', poll) + }) + }, + trackPoll ({ rootState, commit, dispatch }, pollId) { + if (!rootState.polls.trackedPolls[pollId]) { + setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000) + } + commit('trackPoll', pollId) + }, + untrackPoll ({ commit }, pollId) { + commit('untrackPoll', pollId) + }, + votePoll ({ rootState, commit }, { id, pollId, choices }) { + return rootState.api.backendInteractor.vote(pollId, choices).then(poll => { + commit('mergeOrAddPoll', poll) + return poll + }) + } + } +} + +export default polls diff --git a/src/modules/statuses.js b/src/modules/statuses.js index e6ee5447..e863d8a5 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -80,13 +80,13 @@ const mergeOrAdd = (arr, obj, item) => { merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user')) // Reactivity fix. oldItem.attachments.splice(oldItem.attachments.length) - return {item: oldItem, new: false} + return { item: oldItem, new: false } } else { // This is a new item, prepare it prepareStatus(item) arr.push(item) set(obj, item.id, item) - return {item, new: true} + return { item, new: true } } } @@ -137,7 +137,7 @@ const removeStatusFromGlobalStorage = (state, status) => { // TODO: Need to remove from allStatusesObject? // Remove possible notification - remove(state.notifications.data, ({action: {id}}) => id === status.id) + remove(state.notifications.data, ({ action: { id } }) => id === status.id) // Remove from conversation const conversationId = status.statusnet_conversation_id @@ -146,7 +146,8 @@ const removeStatusFromGlobalStorage = (state, status) => { } } -const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId }) => { +const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, + noIdUpdate = false, userId }) => { // Sanity check if (!isArray(statuses)) { return false @@ -269,7 +270,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us }, 'deletion': (deletion) => { const uri = deletion.uri - const status = find(allStatuses, {uri}) + const status = find(allStatuses, { uri }) if (!status) { return } @@ -394,8 +395,9 @@ export const mutations = { state[key] = value }) }, - clearTimeline (state, { timeline }) { - state.timelines[timeline] = emptyTl(state.timelines[timeline].userId) + clearTimeline (state, { timeline, excludeUserId = false }) { + const userId = excludeUserId ? state.timelines[timeline].userId : undefined + state.timelines[timeline] = emptyTl(userId) }, clearNotifications (state) { state.notifications = emptyNotifications() @@ -428,6 +430,10 @@ export const mutations = { const newStatus = state.allStatusesObject[status.id] newStatus.pinned = status.pinned }, + setMuted (state, status) { + const newStatus = state.allStatusesObject[status.id] + newStatus.muted = status.muted + }, setRetweeted (state, { status, value }) { const newStatus = state.allStatusesObject[status.id] @@ -490,10 +496,23 @@ export const mutations = { queueFlush (state, { timeline, id }) { state.timelines[timeline].flushMarker = id }, - addFavsAndRepeats (state, { id, favoritedByUsers, rebloggedByUsers }) { + addRepeats (state, { id, rebloggedByUsers, currentUser }) { const newStatus = state.allStatusesObject[id] - newStatus.favoritedBy = favoritedByUsers.filter(_ => _) newStatus.rebloggedBy = rebloggedByUsers.filter(_ => _) + // repeats stats can be incorrect based on polling condition, let's update them using the most recent data + newStatus.repeat_num = newStatus.rebloggedBy.length + newStatus.repeated = !!newStatus.rebloggedBy.find(({ id }) => currentUser.id === id) + }, + addFavs (state, { id, favoritedByUsers, currentUser }) { + const newStatus = state.allStatusesObject[id] + newStatus.favoritedBy = favoritedByUsers.filter(_ => _) + // favorites stats can be incorrect based on polling condition, let's update them using the most recent data + newStatus.fave_num = newStatus.favoritedBy.length + newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id) + }, + updateStatusWithPoll (state, { id, poll }) { + const status = state.allStatusesObject[id] + status.poll = poll } } @@ -539,7 +558,7 @@ const statuses = { }, fetchPinnedStatuses ({ rootState, dispatch }, userId) { rootState.api.backendInteractor.fetchPinnedStatuses(userId) - .then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true })) + .then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true })) }, pinStatus ({ rootState, commit }, statusId) { return rootState.api.backendInteractor.pinOwnStatus(statusId) @@ -549,6 +568,14 @@ const statuses = { rootState.api.backendInteractor.unpinOwnStatus(statusId) .then((status) => commit('setPinned', status)) }, + muteConversation ({ rootState, commit }, statusId) { + return rootState.api.backendInteractor.muteConversation(statusId) + .then((status) => commit('setMuted', status)) + }, + unmuteConversation ({ rootState, commit }, statusId) { + return rootState.api.backendInteractor.unmuteConversation(statusId) + .then((status) => commit('setMuted', status)) + }, retweet ({ rootState, commit }, status) { // Optimistic retweeting... commit('setRetweeted', { status, value: true }) @@ -575,9 +602,26 @@ const statuses = { Promise.all([ rootState.api.backendInteractor.fetchFavoritedByUsers(id), rootState.api.backendInteractor.fetchRebloggedByUsers(id) - ]).then(([favoritedByUsers, rebloggedByUsers]) => - commit('addFavsAndRepeats', { id, favoritedByUsers, rebloggedByUsers }) - ) + ]).then(([favoritedByUsers, rebloggedByUsers]) => { + commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }) + commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }) + }) + }, + fetchFavs ({ rootState, commit }, id) { + rootState.api.backendInteractor.fetchFavoritedByUsers(id) + .then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })) + }, + fetchRepeats ({ rootState, commit }, id) { + rootState.api.backendInteractor.fetchRebloggedByUsers(id) + .then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })) + }, + search (store, { q, resolve, limit, offset, following }) { + return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following }) + .then((data) => { + store.commit('addNewUsers', data.accounts) + store.commit('addNewStatuses', { statuses: data.statuses }) + return data + }) } }, mutations diff --git a/src/modules/users.js b/src/modules/users.js index 739b8b92..4aebe04b 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,9 +1,8 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' -import userSearchApi from '../services/new_api/user_search.js' +import oauthApi from '../services/new_api/oauth.js' import { compact, map, each, merge, last, concat, uniq } from 'lodash' import { set } from 'vue' import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' -import { humanizeErrors } from './errors' // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { @@ -135,6 +134,7 @@ export const mutations = { user.following = relationship.following user.muted = relationship.muting user.statusnet_blocking = relationship.blocking + user.subscribed = relationship.subscribing } }) }, @@ -166,11 +166,11 @@ export const mutations = { }, setPinned (state, status) { const user = state.usersObject[status.user.id] - const index = user.pinnedStatuseIds.indexOf(status.id) + const index = user.pinnedStatusIds.indexOf(status.id) if (status.pinned && index === -1) { - user.pinnedStatuseIds.push(status.id) + user.pinnedStatusIds.push(status.id) } else if (!status.pinned && index !== -1) { - user.pinnedStatuseIds.splice(index, 1) + user.pinnedStatusIds.splice(index, 1) } }, setUserForStatus (state, status) { @@ -304,6 +304,14 @@ const users = { clearFollowers ({ commit }, userId) { commit('clearFollowers', userId) }, + subscribeUser ({ rootState, commit }, id) { + return rootState.api.backendInteractor.subscribeUser(id) + .then((relationship) => commit('updateUserRelationship', [relationship])) + }, + unsubscribeUser ({ rootState, commit }, id) { + return rootState.api.backendInteractor.unsubscribeUser(id) + .then((relationship) => commit('updateUserRelationship', [relationship])) + }, registerPushNotifications (store) { const token = store.state.currentUser.credentials const vapidPublicKey = store.rootState.instance.vapidPublicKey @@ -346,8 +354,8 @@ const users = { const notificationsObject = store.rootState.statuses.notifications.idStore const relevantNotifications = Object.entries(notificationsObject) - .filter(([k, val]) => notificationIds.includes(k)) - .map(([k, val]) => val) + .filter(([k, val]) => notificationIds.includes(k)) + .map(([k, val]) => val) // Reconnect users to notifications each(relevantNotifications, (notification) => { @@ -355,8 +363,7 @@ const users = { }) }, searchUsers (store, query) { - // TODO: Move userSearch api into api.service - return userSearchApi.search({query, store: { state: store.rootState }}) + return store.rootState.api.backendInteractor.searchUsers(query) .then((users) => { store.commit('addNewUsers', users) return users @@ -374,31 +381,43 @@ const users = { store.dispatch('loginUser', data.access_token) } catch (e) { let errors = e.message - // replace ap_id with username - if (typeof errors === 'object') { - if (errors.ap_id) { - errors.username = errors.ap_id - delete errors.ap_id - } - errors = humanizeErrors(errors) - } store.commit('signUpFailure', errors) - throw Error(errors) + throw e } }, async getCaptcha (store) { - return await store.rootState.api.backendInteractor.getCaptcha() + return store.rootState.api.backendInteractor.getCaptcha() }, logout (store) { - store.commit('clearCurrentUser') - store.dispatch('disconnectFromChat') - store.commit('setToken', false) - store.dispatch('stopFetching', 'friends') - store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) - store.dispatch('stopFetching', 'notifications') - store.commit('clearNotifications') - store.commit('resetStatuses') + const { oauth, instance } = store.rootState + + const data = { + ...oauth, + commit: store.commit, + instance: instance.server + } + + return oauthApi.getOrCreateApp(data) + .then((app) => { + const params = { + app, + instance: data.instance, + token: oauth.userToken + } + + return oauthApi.revokeToken(params) + }) + .then(() => { + store.commit('clearCurrentUser') + store.dispatch('disconnectFromSocket') + store.commit('clearToken') + store.dispatch('stopFetching', 'friends') + store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) + store.dispatch('stopFetching', 'notifications') + store.commit('clearNotifications') + store.commit('resetStatuses') + }) }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { @@ -445,19 +464,19 @@ const users = { // Authentication failed commit('endLogin') if (response.status === 401) { - reject('Wrong username or password') + reject(new Error('Wrong username or password')) } else { - reject('An error occurred, please try again') + reject(new Error('An error occurred, please try again')) } } commit('endLogin') resolve() }) - .catch((error) => { - console.log(error) - commit('endLogin') - reject('Failed to connect to server, try again') - }) + .catch((error) => { + console.log(error) + commit('endLogin') + reject(new Error('Failed to connect to server, try again')) + }) }) } } |
