diff --git a/app/soapbox/actions/__tests__/auth-test.js b/app/soapbox/actions/__tests__/auth-test.js
deleted file mode 100644
index 0e1e3d2b4..000000000
--- a/app/soapbox/actions/__tests__/auth-test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import {
- AUTH_LOGGED_OUT,
- logOut,
-} from '../auth';
-import { ALERT_SHOW } from '../alerts';
-import { Map as ImmutableMap } from 'immutable';
-import { mockStore } from 'soapbox/test_helpers';
-
-describe('logOut()', () => {
- it('creates expected actions', () => {
- const expectedActions = [
- { type: AUTH_LOGGED_OUT },
- { type: ALERT_SHOW, message: 'Logged out.', severity: 'success' },
- ];
- const store = mockStore(ImmutableMap());
-
- store.dispatch(logOut());
- return expect(store.getActions()).toEqual(expectedActions);
- });
-});
diff --git a/app/soapbox/actions/accounts.js b/app/soapbox/actions/accounts.js
index 55e5926e4..c1e2341fd 100644
--- a/app/soapbox/actions/accounts.js
+++ b/app/soapbox/actions/accounts.js
@@ -6,6 +6,11 @@ import {
importFetchedAccounts,
importErrorWhileFetchingAccountByUsername,
} from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
+
+export const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST';
+export const ACCOUNT_CREATE_SUCCESS = 'ACCOUNT_CREATE_SUCCESS';
+export const ACCOUNT_CREATE_FAIL = 'ACCOUNT_CREATE_FAIL';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
@@ -97,6 +102,18 @@ function getFromDB(dispatch, getState, index, id) {
});
}
+export function createAccount(params) {
+ return (dispatch, getState) => {
+ dispatch({ type: ACCOUNT_CREATE_REQUEST, params });
+ return api(getState, 'app').post('/api/v1/accounts', params).then(({ data: token }) => {
+ return dispatch({ type: ACCOUNT_CREATE_SUCCESS, params, token });
+ }).catch(error => {
+ dispatch({ type: ACCOUNT_CREATE_FAIL, error, params });
+ throw error;
+ });
+ };
+}
+
export function fetchAccount(id) {
return (dispatch, getState) => {
dispatch(fetchRelationships([id]));
@@ -162,7 +179,7 @@ export function fetchAccountFail(id, error) {
export function followAccount(id, reblogs = true) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
const locked = getState().getIn(['accounts', id, 'locked'], false);
@@ -179,7 +196,7 @@ export function followAccount(id, reblogs = true) {
export function unfollowAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unfollowAccountRequest(id));
@@ -245,7 +262,7 @@ export function unfollowAccountFail(error) {
export function blockAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(blockAccountRequest(id));
@@ -260,7 +277,7 @@ export function blockAccount(id) {
export function unblockAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unblockAccountRequest(id));
@@ -318,7 +335,7 @@ export function unblockAccountFail(error) {
export function muteAccount(id, notifications) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(muteAccountRequest(id));
@@ -333,7 +350,7 @@ export function muteAccount(id, notifications) {
export function unmuteAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unmuteAccountRequest(id));
@@ -391,7 +408,7 @@ export function unmuteAccountFail(error) {
export function fetchFollowers(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchFollowersRequest(id));
@@ -433,7 +450,7 @@ export function fetchFollowersFail(id, error) {
export function expandFollowers(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'followers', id, 'next']);
@@ -481,7 +498,7 @@ export function expandFollowersFail(id, error) {
export function fetchFollowing(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchFollowingRequest(id));
@@ -523,7 +540,7 @@ export function fetchFollowingFail(id, error) {
export function expandFollowing(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'following', id, 'next']);
@@ -571,7 +588,7 @@ export function expandFollowingFail(id, error) {
export function fetchRelationships(accountIds) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const loadedRelationships = getState().get('relationships');
const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
@@ -616,7 +633,7 @@ export function fetchRelationshipsFail(error) {
export function fetchFollowRequests() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchFollowRequestsRequest());
@@ -651,7 +668,7 @@ export function fetchFollowRequestsFail(error) {
export function expandFollowRequests() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'follow_requests', 'next']);
@@ -692,7 +709,7 @@ export function expandFollowRequestsFail(error) {
export function authorizeFollowRequest(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(authorizeFollowRequestRequest(id));
@@ -728,7 +745,7 @@ export function authorizeFollowRequestFail(id, error) {
export function rejectFollowRequest(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(rejectFollowRequestRequest(id));
@@ -763,7 +780,7 @@ export function rejectFollowRequestFail(id, error) {
export function pinAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(pinAccountRequest(id));
@@ -777,7 +794,7 @@ export function pinAccount(id) {
export function unpinAccount(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unpinAccountRequest(id));
diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js
index a0bb9b9a2..e99ad9fdb 100644
--- a/app/soapbox/actions/auth.js
+++ b/app/soapbox/actions/auth.js
@@ -1,14 +1,19 @@
import api from '../api';
+import { importFetchedAccount } from './importer';
import snackbar from 'soapbox/actions/snackbar';
+import { createAccount } from 'soapbox/actions/accounts';
+import { ME_FETCH_SUCCESS } from 'soapbox/actions/me';
+
+export const SWITCH_ACCOUNT = 'SWITCH_ACCOUNT';
export const AUTH_APP_CREATED = 'AUTH_APP_CREATED';
export const AUTH_APP_AUTHORIZED = 'AUTH_APP_AUTHORIZED';
export const AUTH_LOGGED_IN = 'AUTH_LOGGED_IN';
export const AUTH_LOGGED_OUT = 'AUTH_LOGGED_OUT';
-export const AUTH_REGISTER_REQUEST = 'AUTH_REGISTER_REQUEST';
-export const AUTH_REGISTER_SUCCESS = 'AUTH_REGISTER_SUCCESS';
-export const AUTH_REGISTER_FAIL = 'AUTH_REGISTER_FAIL';
+export const VERIFY_CREDENTIALS_REQUEST = 'VERIFY_CREDENTIALS_REQUEST';
+export const VERIFY_CREDENTIALS_SUCCESS = 'VERIFY_CREDENTIALS_SUCCESS';
+export const VERIFY_CREDENTIALS_FAIL = 'VERIFY_CREDENTIALS_FAIL';
export const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST';
export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS';
@@ -86,8 +91,9 @@ function createUserToken(username, password) {
grant_type: 'password',
username: username,
password: password,
- }).then(response => {
- dispatch(authLoggedIn(response.data));
+ }).then(({ data: token }) => {
+ dispatch(authLoggedIn(token));
+ return token;
});
};
}
@@ -121,8 +127,32 @@ export function otpVerify(code, mfa_token) {
code: code,
challenge_type: 'totp',
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
- }).then(response => {
- dispatch(authLoggedIn(response.data));
+ }).then(({ data: token }) => {
+ dispatch(authLoggedIn(token));
+ return token;
+ });
+ };
+}
+
+export function verifyCredentials(token) {
+ return (dispatch, getState) => {
+ dispatch({ type: VERIFY_CREDENTIALS_REQUEST });
+
+ const request = {
+ method: 'get',
+ url: '/api/v1/accounts/verify_credentials',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ },
+ };
+
+ return api(getState).request(request).then(({ data: account }) => {
+ dispatch(importFetchedAccount(account));
+ dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account });
+ if (account.id === getState().get('me')) dispatch({ type: ME_FETCH_SUCCESS, me: account });
+ return account;
+ }).catch(error => {
+ dispatch({ type: VERIFY_CREDENTIALS_FAIL, token, error });
});
};
}
@@ -147,32 +177,43 @@ export function logIn(username, password) {
export function logOut() {
return (dispatch, getState) => {
const state = getState();
+ const me = state.get('me');
- dispatch({ type: AUTH_LOGGED_OUT });
-
- // Attempt to destroy OAuth token on logout
- api(getState).post('/oauth/revoke', {
+ return api(getState).post('/oauth/revoke', {
client_id: state.getIn(['auth', 'app', 'client_id']),
client_secret: state.getIn(['auth', 'app', 'client_secret']),
- token: state.getIn(['auth', 'user', 'access_token']),
+ token: state.getIn(['auth', 'users', me, 'access_token']),
+ }).finally(() => {
+ dispatch({ type: AUTH_LOGGED_OUT, accountId: me });
+ dispatch(snackbar.success('Logged out.'));
});
+ };
+}
- dispatch(snackbar.success('Logged out.'));
+export function switchAccount(accountId) {
+ return { type: SWITCH_ACCOUNT, accountId };
+}
+
+export function fetchOwnAccounts() {
+ return (dispatch, getState) => {
+ const state = getState();
+ state.getIn(['auth', 'users']).forEach(user => {
+ const account = state.getIn(['accounts', user.get('id')]);
+ if (!account) {
+ dispatch(verifyCredentials(user.get('access_token')));
+ }
+ });
};
}
export function register(params) {
return (dispatch, getState) => {
params.fullname = params.username;
- dispatch({ type: AUTH_REGISTER_REQUEST });
+
return dispatch(createAppAndToken()).then(() => {
- return api(getState, 'app').post('/api/v1/accounts', params);
- }).then(response => {
- dispatch({ type: AUTH_REGISTER_SUCCESS, token: response.data });
- dispatch(authLoggedIn(response.data));
- }).catch(error => {
- dispatch({ type: AUTH_REGISTER_FAIL, error });
- throw error;
+ return dispatch(createAccount(params));
+ }).then(token => {
+ return dispatch(authLoggedIn(token));
});
};
}
@@ -285,9 +326,9 @@ export function authAppAuthorized(app) {
};
}
-export function authLoggedIn(user) {
+export function authLoggedIn(token) {
return {
type: AUTH_LOGGED_IN,
- user,
+ token,
};
}
diff --git a/app/soapbox/actions/blocks.js b/app/soapbox/actions/blocks.js
index d891999bb..5c351b674 100644
--- a/app/soapbox/actions/blocks.js
+++ b/app/soapbox/actions/blocks.js
@@ -1,6 +1,7 @@
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS';
@@ -12,7 +13,7 @@ export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
export function fetchBlocks() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchBlocksRequest());
@@ -48,7 +49,7 @@ export function fetchBlocksFail(error) {
export function expandBlocks() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'blocks', 'next']);
diff --git a/app/soapbox/actions/compose.js b/app/soapbox/actions/compose.js
index 782dee74a..54519c112 100644
--- a/app/soapbox/actions/compose.js
+++ b/app/soapbox/actions/compose.js
@@ -13,6 +13,7 @@ import { openModal, closeModal } from './modal';
import { getSettings } from './settings';
import { getFeatures } from 'soapbox/utils/features';
import { uploadMedia } from './media';
+import { isLoggedIn } from 'soapbox/utils/auth';
let cancelFetchComposeSuggestionsAccounts;
@@ -157,7 +158,7 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
export function submitCompose(routerHistory, group) {
return function(dispatch, getState) {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const status = getState().getIn(['compose', 'text'], '');
const media = getState().getIn(['compose', 'media_attachments']);
@@ -216,7 +217,7 @@ export function submitComposeFail(error) {
export function uploadCompose(files) {
return function(dispatch, getState) {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const uploadLimit = getFeatures(getState().get('instance')).attachmentLimit;
const media = getState().getIn(['compose', 'media_attachments']);
@@ -254,7 +255,7 @@ export function uploadCompose(files) {
export function changeUploadCompose(id, params) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(changeUploadComposeRequest());
diff --git a/app/soapbox/actions/conversations.js b/app/soapbox/actions/conversations.js
index fc1305156..e019441f9 100644
--- a/app/soapbox/actions/conversations.js
+++ b/app/soapbox/actions/conversations.js
@@ -4,6 +4,7 @@ import {
importFetchedStatuses,
importFetchedStatus,
} from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
@@ -24,7 +25,7 @@ export const unmountConversations = () => ({
});
export const markConversationRead = conversationId => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch({
type: CONVERSATIONS_READ,
@@ -35,7 +36,7 @@ export const markConversationRead = conversationId => (dispatch, getState) => {
};
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(expandConversationsRequest());
diff --git a/app/soapbox/actions/domain_blocks.js b/app/soapbox/actions/domain_blocks.js
index a5ebb0c99..533885a56 100644
--- a/app/soapbox/actions/domain_blocks.js
+++ b/app/soapbox/actions/domain_blocks.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
@@ -18,7 +19,7 @@ export const DOMAIN_BLOCKS_EXPAND_FAIL = 'DOMAIN_BLOCKS_EXPAND_FAIL';
export function blockDomain(domain) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(blockDomainRequest(domain));
@@ -57,7 +58,7 @@ export function blockDomainFail(domain, error) {
export function unblockDomain(domain) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unblockDomainRequest(domain));
@@ -102,7 +103,7 @@ export function unblockDomainFail(domain, error) {
export function fetchDomainBlocks() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchDomainBlocksRequest());
@@ -138,7 +139,7 @@ export function fetchDomainBlocksFail(error) {
export function expandDomainBlocks() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['domain_lists', 'blocks', 'next']);
diff --git a/app/soapbox/actions/emoji_reacts.js b/app/soapbox/actions/emoji_reacts.js
index 7b041d4ee..60bb46c2f 100644
--- a/app/soapbox/actions/emoji_reacts.js
+++ b/app/soapbox/actions/emoji_reacts.js
@@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts, importFetchedStatus } from './importer';
import { favourite, unfavourite } from './interactions';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const EMOJI_REACT_REQUEST = 'EMOJI_REACT_REQUEST';
export const EMOJI_REACT_SUCCESS = 'EMOJI_REACT_SUCCESS';
@@ -44,7 +45,7 @@ export const simpleEmojiReact = (status, emoji) => {
export function fetchEmojiReacts(id, emoji) {
return (dispatch, getState) => {
- if (!getState().get('me')) return dispatch(noOp());
+ if (!isLoggedIn(getState)) return dispatch(noOp());
dispatch(fetchEmojiReactsRequest(id, emoji));
@@ -65,7 +66,7 @@ export function fetchEmojiReacts(id, emoji) {
export function emojiReact(status, emoji) {
return function(dispatch, getState) {
- if (!getState().get('me')) return dispatch(noOp());
+ if (!isLoggedIn(getState)) return dispatch(noOp());
dispatch(emojiReactRequest(status, emoji));
@@ -82,7 +83,7 @@ export function emojiReact(status, emoji) {
export function unEmojiReact(status, emoji) {
return (dispatch, getState) => {
- if (!getState().get('me')) return dispatch(noOp());
+ if (!isLoggedIn(getState)) return dispatch(noOp());
dispatch(unEmojiReactRequest(status, emoji));
diff --git a/app/soapbox/actions/favourites.js b/app/soapbox/actions/favourites.js
index d5b774f12..02ecc81c3 100644
--- a/app/soapbox/actions/favourites.js
+++ b/app/soapbox/actions/favourites.js
@@ -1,5 +1,6 @@
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
@@ -11,7 +12,7 @@ export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FA
export function fetchFavouritedStatuses() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
return;
@@ -55,7 +56,7 @@ export function fetchFavouritedStatusesFail(error) {
export function expandFavouritedStatuses() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['status_lists', 'favourites', 'next'], null);
diff --git a/app/soapbox/actions/filters.js b/app/soapbox/actions/filters.js
index e3ad557f5..ab2767a14 100644
--- a/app/soapbox/actions/filters.js
+++ b/app/soapbox/actions/filters.js
@@ -1,5 +1,6 @@
import api from '../api';
import snackbar from 'soapbox/actions/snackbar';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';
export const FILTERS_FETCH_SUCCESS = 'FILTERS_FETCH_SUCCESS';
@@ -14,7 +15,7 @@ export const FILTERS_DELETE_SUCCESS = 'FILTERS_DELETE_SUCCESS';
export const FILTERS_DELETE_FAIL = 'FILTERS_DELETE_FAIL';
export const fetchFilters = () => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch({
type: FILTERS_FETCH_REQUEST,
diff --git a/app/soapbox/actions/group_editor.js b/app/soapbox/actions/group_editor.js
index 4b9245081..b74533a14 100644
--- a/app/soapbox/actions/group_editor.js
+++ b/app/soapbox/actions/group_editor.js
@@ -1,4 +1,5 @@
import api from '../api';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST';
export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS';
@@ -27,7 +28,7 @@ export const submit = (routerHistory) => (dispatch, getState) => {
export const create = (title, description, coverImage, routerHistory) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(createRequest());
@@ -62,7 +63,7 @@ export const createFail = error => ({
});
export const update = (groupId, title, description, coverImage, routerHistory) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(updateRequest());
diff --git a/app/soapbox/actions/groups.js b/app/soapbox/actions/groups.js
index 959b1b0b2..588cbe6e2 100644
--- a/app/soapbox/actions/groups.js
+++ b/app/soapbox/actions/groups.js
@@ -1,6 +1,7 @@
import api, { getLinks } from '../api';
import { importFetchedAccounts } from './importer';
import { fetchRelationships } from './accounts';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST';
export const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS';
@@ -51,7 +52,7 @@ export const GROUP_REMOVE_STATUS_SUCCESS = 'GROUP_REMOVE_STATUS_SUCCESS';
export const GROUP_REMOVE_STATUS_FAIL = 'GROUP_REMOVE_STATUS_FAIL';
export const fetchGroup = id => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchGroupRelationships([id]));
@@ -84,7 +85,7 @@ export const fetchGroupFail = (id, error) => ({
export function fetchGroupRelationships(groupIds) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const loadedRelationships = getState().get('group_relationships');
const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null);
@@ -128,7 +129,7 @@ export function fetchGroupRelationshipsFail(error) {
};
export const fetchGroups = (tab) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchGroupsRequest());
@@ -157,7 +158,7 @@ export const fetchGroupsFail = error => ({
export function joinGroup(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(joinGroupRequest(id));
@@ -171,7 +172,7 @@ export function joinGroup(id) {
export function leaveGroup(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(leaveGroupRequest(id));
@@ -227,7 +228,7 @@ export function leaveGroupFail(error) {
export function fetchMembers(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchMembersRequest(id));
@@ -269,7 +270,7 @@ export function fetchMembersFail(id, error) {
export function expandMembers(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'groups', id, 'next']);
@@ -317,7 +318,7 @@ export function expandMembersFail(id, error) {
export function fetchRemovedAccounts(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchRemovedAccountsRequest(id));
@@ -359,7 +360,7 @@ export function fetchRemovedAccountsFail(id, error) {
export function expandRemovedAccounts(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'groups_removed_accounts', id, 'next']);
@@ -407,7 +408,7 @@ export function expandRemovedAccountsFail(id, error) {
export function removeRemovedAccount(groupId, id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(removeRemovedAccountRequest(groupId, id));
@@ -446,7 +447,7 @@ export function removeRemovedAccountFail(groupId, id, error) {
export function createRemovedAccount(groupId, id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(createRemovedAccountRequest(groupId, id));
@@ -485,7 +486,7 @@ export function createRemovedAccountFail(groupId, id, error) {
export function groupRemoveStatus(groupId, id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(groupRemoveStatusRequest(groupId, id));
diff --git a/app/soapbox/actions/interactions.js b/app/soapbox/actions/interactions.js
index b07feac1b..73da37c96 100644
--- a/app/soapbox/actions/interactions.js
+++ b/app/soapbox/actions/interactions.js
@@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts, importFetchedStatus } from './importer';
import snackbar from 'soapbox/actions/snackbar';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
@@ -44,7 +45,7 @@ export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
export function reblog(status) {
return function(dispatch, getState) {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(reblogRequest(status));
@@ -61,7 +62,7 @@ export function reblog(status) {
export function unreblog(status) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unreblogRequest(status));
@@ -126,7 +127,7 @@ export function unreblogFail(status, error) {
export function favourite(status) {
return function(dispatch, getState) {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(favouriteRequest(status));
@@ -141,7 +142,7 @@ export function favourite(status) {
export function unfavourite(status) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unfavouriteRequest(status));
@@ -280,7 +281,7 @@ export function unbookmarkFail(status, error) {
export function fetchReblogs(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchReblogsRequest(id));
@@ -317,7 +318,7 @@ export function fetchReblogsFail(id, error) {
export function fetchFavourites(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchFavouritesRequest(id));
@@ -354,7 +355,7 @@ export function fetchFavouritesFail(id, error) {
export function pin(status) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(pinRequest(status));
@@ -394,7 +395,7 @@ export function pinFail(status, error) {
export function unpin(status) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unpinRequest(status));
diff --git a/app/soapbox/actions/lists.js b/app/soapbox/actions/lists.js
index d93ddc43c..68171cbe3 100644
--- a/app/soapbox/actions/lists.js
+++ b/app/soapbox/actions/lists.js
@@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
import { showAlertForError } from './alerts';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
@@ -50,7 +51,7 @@ export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS';
export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL';
export const fetchList = id => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
if (getState().getIn(['lists', id])) {
return;
@@ -80,7 +81,7 @@ export const fetchListFail = (id, error) => ({
});
export const fetchLists = () => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchListsRequest());
@@ -129,7 +130,7 @@ export const changeListEditorTitle = value => ({
});
export const createList = (title, shouldReset) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(createListRequest());
@@ -157,7 +158,7 @@ export const createListFail = error => ({
});
export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(updateListRequest(id));
@@ -191,7 +192,7 @@ export const resetListEditor = () => ({
});
export const deleteList = id => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(deleteListRequest(id));
@@ -217,7 +218,7 @@ export const deleteListFail = (id, error) => ({
});
export const fetchListAccounts = listId => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchListAccountsRequest(listId));
@@ -246,7 +247,7 @@ export const fetchListAccountsFail = (id, error) => ({
});
export const fetchListSuggestions = q => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const params = {
q,
@@ -281,7 +282,7 @@ export const addToListEditor = accountId => (dispatch, getState) => {
};
export const addToList = (listId, accountId) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(addToListRequest(listId, accountId));
@@ -314,7 +315,7 @@ export const removeFromListEditor = accountId => (dispatch, getState) => {
};
export const removeFromList = (listId, accountId) => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(removeFromListRequest(listId, accountId));
@@ -356,7 +357,7 @@ export const setupListAdder = accountId => (dispatch, getState) => {
};
export const fetchAccountLists = accountId => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchAccountListsRequest(accountId));
diff --git a/app/soapbox/actions/me.js b/app/soapbox/actions/me.js
index a05f2b516..5b58b50b1 100644
--- a/app/soapbox/actions/me.js
+++ b/app/soapbox/actions/me.js
@@ -1,5 +1,6 @@
import api from '../api';
import { importFetchedAccount } from './importer';
+import { verifyCredentials } from './auth';
export const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST';
export const ME_FETCH_SUCCESS = 'ME_FETCH_SUCCESS';
@@ -10,23 +11,25 @@ export const ME_PATCH_REQUEST = 'ME_PATCH_REQUEST';
export const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS';
export const ME_PATCH_FAIL = 'ME_PATCH_FAIL';
-const hasToken = getState => getState().hasIn(['auth', 'user', 'access_token']);
const noOp = () => new Promise(f => f());
export function fetchMe() {
return (dispatch, getState) => {
+ const state = getState();
- if (!hasToken(getState)) {
+ const me = state.getIn(['auth', 'me']);
+ const token = state.getIn(['auth', 'users', me, 'access_token']);
+
+ if (!token) {
dispatch({ type: ME_FETCH_SKIP }); return noOp();
};
dispatch(fetchMeRequest());
-
- return api(getState).get('/api/v1/accounts/verify_credentials').then(response => {
- dispatch(fetchMeSuccess(response.data));
+ return dispatch(verifyCredentials(token)).then(account => {
+ dispatch(fetchMeSuccess(account));
}).catch(error => {
dispatch(fetchMeFail(error));
- });
+ });;
};
}
diff --git a/app/soapbox/actions/mutes.js b/app/soapbox/actions/mutes.js
index 00d791197..7ad66a3c0 100644
--- a/app/soapbox/actions/mutes.js
+++ b/app/soapbox/actions/mutes.js
@@ -2,6 +2,7 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
import { openModal } from './modal';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
@@ -16,7 +17,7 @@ export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS'
export function fetchMutes() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(fetchMutesRequest());
@@ -52,7 +53,7 @@ export function fetchMutesFail(error) {
export function expandMutes() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const url = getState().getIn(['user_lists', 'mutes', 'next']);
diff --git a/app/soapbox/actions/notifications.js b/app/soapbox/actions/notifications.js
index 63d4adab3..07bc3e1cf 100644
--- a/app/soapbox/actions/notifications.js
+++ b/app/soapbox/actions/notifications.js
@@ -17,6 +17,7 @@ import {
} from 'immutable';
import { unescapeHTML } from '../utils/html';
import { getFilters, regexFromFilters } from '../selectors';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
@@ -156,7 +157,7 @@ const noOp = () => {};
export function expandNotifications({ maxId } = {}, done = noOp) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
const activeFilter = getSettings(getState()).getIn(['notifications', 'quickFilter', 'active']);
const notifications = getState().get('notifications');
@@ -222,7 +223,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
export function clearNotifications() {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch({
type: NOTIFICATIONS_CLEAR,
@@ -256,9 +257,9 @@ export function setFilter(filterType) {
export function markReadNotifications() {
return (dispatch, getState) => {
- const state = getState();
- if (!state.get('me')) return;
+ if (!isLoggedIn(getState)) return;
+ const state = getState();
const topNotification = state.getIn(['notifications', 'items'], ImmutableOrderedMap()).first(ImmutableMap()).get('id');
const lastRead = state.getIn(['notifications', 'lastRead']);
diff --git a/app/soapbox/actions/pin_statuses.js b/app/soapbox/actions/pin_statuses.js
index 0a4a320c1..17f1bfd14 100644
--- a/app/soapbox/actions/pin_statuses.js
+++ b/app/soapbox/actions/pin_statuses.js
@@ -1,5 +1,6 @@
import api from '../api';
import { importFetchedStatuses } from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
@@ -7,8 +8,8 @@ export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
export function fetchPinnedStatuses() {
return (dispatch, getState) => {
+ if (!isLoggedIn(getState)) return;
const me = getState().get('me');
- if (!me) return;
dispatch(fetchPinnedStatusesRequest());
diff --git a/app/soapbox/actions/settings.js b/app/soapbox/actions/settings.js
index b96504647..77ab9b91d 100644
--- a/app/soapbox/actions/settings.js
+++ b/app/soapbox/actions/settings.js
@@ -2,6 +2,7 @@ import { debounce } from 'lodash';
import { showAlertForError } from './alerts';
import { patchMe } from 'soapbox/actions/me';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import { isLoggedIn } from 'soapbox/utils/auth';
import uuid from '../uuid';
export const SETTING_CHANGE = 'SETTING_CHANGE';
@@ -147,8 +148,9 @@ export function changeSetting(path, value) {
};
const debouncedSave = debounce((dispatch, getState) => {
+ if (!isLoggedIn(getState)) return;
+
const state = getState();
- if (!state.get('me')) return;
if (getSettings(state).getIn(['saved'])) return;
const data = state.get('settings').delete('saved').toJS();
diff --git a/app/soapbox/actions/statuses.js b/app/soapbox/actions/statuses.js
index bdf31da8f..f06aefa39 100644
--- a/app/soapbox/actions/statuses.js
+++ b/app/soapbox/actions/statuses.js
@@ -4,6 +4,7 @@ import { evictStatus } from '../storage/modifier';
import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
import { openModal } from './modal';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
@@ -141,7 +142,7 @@ export function redraft(status, raw_text) {
export function deleteStatus(id, routerHistory, withRedraft = false) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
let status = getState().getIn(['statuses', id]);
@@ -233,7 +234,7 @@ export function fetchContextFail(id, error) {
export function muteStatus(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(muteStatusRequest(id));
@@ -269,7 +270,7 @@ export function muteStatusFail(id, error) {
export function unmuteStatus(id) {
return (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch(unmuteStatusRequest(id));
diff --git a/app/soapbox/actions/store.js b/app/soapbox/actions/store.js
new file mode 100644
index 000000000..d6921d7f1
--- /dev/null
+++ b/app/soapbox/actions/store.js
@@ -0,0 +1,7 @@
+export const INIT_STORE = 'INIT_STORE';
+
+export function initStore() {
+ return {
+ type: INIT_STORE,
+ };
+}
diff --git a/app/soapbox/actions/suggestions.js b/app/soapbox/actions/suggestions.js
index d84bb758f..788166d8b 100644
--- a/app/soapbox/actions/suggestions.js
+++ b/app/soapbox/actions/suggestions.js
@@ -1,5 +1,6 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
+import { isLoggedIn } from 'soapbox/utils/auth';
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST';
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS';
@@ -43,7 +44,7 @@ export function fetchSuggestionsFail(error) {
};
export const dismissSuggestion = accountId => (dispatch, getState) => {
- if (!getState().get('me')) return;
+ if (!isLoggedIn(getState)) return;
dispatch({
type: SUGGESTIONS_DISMISS,
diff --git a/app/soapbox/api.js b/app/soapbox/api.js
index f65971e64..dc9be3add 100644
--- a/app/soapbox/api.js
+++ b/app/soapbox/api.js
@@ -9,8 +9,15 @@ export const getLinks = response => {
return LinkHeader.parse(value);
};
-const getToken = (getState, authType) =>
- getState().getIn(['auth', authType, 'access_token']);
+const getToken = (getState, authType) => {
+ const state = getState();
+ if (authType === 'app') {
+ return state.getIn(['auth', 'app', 'access_token']);
+ } else {
+ const me = state.get('me');
+ return state.getIn(['auth', 'users', me, 'access_token']);
+ }
+};
export default (getState, authType = 'user') => {
const accessToken = getToken(getState, authType);
diff --git a/app/soapbox/components/sidebar_menu.js b/app/soapbox/components/sidebar_menu.js
index 306303417..61f184523 100644
--- a/app/soapbox/components/sidebar_menu.js
+++ b/app/soapbox/components/sidebar_menu.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
+import { throttle } from 'lodash';
import { Link, NavLink } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -11,11 +12,12 @@ import IconButton from './icon_button';
import Icon from './icon';
import DisplayName from './display_name';
import { closeSidebar } from '../actions/sidebar';
-import { shortNumberFormat } from '../utils/numbers';
import { isStaff } from '../utils/accounts';
import { makeGetAccount } from '../selectors';
-import { logOut } from 'soapbox/actions/auth';
+import { logOut, switchAccount } from 'soapbox/actions/auth';
import ThemeToggle from '../features/ui/components/theme_toggle_container';
+import { fetchOwnAccounts } from 'soapbox/actions/auth';
+import { List as ImmutableList } from 'immutable';
const messages = defineMessages({
followers: { id: 'account.followers', defaultMessage: 'Followers' },
@@ -38,17 +40,30 @@ const messages = defineMessages({
news: { id: 'tabs_bar.news', defaultMessage: 'News' },
donate: { id: 'donate', defaultMessage: 'Donate' },
info: { id: 'column.info', defaultMessage: 'Server information' },
+ add_account: { id: 'profile_dropdown.add_account', defaultMessage: 'Add an existing account' },
});
const mapStateToProps = state => {
const me = state.get('me');
const getAccount = makeGetAccount();
+ const otherAccounts =
+ state
+ .getIn(['auth', 'users'])
+ .keySeq()
+ .reduce((list, id) => {
+ if (id === me) return list;
+ const account = state.getIn(['accounts', id]);
+ return account ? list.push(account) : list;
+ }, ImmutableList());
+
return {
account: getAccount(state, me),
sidebarOpen: state.get('sidebar').sidebarOpen,
donateUrl: state.getIn(['patron', 'instance', 'url']),
isStaff: isStaff(state.getIn(['accounts', me])),
+ otherAccounts,
+
};
};
@@ -60,6 +75,12 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(logOut());
e.preventDefault();
},
+ fetchOwnAccounts() {
+ dispatch(fetchOwnAccounts());
+ },
+ switchAccount(account) {
+ dispatch(switchAccount(account.get('id')));
+ },
});
export default @connect(mapStateToProps, mapDispatchToProps)
@@ -78,8 +99,57 @@ class SidebarMenu extends ImmutablePureComponent {
isStaff: false,
}
+ state = {
+ switcher: false,
+ }
+
+ handleClose = () => {
+ this.setState({ switcher: false });
+ this.props.onClose();
+ }
+
+ handleSwitchAccount = account => {
+ return e => {
+ this.props.switchAccount(account);
+ e.preventDefault();
+ };
+ }
+
+ handleSwitcherClick = e => {
+ this.setState({ switcher: !this.state.switcher });
+ e.preventDefault();
+ }
+
+ fetchOwnAccounts = throttle(() => {
+ this.props.fetchOwnAccounts();
+ }, 2000);
+
+ componentDidMount() {
+ this.fetchOwnAccounts();
+ }
+
+ componentDidUpdate() {
+ this.fetchOwnAccounts();
+ }
+
+ renderAccount = account => {
+ return (
+
+
{