feat: implement UI for logging out (#638)

* feat: implement UI for logging out

* feat: use old style dialogs for logout confirmation

* feat: implement manage sessions

* feat: implement session logout success dialog

* feat: use snjs alert for revoking sessions confirmation

* fix: make OtherSessionsLogout easier to read
This commit is contained in:
Gorjan Petrovski
2021-09-21 19:01:32 +02:00
committed by GitHub
parent a9610fdbc6
commit 77525a56cd
19 changed files with 342 additions and 70 deletions

View File

@@ -7,9 +7,11 @@ import { PreferencesMenu } from './PreferencesMenu';
import { PreferencesMenuView } from './PreferencesMenuView';
import { WebApplication } from '@/ui_models/application';
import { MfaProps } from './panes/two-factor-auth/MfaProps';
import { AppState } from '@/ui_models/app_state';
interface PreferencesProps extends MfaProps {
application: WebApplication;
appState: AppState;
closePreferences: () => void;
}
@@ -20,7 +22,12 @@ const PaneSelector: FunctionComponent<
case 'general':
return null;
case 'account':
return <AccountPreferences application={props.application} />;
return (
<AccountPreferences
application={props.application}
appState={props.appState}
/>
);
case 'appearance':
return null;
case 'security':

View File

@@ -2,9 +2,10 @@ import { FunctionComponent } from 'preact';
import { observer } from 'mobx-react-lite';
import { WebApplication } from '@/ui_models/application';
import { PreferencesView } from './PreferencesView';
import { AppState } from '@/ui_models/app_state';
export interface PreferencesViewWrapperProps {
appState: { preferences: { isOpen: boolean; closePreferences: () => void } };
appState: AppState;
application: WebApplication;
}
@@ -18,6 +19,7 @@ export const PreferencesViewWrapper: FunctionComponent<PreferencesViewWrapperPro
<PreferencesView
closePreferences={() => appState.preferences.closePreferences()}
application={application}
appState={appState}
mfaProvider={application}
userProvider={application}
/>

View File

@@ -0,0 +1,24 @@
import { FunctionComponent } from 'preact';
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
const HorizontalLine: FunctionComponent<{ index: number; length: number }> = ({
index,
length,
}) => (index < length - 1 ? <HorizontalSeparator classes="my-4" /> : null);
export const PreferencesGroup: FunctionComponent = ({ children }) => (
<div className="bg-default border-1 border-solid rounded border-gray-300 px-6 py-6 flex flex-col">
{Array.isArray(children)
? children
.filter(
(child) => child != undefined && child !== '' && child !== false
)
.map((child, i, arr) => (
<>
{child}
<HorizontalLine index={i} length={arr.length} />
</>
))
: children}
</div>
);

View File

@@ -1,43 +1,18 @@
import { FunctionComponent } from 'preact';
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
const HorizontalLine: FunctionComponent<{ index: number; length: number }> = ({
index,
length,
}) => (index < length - 1 ? <HorizontalSeparator classes="my-4" /> : null);
export const PreferencesSegment: FunctionComponent = ({ children }) => (
<div className="flex flex-col">{children}</div>
);
export const PreferencesGroup: FunctionComponent = ({ children }) => (
<div className="bg-default border-1 border-solid rounded border-gray-300 px-6 py-6 flex flex-col">
{Array.isArray(children)
? children
.filter(
(child) => child != undefined && child !== '' && child !== false
)
.map((child, i, arr) => (
<>
{child}
<HorizontalLine index={i} length={arr.length} />
</>
))
: children}
</div>
);
export const PreferencesPane: FunctionComponent = ({ children }) => (
<div className="color-black flex-grow flex flex-row overflow-y-auto min-h-0">
<div className="flex-grow flex flex-col py-6 items-center">
<div className="w-125 max-w-125 flex flex-col">
{children != undefined && Array.isArray(children)
? children.map((child, idx, arr) => (
<>
{child}
{idx < arr.length - 1 ? <div className="min-h-3" /> : undefined}
</>
))
? children
.filter((child) => child != undefined)
.map((child) => (
<>
{child}
<div className="min-h-3" />
</>
))
: children}
</div>
</div>

View File

@@ -0,0 +1,5 @@
import { FunctionComponent } from 'preact';
export const PreferencesSegment: FunctionComponent = ({ children }) => (
<div className="flex flex-col">{children}</div>
);

View File

@@ -1,3 +1,5 @@
export * from './Content';
export * from './MenuItem';
export * from './PreferencesPane';
export * from './PreferencesGroup';
export * from './PreferencesSegment';

View File

@@ -1,17 +1,28 @@
import { Sync, SubscriptionWrapper, Credentials } from '@/preferences/panes/account';
import {
Sync,
SubscriptionWrapper,
Credentials,
LogOutWrapper,
} from '@/preferences/panes/account';
import { PreferencesPane } from '@/preferences/components';
import { observer } from 'mobx-react-lite';
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
type Props = {
application: WebApplication;
}
export const AccountPreferences = observer(({application}: Props) => {
return (
<PreferencesPane>
<Credentials application={application} />
<Sync application={application} />
<SubscriptionWrapper application={application} />
</PreferencesPane>
);
});
appState: AppState;
};
export const AccountPreferences = observer(
({ application, appState }: Props) => {
return (
<PreferencesPane>
<Credentials application={application} />
<Sync application={application} />
<SubscriptionWrapper application={application} />
<LogOutWrapper application={application} appState={appState} />
</PreferencesPane>
);
}
);

View File

@@ -0,0 +1,103 @@
import { Button } from '@/components/Button';
import { ConfirmSignoutContainer } from '@/components/ConfirmSignoutModal';
import { OtherSessionsLogoutContainer } from '@/components/OtherSessionsLogout';
import {
PreferencesGroup,
PreferencesSegment,
Subtitle,
Text,
Title,
} from '@/preferences/components';
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
const LogOutView: FunctionComponent<{
application: WebApplication;
appState: AppState;
}> = observer(({ application, appState }) => {
return (
<>
<PreferencesGroup>
<PreferencesSegment>
<Title>Log out</Title>
<div className="min-h-2" />
<Subtitle>Other devices</Subtitle>
<Text>Want to log out on all devices except this one?</Text>
<div className="min-h-3" />
<div className="flex flex-row">
<Button
className="mr-3"
type="normal"
label="Log out other sessions"
onClick={() => {
appState.accountMenu.setOtherSessionsLogout(true);
}}
/>
<Button type="normal" label="Manage sessions" onClick={() => appState.openSessionsModal()} />
</div>
</PreferencesSegment>
<PreferencesSegment>
<Subtitle>This device</Subtitle>
<Text>This will delete all local items and preferences.</Text>
<div className="min-h-3" />
<Button
type="danger"
label="Log out and clear local data"
onClick={() => {
appState.accountMenu.setSigningOut(true);
}}
/>
</PreferencesSegment>
</PreferencesGroup>
<OtherSessionsLogoutContainer appState={appState} application={application} />
<ConfirmSignoutContainer
appState={appState}
application={application}
/>
</>
);
});
const ClearSessionDataView: FunctionComponent<{
application: WebApplication;
appState: AppState;
}> = observer(({ application, appState }) => {
return (
<>
<PreferencesGroup>
<PreferencesSegment>
<Title>Clear Session Data</Title>
<div className="min-h-2" />
<Text>This will delete all local items and preferences.</Text>
<div className="min-h-3" />
<Button
type="danger"
label="Clear Session Data"
onClick={() => {
appState.accountMenu.setSigningOut(true);
}}
/>
</PreferencesSegment>
</PreferencesGroup>
<ConfirmSignoutContainer
appState={appState}
application={application}
/>
</>);
});
export const LogOutWrapper: FunctionComponent<{
application: WebApplication;
appState: AppState;
}> = observer(({ application, appState }) => {
const isLoggedIn = application.getUser() != undefined;
if (!isLoggedIn) return <ClearSessionDataView appState={appState} application={application} />;
return <LogOutView appState={appState} application={application} />;
});

View File

@@ -1,3 +1,4 @@
export { SubscriptionWrapper } from './subscription/SubscriptionWrapper';
export { Sync } from './Sync';
export { Credentials } from './Credentials';
export { LogOutWrapper } from './LogOutView';