aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/api.js13
-rw-r--r--src/modules/config.js47
-rw-r--r--src/modules/errors.js12
-rw-r--r--src/modules/instance.js69
-rw-r--r--src/modules/interface.js49
-rw-r--r--src/modules/oauth.js18
-rw-r--r--src/modules/statuses.js225
-rw-r--r--src/modules/users.js101
8 files changed, 446 insertions, 88 deletions
diff --git a/src/modules/api.js b/src/modules/api.js
index c91fb97b..2f07a91e 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -7,7 +7,8 @@ const api = {
backendInteractor: backendInteractorService(),
fetchers: {},
socket: null,
- chatDisabled: false
+ chatDisabled: false,
+ followRequests: []
},
mutations: {
setBackendInteractor (state, backendInteractor) {
@@ -24,6 +25,9 @@ const api = {
},
setChatDisabled (state, value) {
state.chatDisabled = value
+ },
+ setFollowRequests (state, value) {
+ state.followRequests = value
}
},
actions: {
@@ -42,6 +46,9 @@ const api = {
store.commit('addFetcher', {timeline, fetcher})
}
},
+ fetchOldPost (store, { postId }) {
+ store.state.backendInteractor.fetchOldPost({ store, postId })
+ },
stopFetching (store, timeline) {
const fetcher = store.state.fetchers[timeline]
window.clearInterval(fetcher)
@@ -57,6 +64,10 @@ const api = {
},
disableChat (store) {
store.commit('setChatDisabled', true)
+ },
+ removeFollowRequest (store, request) {
+ let requests = store.state.followRequests.filter((it) => it !== request)
+ store.commit('setFollowRequests', requests)
}
}
}
diff --git a/src/modules/config.js b/src/modules/config.js
index 9a62905e..ccfd0190 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -1,16 +1,36 @@
-import { set } from 'vue'
-import StyleSetter from '../services/style_setter/style_setter.js'
+import { set, delete as del } from 'vue'
+import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
+
+const browserLocale = (window.navigator.language || 'en').split('-')[0]
const defaultState = {
- name: 'Pleroma FE',
colors: {},
+ collapseMessageWithSubject: undefined, // instance default
hideAttachments: false,
hideAttachmentsInConv: false,
hideNsfw: true,
+ preloadImage: true,
+ loopVideo: true,
+ loopVideoSilentOnly: true,
autoLoad: true,
streaming: false,
hoverPreview: true,
- muteWords: []
+ pauseOnUnfocused: true,
+ stopGifs: false,
+ replyVisibility: 'all',
+ notificationVisibility: {
+ follows: true,
+ mentions: true,
+ likes: true,
+ repeats: true
+ },
+ webPushNotifications: true,
+ muteWords: [],
+ highlight: {},
+ interfaceLanguage: browserLocale,
+ scopeCopy: undefined, // instance default
+ subjectLineBehavior: undefined, // instance default
+ alwaysShowSubjectInput: undefined // instance default
}
const config = {
@@ -18,23 +38,28 @@ const config = {
mutations: {
setOption (state, { name, value }) {
set(state, name, value)
+ },
+ setHighlight (state, { user, color, type }) {
+ const data = this.state.config.highlight[user]
+ if (color || type) {
+ set(state.highlight, user, { color: color || data.color, type: type || data.type })
+ } else {
+ del(state.highlight, user)
+ }
}
},
actions: {
- setPageTitle ({state}, option = '') {
- document.title = `${option} ${state.name}`
+ setHighlight ({ commit, dispatch }, { user, color, type }) {
+ commit('setHighlight', {user, color, type})
},
setOption ({ commit, dispatch }, { name, value }) {
commit('setOption', {name, value})
switch (name) {
- case 'name':
- dispatch('setPageTitle')
- break
case 'theme':
- StyleSetter.setPreset(value, commit)
+ setPreset(value, commit)
break
case 'customTheme':
- StyleSetter.setColors(value, commit)
+ applyTheme(value, commit)
}
}
}
diff --git a/src/modules/errors.js b/src/modules/errors.js
new file mode 100644
index 00000000..c809e1b5
--- /dev/null
+++ b/src/modules/errors.js
@@ -0,0 +1,12 @@
+import { capitalize } from 'lodash'
+
+export function humanizeErrors (errors) {
+ return Object.entries(errors).reduce((errs, [k, val]) => {
+ let message = val.reduce((acc, message) => {
+ let key = capitalize(k.replace(/_/g, ' '))
+ return acc + [key, message].join(' ') + '. '
+ }, '')
+ return [...errs, message]
+ }, [])
+}
+
diff --git a/src/modules/instance.js b/src/modules/instance.js
new file mode 100644
index 00000000..7c27d52a
--- /dev/null
+++ b/src/modules/instance.js
@@ -0,0 +1,69 @@
+import { set } from 'vue'
+import { setPreset } from '../services/style_setter/style_setter.js'
+
+const defaultState = {
+ // Stuff from static/config.json and apiConfig
+ name: 'Pleroma FE',
+ registrationOpen: true,
+ textlimit: 5000,
+ server: 'http://localhost:4040/',
+ theme: 'pleroma-dark',
+ background: '/static/aurora_borealis.jpg',
+ logo: '/static/logo.png',
+ logoMask: true,
+ logoMargin: '.2em',
+ redirectRootNoLogin: '/main/all',
+ redirectRootLogin: '/main/friends',
+ showInstanceSpecificPanel: false,
+ scopeOptionsEnabled: true,
+ formattingOptionsEnabled: false,
+ alwaysShowSubjectInput: true,
+ collapseMessageWithSubject: false,
+ hidePostStats: false,
+ hideUserStats: false,
+ disableChat: false,
+ scopeCopy: true,
+ subjectLineBehavior: 'email',
+ loginMethod: 'password',
+
+ // Nasty stuff
+ pleromaBackend: true,
+ emoji: [],
+ customEmoji: [],
+
+ // Feature-set, apparently, not everything here is reported...
+ mediaProxyAvailable: false,
+ chatAvailable: false,
+ gopherAvailable: false,
+ suggestionsEnabled: false,
+ suggestionsWeb: '',
+
+ // Html stuff
+ instanceSpecificPanelContent: '',
+ tos: ''
+}
+
+const instance = {
+ state: defaultState,
+ mutations: {
+ setInstanceOption (state, { name, value }) {
+ if (typeof value !== 'undefined') {
+ set(state, name, value)
+ }
+ }
+ },
+ actions: {
+ setInstanceOption ({ commit, dispatch }, { name, value }) {
+ commit('setInstanceOption', {name, value})
+ switch (name) {
+ case 'name':
+ dispatch('setPageTitle')
+ break
+ case 'theme':
+ setPreset(value, commit)
+ }
+ }
+ }
+}
+
+export default instance
diff --git a/src/modules/interface.js b/src/modules/interface.js
new file mode 100644
index 00000000..956c9cb3
--- /dev/null
+++ b/src/modules/interface.js
@@ -0,0 +1,49 @@
+import { set, delete as del } from 'vue'
+
+const defaultState = {
+ settings: {
+ currentSaveStateNotice: null,
+ noticeClearTimeout: null,
+ notificationPermission: null
+ },
+ browserSupport: {
+ cssFilter: window.CSS && window.CSS.supports && (
+ window.CSS.supports('filter', 'drop-shadow(0 0)') ||
+ window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
+ )
+ }
+}
+
+const interfaceMod = {
+ state: defaultState,
+ mutations: {
+ settingsSaved (state, { success, error }) {
+ if (success) {
+ if (state.noticeClearTimeout) {
+ clearTimeout(state.noticeClearTimeout)
+ }
+ set(state.settings, 'currentSaveStateNotice', { error: false, data: success })
+ set(state.settings, 'noticeClearTimeout',
+ setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))
+ } else {
+ set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error })
+ }
+ },
+ setNotificationPermission (state, permission) {
+ state.notificationPermission = permission
+ }
+ },
+ actions: {
+ setPageTitle ({ rootState }, option = '') {
+ document.title = `${option} ${rootState.instance.name}`
+ },
+ settingsSaved ({ commit, dispatch }, { success, error }) {
+ commit('settingsSaved', { success, error })
+ },
+ setNotificationPermission ({ commit }, permission) {
+ commit('setNotificationPermission', permission)
+ }
+ }
+}
+
+export default interfaceMod
diff --git a/src/modules/oauth.js b/src/modules/oauth.js
new file mode 100644
index 00000000..144ff830
--- /dev/null
+++ b/src/modules/oauth.js
@@ -0,0 +1,18 @@
+const oauth = {
+ state: {
+ client_id: false,
+ client_secret: false,
+ token: false
+ },
+ mutations: {
+ setClientData (state, data) {
+ state.client_id = data.client_id
+ state.client_secret = data.client_secret
+ },
+ setToken (state, token) {
+ state.token = token
+ }
+ }
+}
+
+export default oauth
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index bd52f161..8cdd4e28 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -15,6 +15,7 @@ const emptyTl = () => ({
followers: [],
friends: [],
viewing: 'statuses',
+ userId: 0,
flushMarker: 0
})
@@ -22,16 +23,25 @@ export const defaultState = {
allStatuses: [],
allStatusesObject: {},
maxId: 0,
- notifications: [],
+ notifications: {
+ desktopNotificationSilence: true,
+ maxId: 0,
+ minId: Number.POSITIVE_INFINITY,
+ data: [],
+ error: false,
+ brokenFavorites: {}
+ },
favorites: new Set(),
error: false,
timelines: {
mentions: emptyTl(),
public: emptyTl(),
user: emptyTl(),
+ own: emptyTl(),
publicAndExternal: emptyTl(),
friends: emptyTl(),
- tag: emptyTl()
+ tag: emptyTl(),
+ dms: emptyTl()
}
}
@@ -45,7 +55,7 @@ export const prepareStatus = (status) => {
if (status.nsfw === undefined) {
status.nsfw = isNsfw(status)
if (status.retweeted_status) {
- status.retweeted_status.nsfw = status.nsfw
+ status.nsfw = status.retweeted_status.nsfw
}
}
@@ -58,6 +68,15 @@ export const prepareStatus = (status) => {
return status
}
+const visibleNotificationTypes = (rootState) => {
+ return [
+ rootState.config.notificationVisibility.likes && 'like',
+ rootState.config.notificationVisibility.mentions && 'mention',
+ rootState.config.notificationVisibility.repeats && 'repeat',
+ rootState.config.notificationVisibility.follows && 'follow'
+ ].filter(_ => _)
+}
+
export const statusType = (status) => {
if (status.is_post_verb) {
return 'status'
@@ -76,8 +95,7 @@ export const statusType = (status) => {
return 'deletion'
}
- // TODO change to status.activity_type === 'follow' when gs supports it
- if (status.text.match(/started following/)) {
+ if (status.text.match(/started following/) || status.activity_type === 'follow') {
return 'follow'
}
@@ -113,7 +131,7 @@ const sortTimeline = (timeline) => {
return timeline
}
-const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false }) => {
+const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId }) => {
// Sanity check
if (!isArray(statuses)) {
return false
@@ -130,15 +148,24 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
timelineObject.maxId = maxNew
}
+ // This makes sure that user timeline won't get data meant for other
+ // user. I.e. opening different user profiles makes request which could
+ // return data late after user already viewing different user profile
+ if (timeline === 'user' && timelineObject.userId !== userId) {
+ return
+ }
+
const addStatus = (status, showImmediately, addToTimeline = true) => {
const result = mergeOrAdd(allStatuses, allStatusesObject, status)
status = result.item
- if (result.new) {
- if (statusType(status) === 'retweet' && status.retweeted_status.user.id === user.id) {
- addNotification({ type: 'repeat', status: status.retweeted_status, action: status })
- }
+ const brokenFavorites = state.notifications.brokenFavorites[status.id] || []
+ brokenFavorites.forEach((fav) => {
+ fav.status = status
+ })
+ delete state.notifications.brokenFavorites[status.id]
+ if (result.new) {
// We are mentioned in a post
if (statusType(status) === 'status' && find(status.attentions, { id: user.id })) {
const mentions = state.timelines.mentions
@@ -150,10 +177,14 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
sortTimeline(mentions)
}
- // Don't add notification for self-mention
- if (status.user.id !== user.id) {
- addNotification({ type: 'mention', status, action: status })
- }
+ }
+ if (status.visibility === 'direct') {
+ const dms = state.timelines.dms
+
+ mergeOrAdd(dms.statuses, dms.statusesObject, status)
+ dms.newStatusCount += 1
+
+ sortTimeline(dms)
}
}
@@ -176,45 +207,14 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
return status
}
- const addNotification = ({type, status, action}) => {
- // Only add a new notification if we don't have one for the same action
- if (!find(state.notifications, (oldNotification) => oldNotification.action.id === action.id)) {
- state.notifications.push({ type, status, action, seen: false })
-
- if ('Notification' in window && window.Notification.permission === 'granted') {
- const title = action.user.name
- const result = {}
- result.icon = action.user.profile_image_url
- result.body = action.text // there's a problem that it doesn't put a space before links tho
-
- // 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/')) {
- result.image = action.attachments[0].url
- }
-
- let notification = new window.Notification(title, result)
-
- // Chrome is known for not closing notifications automatically
- // according to MDN, anyway.
- setTimeout(notification.close.bind(notification), 5000)
- }
- }
- }
-
- const favoriteStatus = (favorite) => {
+ const favoriteStatus = (favorite, counter) => {
const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) })
if (status) {
- status.fave_num += 1
-
// This is our favorite, so the relevant bit.
if (favorite.user.id === user.id) {
status.favorited = true
- }
-
- // Add a notification if the user's status is favorited
- if (status.user.id === user.id) {
- addNotification({type: 'favorite', status, action: favorite})
+ } else {
+ status.fave_num += 1
}
}
return status
@@ -248,18 +248,12 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
},
'favorite': (favorite) => {
// Only update if this is a new favorite.
+ // Ignore our own favorites because we get info about likes as response to like request
if (!state.favorites.has(favorite.id)) {
state.favorites.add(favorite.id)
favoriteStatus(favorite)
}
},
- 'follow': (status) => {
- let re = new RegExp(`started following ${user.name} \\(${user.statusnet_profile_url}\\)`)
- let repleroma = new RegExp(`started following ${user.screen_name}$`)
- if (status.text.match(re) || status.text.match(repleroma)) {
- addNotification({ type: 'follow', status: status, action: status })
- }
- },
'deletion': (deletion) => {
const uri = deletion.uri
@@ -269,7 +263,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
return
}
- remove(state.notifications, ({action: {id}}) => id === status.id)
+ remove(state.notifications.data, ({action: {id}}) => id === status.id)
remove(allStatuses, { uri })
if (timeline) {
@@ -298,8 +292,68 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
}
+const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => {
+ const allStatuses = state.allStatuses
+ const allStatusesObject = state.allStatusesObject
+ each(notifications, (notification) => {
+ const result = mergeOrAdd(allStatuses, allStatusesObject, notification.notice)
+ const action = result.item
+ // Only add a new notification if we don't have one for the same action
+ if (!find(state.notifications.data, (oldNotification) => oldNotification.action.id === action.id)) {
+ state.notifications.maxId = Math.max(notification.id, state.notifications.maxId)
+ state.notifications.minId = Math.min(notification.id, state.notifications.minId)
+
+ const fresh = !notification.is_seen
+ const status = notification.ntype === 'like'
+ ? find(allStatuses, { id: action.in_reply_to_status_id })
+ : action
+
+ const result = {
+ type: notification.ntype,
+ status,
+ action,
+ seen: !fresh
+ }
+
+ if (notification.ntype === 'like' && !status) {
+ let broken = state.notifications.brokenFavorites[action.in_reply_to_status_id]
+ if (broken) {
+ broken.push(result)
+ } else {
+ dispatch('fetchOldPost', { postId: action.in_reply_to_status_id })
+ broken = [ result ]
+ state.notifications.brokenFavorites[action.in_reply_to_status_id] = broken
+ }
+ }
+
+ state.notifications.data.push(result)
+
+ if ('Notification' in window && window.Notification.permission === 'granted') {
+ const title = action.user.name
+ const result = {}
+ result.icon = action.user.profile_image_url
+ result.body = action.text // there's a problem that it doesn't put a space before links tho
+
+ // 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/')) {
+ result.image = action.attachments[0].url
+ }
+
+ if (fresh && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.ntype)) {
+ let notification = new window.Notification(title, result)
+ // Chrome is known for not closing notifications automatically
+ // according to MDN, anyway.
+ setTimeout(notification.close.bind(notification), 5000)
+ }
+ }
+ }
+ })
+}
+
export const mutations = {
addNewStatuses,
+ addNewNotifications,
showNewStatuses (state, { timeline }) {
const oldTimeline = (state.timelines[timeline])
@@ -316,6 +370,11 @@ export const mutations = {
const newStatus = state.allStatusesObject[status.id]
newStatus.favorited = value
},
+ setFavoritedConfirm (state, { status }) {
+ const newStatus = state.allStatusesObject[status.id]
+ newStatus.favorited = status.favorited
+ newStatus.fave_num = status.fave_num
+ },
setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
newStatus.repeated = value
@@ -334,6 +393,12 @@ export const mutations = {
setError (state, { value }) {
state.error = value
},
+ setNotificationsError (state, { value }) {
+ state.notifications.error = value
+ },
+ setNotificationsSilence (state, { value }) {
+ state.notifications.desktopNotificationSilence = value
+ },
setProfileView (state, { v }) {
// load followers / friends only when needed
state.timelines['user'].viewing = v
@@ -344,8 +409,8 @@ export const mutations = {
addFollowers (state, { followers }) {
state.timelines['user'].followers = followers
},
- markNotificationsAsSeen (state, notifications) {
- each(notifications, (notification) => {
+ markNotificationsAsSeen (state) {
+ each(state.notifications.data, (notification) => {
notification.seen = true
})
},
@@ -357,12 +422,21 @@ export const mutations = {
const statuses = {
state: defaultState,
actions: {
- addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false }) {
- commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser })
+ 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 })
},
setError ({ rootState, commit }, { value }) {
commit('setError', { value })
},
+ setNotificationsError ({ rootState, commit }, { value }) {
+ commit('setNotificationsError', { value })
+ },
+ setNotificationsSilence ({ rootState, commit }, { value }) {
+ commit('setNotificationsSilence', { value })
+ },
addFriends ({ rootState, commit }, { friends }) {
commit('addFriends', { friends })
},
@@ -377,19 +451,50 @@ const statuses = {
// Optimistic favoriting...
commit('setFavorited', { status, value: true })
apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
+ .then(response => {
+ if (response.ok) {
+ return response.json()
+ } else {
+ return {}
+ }
+ })
+ .then(status => {
+ commit('setFavoritedConfirm', { status })
+ })
},
unfavorite ({ rootState, commit }, status) {
// Optimistic favoriting...
commit('setFavorited', { status, value: false })
apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
+ .then(response => {
+ if (response.ok) {
+ return response.json()
+ } else {
+ return {}
+ }
+ })
+ .then(status => {
+ commit('setFavoritedConfirm', { status })
+ })
},
retweet ({ rootState, commit }, status) {
// Optimistic retweeting...
commit('setRetweeted', { status, value: true })
apiService.retweet({ id: status.id, credentials: rootState.users.currentUser.credentials })
},
+ unretweet ({ rootState, commit }, status) {
+ commit('setRetweeted', { status, value: false })
+ apiService.unretweet({ id: status.id, credentials: rootState.users.currentUser.credentials })
+ },
queueFlush ({ rootState, commit }, { timeline, id }) {
commit('queueFlush', { timeline, id })
+ },
+ markNotificationsAsSeen ({ rootState, commit }) {
+ commit('markNotificationsAsSeen')
+ apiService.markNotificationsAsSeen({
+ id: rootState.statuses.notifications.maxId,
+ credentials: rootState.users.currentUser.credentials
+ })
}
},
mutations
diff --git a/src/modules/users.js b/src/modules/users.js
index 8303ecc1..25d1c81f 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -1,6 +1,9 @@
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { compact, map, each, merge } from 'lodash'
import { set } from 'vue'
+import registerPushNotifications from '../services/push/push.js'
+import oauthApi from '../services/new_api/oauth'
+import { humanizeErrors } from './errors'
// TODO: Unify with mergeOrAdd in statuses.js
export const mergeOrAdd = (arr, obj, item) => {
@@ -9,17 +12,28 @@ export const mergeOrAdd = (arr, obj, item) => {
if (oldItem) {
// We already have this, so only merge the new info.
merge(oldItem, item)
- return {item: oldItem, new: false}
+ return { item: oldItem, new: false }
} else {
// This is a new item, prepare it
arr.push(item)
obj[item.id] = item
- return {item, new: true}
+ if (item.screen_name && !item.screen_name.includes('@')) {
+ obj[item.screen_name] = item
+ }
+ return { item, new: true }
}
}
+const getNotificationPermission = () => {
+ const Notification = window.Notification
+
+ if (!Notification) return Promise.resolve(null)
+ if (Notification.permission === 'default') return Notification.requestPermission()
+ return Promise.resolve(Notification.permission)
+}
+
export const mutations = {
- setMuted (state, { user: {id}, muted }) {
+ setMuted (state, { user: { id }, muted }) {
const user = state.usersObject[id]
set(user, 'muted', muted)
},
@@ -42,15 +56,32 @@ export const mutations = {
},
setUserForStatus (state, status) {
status.user = state.usersObject[status.user.id]
+ },
+ setColor (state, { user: { id }, highlighted }) {
+ const user = state.usersObject[id]
+ set(user, 'highlight', highlighted)
+ },
+ signUpPending (state) {
+ state.signUpPending = true
+ state.signUpErrors = []
+ },
+ signUpSuccess (state) {
+ state.signUpPending = false
+ },
+ signUpFailure (state, errors) {
+ state.signUpPending = false
+ state.signUpErrors = errors
}
}
export const defaultState = {
+ loggingIn: false,
lastLoginName: false,
currentUser: false,
- loggingIn: false,
users: [],
- usersObject: {}
+ usersObject: {},
+ signUpPending: false,
+ signUpErrors: []
}
const users = {
@@ -58,8 +89,15 @@ const users = {
mutations,
actions: {
fetchUser (store, id) {
- store.rootState.api.backendInteractor.fetchUser({id})
- .then((user) => store.commit('addNewUsers', user))
+ store.rootState.api.backendInteractor.fetchUser({ id })
+ .then((user) => store.commit('addNewUsers', [user]))
+ },
+ registerPushNotifications (store) {
+ const token = store.state.currentUser.credentials
+ const vapidPublicKey = store.rootState.instance.vapidPublicKey
+ const isEnabled = store.rootState.config.webPushNotifications
+
+ registerPushNotifications(isEnabled, vapidPublicKey, token)
},
addNewStatuses (store, { statuses }) {
const users = map(statuses, 'user')
@@ -76,26 +114,59 @@ const users = {
store.commit('setUserForStatus', status)
})
},
+ async signUp (store, userInfo) {
+ store.commit('signUpPending')
+
+ let rootState = store.rootState
+
+ let response = await rootState.api.backendInteractor.register(userInfo)
+ if (response.ok) {
+ const data = {
+ oauth: rootState.oauth,
+ instance: rootState.instance.server
+ }
+ let app = await oauthApi.getOrCreateApp(data)
+ let result = await oauthApi.getTokenWithCredentials({
+ app,
+ instance: data.instance,
+ username: userInfo.username,
+ password: userInfo.password
+ })
+ store.commit('signUpSuccess')
+ store.commit('setToken', result.access_token)
+ store.dispatch('loginUser', result.access_token)
+ } else {
+ let data = await response.json()
+ let errors = humanizeErrors(JSON.parse(data.error))
+ store.commit('signUpFailure', errors)
+ throw Error(errors)
+ }
+ },
logout (store) {
store.commit('clearCurrentUser')
+ store.commit('setToken', false)
store.dispatch('stopFetching', 'friends')
store.commit('setBackendInteractor', backendInteractorService())
},
- loginUser (store, userCredentials) {
+ loginUser (store, accessToken) {
return new Promise((resolve, reject) => {
const commit = store.commit
commit('beginLogin')
- store.rootState.api.backendInteractor.verifyCredentials(userCredentials)
+ store.rootState.api.backendInteractor.verifyCredentials(accessToken)
.then((response) => {
if (response.ok) {
response.json()
.then((user) => {
- user.credentials = userCredentials
+ // user.credentials = userCredentials
+ user.credentials = accessToken
commit('setCurrentUser', user)
commit('addNewUsers', [user])
+ getNotificationPermission()
+ .then(permission => commit('setNotificationPermission', permission))
+
// Set our new backend interactor
- commit('setBackendInteractor', backendInteractorService(userCredentials))
+ commit('setBackendInteractor', backendInteractorService(accessToken))
if (user.token) {
store.dispatch('initializeSocket', user.token)
@@ -103,6 +174,8 @@ const users = {
// Start getting fresh tweets.
store.dispatch('startFetching', 'friends')
+ // Start getting our own posts, only really needed for mitigating broken favorites
+ store.dispatch('startFetching', ['own', user.id])
// Get user mutes and follower info
store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => {
@@ -110,12 +183,8 @@ const users = {
store.commit('addNewUsers', mutedUsers)
})
- if ('Notification' in window && window.Notification.permission === 'default') {
- window.Notification.requestPermission()
- }
-
// Fetch our friends
- store.rootState.api.backendInteractor.fetchFriends()
+ store.rootState.api.backendInteractor.fetchFriends({ id: user.id })
.then((friends) => commit('addNewUsers', friends))
})
} else {