diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js index 23252cbcd..b6bcd888c 100644 --- a/app/soapbox/actions/auth.js +++ b/app/soapbox/actions/auth.js @@ -23,6 +23,10 @@ export const CHANGE_PASSWORD_REQUEST = 'CHANGE_PASSWORD_REQUEST'; export const CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS'; export const CHANGE_PASSWORD_FAIL = 'CHANGE_PASSWORD_FAIL'; +export const FETCH_TOKENS_REQUEST = 'FETCH_TOKENS_REQUEST'; +export const FETCH_TOKENS_SUCCESS = 'FETCH_TOKENS_SUCCESS'; +export const FETCH_TOKENS_FAIL = 'FETCH_TOKENS_FAIL'; + const noOp = () => () => new Promise(f => f()); function createAppAndToken() { @@ -189,6 +193,17 @@ export function changePassword(oldPassword, newPassword, confirmation) { }; } +export function fetchOAuthTokens() { + return (dispatch, getState) => { + dispatch({ type: FETCH_TOKENS_REQUEST }); + return api(getState).get('/api/oauth_tokens.json').then(response => { + dispatch({ type: FETCH_TOKENS_SUCCESS, tokens: response.data }); + }).catch(error => { + dispatch({ type: FETCH_TOKENS_FAIL }); + }); + }; +} + export function authAppCreated(app) { return { type: AUTH_APP_CREATED, diff --git a/app/soapbox/features/security/index.js b/app/soapbox/features/security/index.js index 91d907ce7..2dc378ff6 100644 --- a/app/soapbox/features/security/index.js +++ b/app/soapbox/features/security/index.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import Column from '../ui/components/column'; import { SimpleForm, @@ -10,7 +11,11 @@ import { FieldsGroup, TextInput, } from 'soapbox/features/forms'; -import { changeEmail, changePassword } from 'soapbox/actions/auth'; +import { + changeEmail, + changePassword, + fetchOAuthTokens, +} from 'soapbox/actions/auth'; import { showAlert } from 'soapbox/actions/alerts'; const messages = defineMessages({ @@ -37,6 +42,7 @@ class SecurityForm extends ImmutablePureComponent { + ); } @@ -191,3 +197,37 @@ class ChangePasswordForm extends ImmutablePureComponent { } } + +const mapStateToProps = state => ({ + tokens: state.getIn(['auth', 'tokens']), +}); + +@connect(mapStateToProps) +@injectIntl +class AuthTokenList extends ImmutablePureComponent { + + static propTypes = { + tokens: ImmutablePropTypes.list, + } + + componentDidMount() { + this.props.dispatch(fetchOAuthTokens()); + } + + render() { + if (this.props.tokens.isEmpty()) return null; + return ( + + {this.props.tokens.map((token, i) => ( +
+
{token.get('app_name')}
+
{token.get('id')}
+
{token.get('valid_until')}
+
+
+ ))} +
+ ); + } + +} diff --git a/app/soapbox/reducers/auth.js b/app/soapbox/reducers/auth.js index b25330518..35bf85559 100644 --- a/app/soapbox/reducers/auth.js +++ b/app/soapbox/reducers/auth.js @@ -3,12 +3,14 @@ import { AUTH_LOGGED_IN, AUTH_APP_AUTHORIZED, AUTH_LOGGED_OUT, + FETCH_TOKENS_SUCCESS, } from '../actions/auth'; -import { Map as ImmutableMap } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; const initialState = ImmutableMap({ app: ImmutableMap(JSON.parse(localStorage.getItem('soapbox:auth:app'))), user: ImmutableMap(JSON.parse(localStorage.getItem('soapbox:auth:user'))), + tokens: ImmutableList(), }); export default function auth(state = initialState, action) { @@ -25,7 +27,9 @@ export default function auth(state = initialState, action) { return state.set('user', ImmutableMap(action.user)); case AUTH_LOGGED_OUT: localStorage.removeItem('soapbox:auth:user'); - return state.setIn(['user'], ImmutableMap()); + return state.set('user', ImmutableMap()); + case FETCH_TOKENS_SUCCESS: + return state.set('tokens', fromJS(action.tokens)); default: return state; }