aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorHenry Jameson <me@hjkos.com>2019-08-31 22:38:02 +0300
committerHenry Jameson <me@hjkos.com>2019-08-31 22:38:02 +0300
commit18ec13d796c0928d09fa93de4117822d2e35502c (patch)
tree1cfb4d68a246c604396bb64bbba3e69bdf4fe511 /src/modules
parentb3e9a5a71819c7d3a4b35c5b6ad551785a7ba44f (diff)
parent018a650166a5dce0878b696359a999ab67634cfc (diff)
Merge remote-tracking branch 'upstream/develop' into docs
* upstream/develop: (193 commits) fix user avatar fallback logic remove dead code make bio textarea resizable vertically only remove dead code remove dead code fix crazy watch logic in conversation show three dot button only if needed hide mute conversation button to guests update keyBy generate idObj at timeline level fix pin showing logic in conversation Show a message when JS is disabled Initialize chat only if user is logged in and it wasn't initialized before i18n/Update Japanese i18n/Update pedantic Japanese sync profile tab state with location query refactor TabSwitcher use better name of controlled prop fix potential bug to render active tab in controlled way remove unused param ...
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'))
+ })
})
}
}