aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/api.js21
-rw-r--r--src/modules/auth_flow.js5
-rw-r--r--src/modules/chat.js12
-rw-r--r--src/modules/config.js4
-rw-r--r--src/modules/errors.js1
-rw-r--r--src/modules/instance.js18
-rw-r--r--src/modules/oauth.js8
-rw-r--r--src/modules/oauth_tokens.js4
-rw-r--r--src/modules/polls.js70
-rw-r--r--src/modules/statuses.js70
-rw-r--r--src/modules/users.js87
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'))
+ })
})
}
}