153 lines
4.8 KiB
TypeScript
153 lines
4.8 KiB
TypeScript
import React, {
|
|
FC, memo, useCallback, useEffect, useMemo,
|
|
} from '../../../lib/teact/teact';
|
|
import { withGlobal } from '../../../lib/teact/teactn';
|
|
|
|
import { GlobalActions } from '../../../global/types';
|
|
import { ApiSession } from '../../../api/types';
|
|
|
|
import { pick } from '../../../util/iteratees';
|
|
import { formatPastTimeShort } from '../../../util/dateFormat';
|
|
import useFlag from '../../../hooks/useFlag';
|
|
import useLang from '../../../hooks/useLang';
|
|
|
|
import ListItem from '../../ui/ListItem';
|
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
|
|
|
type StateProps = {
|
|
activeSessions: ApiSession[];
|
|
};
|
|
|
|
type DispatchProps = Pick<GlobalActions, (
|
|
'loadAuthorizations' | 'terminateAuthorization' | 'terminateAllAuthorizations'
|
|
)>;
|
|
|
|
const SettingsPrivacyActiveSessions: FC<StateProps & DispatchProps> = ({
|
|
activeSessions,
|
|
loadAuthorizations,
|
|
terminateAuthorization,
|
|
terminateAllAuthorizations,
|
|
}) => {
|
|
const [isConfirmTerminateAllDialogOpen, openConfirmTerminateAllDialog, closeConfirmTerminateAllDialog] = useFlag();
|
|
useEffect(() => {
|
|
loadAuthorizations();
|
|
}, [loadAuthorizations]);
|
|
|
|
const handleTerminateSessionClick = useCallback((hash: string) => {
|
|
terminateAuthorization({ hash });
|
|
}, [terminateAuthorization]);
|
|
|
|
const handleTerminateAllSessions = useCallback(() => {
|
|
closeConfirmTerminateAllDialog();
|
|
terminateAllAuthorizations();
|
|
}, [closeConfirmTerminateAllDialog, terminateAllAuthorizations]);
|
|
|
|
const currentSession = useMemo(() => {
|
|
return activeSessions.find((session) => session.isCurrent);
|
|
}, [activeSessions]);
|
|
|
|
const otherSessions = useMemo(() => {
|
|
return activeSessions.filter((session) => !session.isCurrent);
|
|
}, [activeSessions]);
|
|
|
|
const lang = useLang();
|
|
|
|
function renderCurrentSession(session: ApiSession) {
|
|
return (
|
|
<div className="settings-item">
|
|
<h4 className="settings-item-header mb-4" dir={lang.isRtl ? 'rtl' : undefined}>
|
|
{lang('AuthSessions.CurrentSession')}
|
|
</h4>
|
|
|
|
<ListItem narrow inactive>
|
|
<div className="multiline-menu-item" dir="auto">
|
|
<span className="title" dir="auto">{session.appName}</span>
|
|
<span className="subtitle black tight">{getDeviceEnvironment(session)}</span>
|
|
<span className="subtitle">{session.ip} - {getLocation(session)}</span>
|
|
</div>
|
|
</ListItem>
|
|
|
|
<ListItem
|
|
className="destructive mb-0"
|
|
icon="stop"
|
|
ripple
|
|
narrow
|
|
onClick={openConfirmTerminateAllDialog}
|
|
>
|
|
{lang('TerminateAllSessions')}
|
|
</ListItem>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function renderOtherSessions(sessions: ApiSession[]) {
|
|
return (
|
|
<div className="settings-item">
|
|
<h4 className="settings-item-header mb-4" dir={lang.isRtl ? 'rtl' : undefined}>Other Sessions</h4>
|
|
|
|
{sessions.map(renderSession)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function renderSession(session: ApiSession) {
|
|
return (
|
|
<ListItem
|
|
key={session.hash}
|
|
ripple
|
|
narrow
|
|
contextActions={[{
|
|
title: 'Terminate',
|
|
icon: 'stop',
|
|
handler: () => {
|
|
handleTerminateSessionClick(session.hash);
|
|
},
|
|
}]}
|
|
>
|
|
<div className="multiline-menu-item full-size" dir="auto">
|
|
<span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span>
|
|
<span className="title">{session.appName}</span>
|
|
<span className="subtitle black tight">{getDeviceEnvironment(session)}</span>
|
|
<span className="subtitle">{session.ip} - {getLocation(session)}</span>
|
|
</div>
|
|
</ListItem>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="settings-content custom-scroll">
|
|
{currentSession && renderCurrentSession(currentSession)}
|
|
{otherSessions && renderOtherSessions(otherSessions)}
|
|
{otherSessions && (
|
|
<ConfirmDialog
|
|
isOpen={isConfirmTerminateAllDialogOpen}
|
|
onClose={closeConfirmTerminateAllDialog}
|
|
text="Are you sure you want to terminate all other sessions?"
|
|
confirmLabel="Terminate All Other Sessions"
|
|
confirmHandler={handleTerminateAllSessions}
|
|
confirmIsDestructive
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function getLocation(session: ApiSession) {
|
|
return [session.region, session.country].filter(Boolean).join(', ');
|
|
}
|
|
|
|
function getDeviceEnvironment(session: ApiSession) {
|
|
return `${session.deviceModel}${session.deviceModel ? ', ' : ''} ${session.platform} ${session.systemVersion}`;
|
|
}
|
|
|
|
export default memo(withGlobal(
|
|
(global): StateProps => {
|
|
return {
|
|
activeSessions: global.activeSessions,
|
|
};
|
|
},
|
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
|
'loadAuthorizations', 'terminateAuthorization', 'terminateAllAuthorizations',
|
|
]),
|
|
)(SettingsPrivacyActiveSessions));
|