2022-05-28 09:02:04 -07:00
import React , { useEffect } from 'react' ;
2022-05-12 08:45:40 -07:00
import { defineMessages , FormattedDate , useIntl } from 'react-intl' ;
2022-11-05 08:55:23 -07:00
import { openModal } from 'soapbox/actions/modals' ;
2022-05-12 08:45:40 -07:00
import { fetchOAuthTokens , revokeOAuthTokenById } from 'soapbox/actions/security' ;
2023-03-05 10:49:40 -08:00
import { Button , Card , CardBody , CardHeader , CardTitle , Column , HStack , Spinner , Stack , Text } from 'soapbox/components/ui' ;
2022-05-12 08:45:40 -07:00
import { useAppDispatch , useAppSelector } from 'soapbox/hooks' ;
import { Token } from 'soapbox/reducers/security' ;
const messages = defineMessages ( {
header : { id : 'security.headers.tokens' , defaultMessage : 'Sessions' } ,
revoke : { id : 'security.tokens.revoke' , defaultMessage : 'Revoke' } ,
2022-11-05 08:55:23 -07:00
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' } ,
2022-05-12 08:45:40 -07:00
} ) ;
interface IAuthToken {
2023-02-15 13:26:27 -08:00
token : Token
isCurrent : boolean
2022-05-12 08:45:40 -07:00
}
2022-11-05 08:55:23 -07:00
const AuthToken : React.FC < IAuthToken > = ( { token , isCurrent } ) = > {
2022-05-12 08:45:40 -07:00
const dispatch = useAppDispatch ( ) ;
const intl = useIntl ( ) ;
const handleRevoke = ( ) = > {
2022-11-05 08:55:23 -07:00
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 ) ) ;
}
2022-05-12 08:45:40 -07:00
} ;
return (
2023-02-01 14:13:42 -08:00
< div className = 'rounded-lg bg-gray-100 p-4 dark:bg-primary-800' >
2022-05-12 08:45:40 -07:00
< Stack space = { 2 } >
< Stack >
< Text size = 'md' weight = 'medium' > { token . app_name } < / Text >
2023-01-24 10:00:34 -08:00
{ token . valid_until && (
< Text size = 'sm' theme = 'muted' >
< FormattedDate
value = { token . valid_until }
hour12
year = 'numeric'
month = 'short'
day = '2-digit'
hour = 'numeric'
minute = '2-digit'
/ >
< / Text >
) }
2022-05-12 08:45:40 -07:00
< / Stack >
2023-03-05 10:49:40 -08:00
< HStack justifyContent = 'end' >
2022-11-05 08:55:23 -07:00
< Button theme = { isCurrent ? 'danger' : 'primary' } onClick = { handleRevoke } >
2022-05-12 08:45:40 -07:00
{ intl . formatMessage ( messages . revoke ) }
< / Button >
2023-03-05 10:49:40 -08:00
< / HStack >
2022-05-12 08:45:40 -07:00
< / Stack >
< / div >
) ;
} ;
2022-10-14 08:38:37 -07:00
const AuthTokenList : React.FC = ( ) = > {
2022-05-12 08:45:40 -07:00
const dispatch = useAppDispatch ( ) ;
const intl = useIntl ( ) ;
2022-05-14 01:37:06 -07:00
const tokens = useAppSelector ( state = > state . security . get ( 'tokens' ) . reverse ( ) ) ;
2022-11-05 08:55:23 -07:00
const currentTokenId = useAppSelector ( state = > {
2022-12-25 15:31:07 -08:00
const currentToken = state . auth . tokens . valueSeq ( ) . find ( ( token ) = > token . me === state . auth . me ) ;
2022-11-05 08:55:23 -07:00
2022-12-31 10:48:31 -08:00
return currentToken ? . id ;
2022-11-05 08:55:23 -07:00
} ) ;
2022-05-12 08:45:40 -07:00
useEffect ( ( ) = > {
dispatch ( fetchOAuthTokens ( ) ) ;
} , [ ] ) ;
const body = tokens ? (
2023-02-01 14:13:42 -08:00
< div className = 'grid grid-cols-1 gap-4 sm:grid-cols-2' >
2022-05-12 08:45:40 -07:00
{ tokens . map ( ( token ) = > (
2022-11-05 08:55:23 -07:00
< AuthToken key = { token . id } token = { token } isCurrent = { token . id === currentTokenId } / >
2022-05-12 08:45:40 -07:00
) ) }
< / div >
) : < Spinner / > ;
return (
< Column label = { intl . formatMessage ( messages . header ) } transparent withHeader = { false } >
< Card variant = 'rounded' >
< CardHeader backHref = '/settings' >
< CardTitle title = { intl . formatMessage ( messages . header ) } / >
< / CardHeader >
< CardBody >
{ body }
< / CardBody >
< / Card >
< / Column >
) ;
} ;
export default AuthTokenList ;