Merge branch 'revoke-confirmation' into 'develop'
Ask for confirmation before revoking current session See merge request soapbox-pub/soapbox!1871
This commit is contained in:
commit
1cdf277d08
2 changed files with 30 additions and 4 deletions
|
@ -1,26 +1,45 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { fetchOAuthTokens, revokeOAuthTokenById } from 'soapbox/actions/security';
|
import { fetchOAuthTokens, revokeOAuthTokenById } from 'soapbox/actions/security';
|
||||||
import { Button, Card, CardBody, CardHeader, CardTitle, Column, Spinner, Stack, Text } from 'soapbox/components/ui';
|
import { Button, Card, CardBody, CardHeader, CardTitle, Column, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||||
import { Token } from 'soapbox/reducers/security';
|
import { Token } from 'soapbox/reducers/security';
|
||||||
|
|
||||||
|
import type { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
header: { id: 'security.headers.tokens', defaultMessage: 'Sessions' },
|
header: { id: 'security.headers.tokens', defaultMessage: 'Sessions' },
|
||||||
revoke: { id: 'security.tokens.revoke', defaultMessage: 'Revoke' },
|
revoke: { id: 'security.tokens.revoke', defaultMessage: 'Revoke' },
|
||||||
|
revokeSessionHeading: { id: 'confirmations.revoke_session.heading', defaultMessage: 'Revoke current session' },
|
||||||
|
revokeSessionMessage: { id: 'confirmations.revoke_session.message', defaultMessage: 'You are about to revoke your current session. You will be signed out.' },
|
||||||
|
revokeSessionConfirm: { id: 'confirmations.revoke_session.confirm', defaultMessage: 'Revoke' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IAuthToken {
|
interface IAuthToken {
|
||||||
token: Token,
|
token: Token,
|
||||||
|
isCurrent: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthToken: React.FC<IAuthToken> = ({ token }) => {
|
const AuthToken: React.FC<IAuthToken> = ({ token, isCurrent }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleRevoke = () => {
|
const handleRevoke = () => {
|
||||||
dispatch(revokeOAuthTokenById(token.id));
|
if (isCurrent)
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
icon: require('@tabler/icons/alert-triangle.svg'),
|
||||||
|
heading: intl.formatMessage(messages.revokeSessionHeading),
|
||||||
|
message: intl.formatMessage(messages.revokeSessionMessage),
|
||||||
|
confirm: intl.formatMessage(messages.revokeSessionConfirm),
|
||||||
|
onConfirm: () => {
|
||||||
|
dispatch(revokeOAuthTokenById(token.id));
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
else {
|
||||||
|
dispatch(revokeOAuthTokenById(token.id));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -42,7 +61,7 @@ const AuthToken: React.FC<IAuthToken> = ({ token }) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<div className='flex justify-end'>
|
<div className='flex justify-end'>
|
||||||
<Button theme='primary' onClick={handleRevoke}>
|
<Button theme={isCurrent ? 'danger' : 'primary'} onClick={handleRevoke}>
|
||||||
{intl.formatMessage(messages.revoke)}
|
{intl.formatMessage(messages.revoke)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,6 +74,11 @@ const AuthTokenList: React.FC = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const tokens = useAppSelector(state => state.security.get('tokens').reverse());
|
const tokens = useAppSelector(state => state.security.get('tokens').reverse());
|
||||||
|
const currentTokenId = useAppSelector(state => {
|
||||||
|
const currentToken = state.auth.get('tokens').valueSeq().find((token: ImmutableMap<string, any>) => token.get('me') === state.auth.get('me'));
|
||||||
|
|
||||||
|
return currentToken?.get('id');
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchOAuthTokens());
|
dispatch(fetchOAuthTokens());
|
||||||
|
@ -63,7 +87,7 @@ const AuthTokenList: React.FC = () => {
|
||||||
const body = tokens ? (
|
const body = tokens ? (
|
||||||
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
|
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
|
||||||
{tokens.map((token) => (
|
{tokens.map((token) => (
|
||||||
<AuthToken key={token.id} token={token} />
|
<AuthToken key={token.id} token={token} isCurrent={token.id === currentTokenId} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : <Spinner />;
|
) : <Spinner />;
|
||||||
|
|
|
@ -39,6 +39,8 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
useAppSelector((state) => console.log(state.auth.toJS()));
|
||||||
|
|
||||||
const authUsers = useAppSelector((state) => state.auth.get('users'));
|
const authUsers = useAppSelector((state) => state.auth.get('users'));
|
||||||
const otherAccounts = useAppSelector((state) => authUsers.map((authUser: any) => getAccount(state, authUser.get('id'))));
|
const otherAccounts = useAppSelector((state) => authUsers.map((authUser: any) => getAccount(state, authUser.get('id'))));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue