chore: move SessionToggle & SettingList to styled

pull/2425/head
Audric Ackermann 3 years ago
parent 36f929a1ca
commit 39cbbda609

@ -269,7 +269,7 @@ textarea {
/* CONVERSATION AND MESSAGES */
.module-conversation-header {
height: $main-view-header-height;
height: var(--main-view-header-height);
}
.module-conversation-header__title-flex,
@ -563,37 +563,9 @@ label {
}
.session-toggle {
width: 51px;
height: 31px;
border: 1.5px solid #e5e5ea;
border-radius: 16px;
position: relative;
cursor: pointer;
background-color: rgba(0, 0, 0, 0);
.knob {
position: absolute;
top: 0.5px;
left: 0.5px;
height: 27px;
width: 27px;
border-radius: 28px;
background-color: $session-color-white;
box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.05), 0 3px 1px 0 rgba(0, 0, 0, 0.05),
0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05);
transition: transform var(--default-duration) ease,
background-color var(--default-duration) ease;
}
&.active {
background-color: $session-color-green;
border-color: $session-color-green;
.knob {
transform: translateX(20px);
}
}
}
@ -830,68 +802,6 @@ label {
overflow-x: hidden;
}
&-header {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: $main-view-header-height;
background: var(--color-cell-background);
&-title {
line-height: $main-view-header-height;
font-weight: bold;
font-size: var(--font-size-lg);
text-align: center;
flex-grow: 1;
}
.session-button,
.session-icon-button {
margin-inline-end: var(--margins-lg);
}
}
&-item {
font-size: $session-font-md;
padding: var(--margins-lg);
margin-bottom: 20px;
background: var(--color-cell-background);
color: var(--color-text);
border-bottom: var(--border-session);
&.inline {
display: flex;
align-items: center;
justify-content: space-between;
}
&__info {
padding-inline-end: var(--margins-lg);
}
&__title {
line-height: 1.7;
font-size: var(--font-size-lg);
font-weight: bold;
}
&__description {
font-family: $session-font-default;
font-size: $session-font-sm;
font-weight: 100;
max-width: 700px;
color: var(--color-text-subtle);
}
&__selection {
.session-toggle {
transition: var(--default-duration);
}
}
}
&-view {
flex-grow: 1;
display: flex;
@ -900,24 +810,6 @@ label {
overflow: hidden;
}
&__version-info {
display: flex;
justify-content: space-between;
padding: var(--margins-sm) var(--margins-md);
background: none;
font-size: $session-font-xs;
span {
opacity: 0.4;
transition: var(--default-duration);
&:hover {
opacity: 1;
}
}
}
&__password-lock {
display: flex;
align-items: center;
@ -938,27 +830,6 @@ label {
border: 1px solid $session-shade-8;
border-radius: 5px;
h3 {
padding: 0px;
margin-bottom: var(--margins-lg);
}
input {
width: 100%;
background: var(--color-input-background);
color: var(--color-text);
padding: var(--margins-xs) var(--margins-md);
margin-bottom: var(--margins-lg);
outline: none;
border: none;
border-radius: 2px;
text-align: center;
font-size: 24px;
letter-spacing: 5px;
font-family: $session-font-default;
}
}
}
}

@ -148,7 +148,6 @@ $session-subtle-factor: 0.6;
// Default Components
$session-search-input-height: 34px;
$main-view-header-height: 63px;
$session-left-pane-width: 300px;
// Various Components

@ -67,7 +67,7 @@
padding: 0px var(--margins-md);
align-items: center;
justify-content: space-between;
height: $main-view-header-height;
height: var(--main-view-header-height);
background: var(--color-cell-background);
.close-button {

@ -86,7 +86,7 @@ $session-compose-margin: 20px;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: $main-view-header-height;
height: var(--main-view-header-height);
padding-inline-end: 7px;
transition: var(--default-duration);
}

@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import { updateConfirmModal } from '../../state/ducks/modalDialog';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
type Props = {
active: boolean;
@ -12,7 +12,7 @@ type Props = {
export const SessionToggle = (props: Props) => {
const dispatch = useDispatch();
const clickHandler = (event: any) => {
const clickHandler = (event: React.MouseEvent<HTMLDivElement>) => {
const stateManager = (e: any) => {
e.stopPropagation();
props.onClick();
@ -45,14 +45,39 @@ export const SessionToggle = (props: Props) => {
};
return (
<div className="session-settings-item__selection">
<div
className={classNames('session-toggle', props.active ? 'active' : '')}
role="button"
onClick={clickHandler}
>
<div className="knob" />
</div>
</div>
<StyledSessionToggle role="button" onClick={clickHandler} active={props.active}>
<StyledKnob active={props.active} />
</StyledSessionToggle>
);
};
const StyledKnob = styled.div<{ active: boolean }>`
position: absolute;
top: 0.5px;
left: 0.5px;
height: 27px;
width: 27px;
border-radius: 28px;
background-color: white;
box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.05), 0 3px 1px 0 rgba(0, 0, 0, 0.05),
0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05);
transition: transform var(--default-duration) ease, background-color var(--default-duration) ease;
transform: ${props => (props.active ? 'translateX(20px)' : '')};
`;
const StyledSessionToggle = styled.div<{ active: boolean }>`
width: 51px;
height: 31px;
border: 1.5px solid #e5e5ea;
border-radius: 16px;
position: relative;
cursor: pointer;
background-color: rgba(0, 0, 0, 0);
transition: var(--default-duration);
background-color: ${props => (props.active ? 'var(--color-accent)' : 'unset')};
border-color: ${props => (props.active ? 'var(--color-accent)' : 'unset')};
`;

@ -1,8 +1,8 @@
import React from 'react';
import classNames from 'classnames';
import { SessionButton, SessionButtonColor } from '../basic/SessionButton';
import { SessionToggle } from '../basic/SessionToggle';
import { SessionConfirmDialogProps } from '../dialog/SessionConfirm';
import styled from 'styled-components';
type ButtonSettingsProps = {
title?: string;
@ -13,36 +13,65 @@ type ButtonSettingsProps = {
onClick: () => void;
};
const StyledDescription = styled.div`
font-family: var(--font-default);
font-size: var(--font-size-sm);
font-weight: 100;
max-width: 700px;
color: var(--color-text-subtle);
`;
const StyledTitle = styled.div`
line-height: 1.7;
font-size: var(--font-size-lg);
font-weight: bold;
`;
const StyledInfo = styled.div`
padding-inline-end: var(--margins-lg);
`;
const SettingsTitleAndDescription = (props: { title?: string; description?: string }) => {
return (
<div className="session-settings-item__info">
<div className="session-settings-item__title">{props.title}</div>
<StyledInfo>
<StyledTitle>{props.title}</StyledTitle>
{props.description && (
<div className="session-settings-item__description">{props.description}</div>
)}
</div>
{props.description && <StyledDescription>{props.description}</StyledDescription>}
</StyledInfo>
);
};
const SessionSettingsContent = (props: { children: React.ReactNode }) => {
return <div className="session-settings-item__content">{props.children}</div>;
};
export const SessionSettingsItemWrapper = (props: {
inline: boolean;
title?: string;
description?: string;
children?: React.ReactNode;
}) => {
const ComponentToRender = props.inline ? StyledSettingItemInline : StyledSettingItem;
return (
<div className={classNames('session-settings-item', props.inline && 'inline')}>
<ComponentToRender>
<SettingsTitleAndDescription title={props.title} description={props.description} />
<SessionSettingsContent>{props.children}</SessionSettingsContent>
</div>
<div>{props.children}</div>
</ComponentToRender>
);
};
const StyledSettingItem = styled.div`
font-size: var(--font-size-md);
padding: var(--margins-lg);
margin-bottom: 20px;
background: var(--color-cell-background);
color: var(--color-text);
border-bottom: var(--border-session);
`;
const StyledSettingItemInline = styled(StyledSettingItem)`
display: flex;
align-items: center;
justify-content: space-between;
`;
export const SessionToggleWithDescription = (props: {
title?: string;
description?: string;

@ -11,10 +11,10 @@ import { SettingsCategoryPrivacy } from './section/CategoryPrivacy';
import { SettingsCategoryAppearance } from './section/CategoryAppearance';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
import { Data } from '../../data/data';
import { LocalizerKeys } from '../../types/LocalizerKeys';
import { matchesHash } from '../../util/passwordUtils';
import { SettingsCategoryPermissions } from './section/CategoryPermissions';
import { SettingsCategoryHelp } from './section/CategoryHelp';
import styled from 'styled-components';
export function getMediaPermissionsSettings() {
return window.getSettingValue('media-permissions');
@ -50,22 +50,62 @@ interface State {
shouldLockSettings: boolean | null;
}
const StyledVersionInfo = styled.div`
display: flex;
justify-content: space-between;
padding: var(--margins-sm) var(--margins-md);
background: none;
font-size: var(--font-size-xs);
`;
const StyledSpanSessionInfo = styled.span`
opacity: 0.4;
transition: var(--default-duration);
user-select: text;
:hover {
opacity: 1;
}
`;
const SessionInfo = () => {
const openOxenWebsite = () => {
void shell.openExternal('https://oxen.io/');
};
return (
<div className="session-settings__version-info">
<span className="text-selectable">v{window.versionInfo.version}</span>
<span>
<StyledVersionInfo>
<StyledSpanSessionInfo>v{window.versionInfo.version}</StyledSpanSessionInfo>
<StyledSpanSessionInfo>
<SessionIconButton iconSize="medium" iconType="oxen" onClick={openOxenWebsite} />
</span>
<span className="text-selectable">{window.versionInfo.commitHash}</span>
</div>
</StyledSpanSessionInfo>
<StyledSpanSessionInfo>{window.versionInfo.commitHash}</StyledSpanSessionInfo>
</StyledVersionInfo>
);
};
export const PasswordLock = ({
const StyledPasswordInput = styled.input`
width: 100%;
background: var(--color-input-background);
color: var(--color-text);
padding: var(--margins-xs) var(--margins-md);
margin-bottom: var(--margins-lg);
outline: none;
border: none;
border-radius: 2px;
text-align: center;
font-size: 24px;
letter-spacing: 5px;
font-family: var(--font-default);
`;
const StyledH3 = styled.h3`
padding: 0px;
margin-bottom: var(--margins-lg);
`;
const PasswordLock = ({
pwdLockError,
validatePasswordLock,
}: {
@ -75,13 +115,14 @@ export const PasswordLock = ({
return (
<div className="session-settings__password-lock">
<div className="session-settings__password-lock-box">
<h3>{window.i18n('password')}</h3>
<input
<StyledH3>{window.i18n('password')}</StyledH3>
<StyledPasswordInput
type="password"
id="password-lock-input"
defaultValue=""
placeholder="Password"
data-testid="password-lock-input"
autoFocus={true}
/>
{pwdLockError && <div className="session-label warning">{pwdLockError}</div>}
@ -97,6 +138,42 @@ export const PasswordLock = ({
);
};
const SettingInCategory = (props: {
category: SessionSettingCategory;
hasPassword: boolean;
onPasswordUpdated: (action: string) => void;
}) => {
const { category, hasPassword, onPasswordUpdated } = props;
if (hasPassword === null) {
return null;
}
switch (category) {
// special case for blocked user
case SessionSettingCategory.Conversations:
return <CategoryConversations />;
case SessionSettingCategory.Appearance:
return <SettingsCategoryAppearance hasPassword={hasPassword} />;
case SessionSettingCategory.Notifications:
return <SessionNotificationGroupSettings hasPassword={hasPassword} />;
case SessionSettingCategory.Privacy:
return (
<SettingsCategoryPrivacy onPasswordUpdated={onPasswordUpdated} hasPassword={hasPassword} />
);
case SessionSettingCategory.Help:
return <SettingsCategoryHelp hasPassword={hasPassword} />;
case SessionSettingCategory.Permissions:
return <SettingsCategoryPermissions hasPassword={hasPassword} />;
// those three down there have no options, they are just a button
case SessionSettingCategory.ClearData:
case SessionSettingCategory.MessageRequests:
case SessionSettingCategory.RecoveryPhrase:
default:
return null;
}
};
export class SessionSettingsView extends React.Component<SettingsViewProps, State> {
public settingsViewRef: React.RefObject<HTMLDivElement>;
@ -114,7 +191,11 @@ export class SessionSettingsView extends React.Component<SettingsViewProps, Stat
this.settingsViewRef = React.createRef();
autoBind(this);
void this.hasPassword();
void Data.getPasswordHash().then(hash => {
this.setState({
hasPassword: !!hash,
});
});
}
public componentDidMount() {
@ -123,51 +204,12 @@ export class SessionSettingsView extends React.Component<SettingsViewProps, Stat
const mediaSetting = getMediaPermissionsSettings();
const callMediaSetting = getCallMediaPermissionsSettings();
this.setState({ mediaSetting, callMediaSetting });
setTimeout(() => document.getElementById('password-lock-input')?.focus(), 100);
}
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
/* tslint:disable-next-line:max-func-body-length */
public renderSettingInCategory() {
const { category } = this.props;
if (this.state.hasPassword === null) {
return null;
}
const hasPassword = this.state.hasPassword;
switch (category) {
// special case for blocked user
case SessionSettingCategory.Conversations:
return <CategoryConversations />;
case SessionSettingCategory.Appearance:
return <SettingsCategoryAppearance hasPassword={hasPassword} />;
case SessionSettingCategory.Notifications:
return <SessionNotificationGroupSettings hasPassword={hasPassword} />;
case SessionSettingCategory.Privacy:
return (
<SettingsCategoryPrivacy
onPasswordUpdated={this.onPasswordUpdated}
hasPassword={hasPassword}
/>
);
case SessionSettingCategory.Help:
return <SettingsCategoryHelp hasPassword={hasPassword} />;
case SessionSettingCategory.Permissions:
return <SettingsCategoryPermissions hasPassword={hasPassword} />;
// those three down there have no options, they are just a button
case SessionSettingCategory.ClearData:
case SessionSettingCategory.MessageRequests:
case SessionSettingCategory.RecoveryPhrase:
default:
return null;
}
}
public async validatePasswordLock() {
const enteredPassword = String(
(document.getElementById('password-lock-input') as HTMLInputElement)?.value
@ -203,18 +245,10 @@ export class SessionSettingsView extends React.Component<SettingsViewProps, Stat
public render() {
const { category } = this.props;
const shouldRenderPasswordLock = this.state.shouldLockSettings && this.state.hasPassword;
const categoryLocalized: LocalizerKeys =
category === SessionSettingCategory.Appearance
? 'appearanceSettingsTitle'
: category === SessionSettingCategory.Conversations
? 'blockedSettingsTitle'
: category === SessionSettingCategory.Notifications
? 'notificationsSettingsTitle'
: 'privacySettingsTitle';
return (
<div className="session-settings">
<SettingsHeader category={category} categoryTitle={window.i18n(categoryLocalized)} />
<SettingsHeader category={category} />
<div className="session-settings-view">
{shouldRenderPasswordLock ? (
@ -224,7 +258,11 @@ export class SessionSettingsView extends React.Component<SettingsViewProps, Stat
/>
) : (
<div ref={this.settingsViewRef} className="session-settings-list">
{this.renderSettingInCategory()}
<SettingInCategory
category={category}
onPasswordUpdated={this.onPasswordUpdated}
hasPassword={Boolean(this.state.hasPassword)}
/>
</div>
)}
<SessionInfo />
@ -233,14 +271,6 @@ export class SessionSettingsView extends React.Component<SettingsViewProps, Stat
);
}
public async hasPassword() {
const hash = await Data.getPasswordHash();
this.setState({
hasPassword: !!hash,
});
}
public onPasswordUpdated(action: string) {
if (action === 'set' || action === 'change') {
this.setState({

@ -1,15 +1,60 @@
import React from 'react';
import { SettingsViewProps } from './SessionSettings';
import styled from 'styled-components';
import { LocalizerKeys } from '../../types/LocalizerKeys';
import { missingCaseError } from '../../util';
import { SessionSettingCategory, SettingsViewProps } from './SessionSettings';
type Props = Pick<SettingsViewProps, 'category'> & {
categoryTitle: string;
};
type Props = Pick<SettingsViewProps, 'category'>;
export const SettingsHeader = (props: Props) => {
const { categoryTitle } = props;
const { category } = props;
let categoryLocalized: LocalizerKeys | null = null;
switch (category) {
case SessionSettingCategory.Appearance:
categoryLocalized = 'appearanceSettingsTitle';
break;
case SessionSettingCategory.Conversations:
categoryLocalized = 'blockedSettingsTitle';
break;
case SessionSettingCategory.Notifications:
categoryLocalized = 'notificationsSettingsTitle';
break;
case SessionSettingCategory.Help:
categoryLocalized = 'helpSettingsTitle';
break;
case SessionSettingCategory.Permissions:
categoryLocalized = 'permissionsSettingsTitle';
break;
case SessionSettingCategory.Privacy:
categoryLocalized = 'privacySettingsTitle';
break;
default:
throw missingCaseError('SettingsHeader' as never);
}
const categoryTitle = window.i18n(categoryLocalized);
return (
<div className="session-settings-header">
<div className="session-settings-header-title">{categoryTitle}</div>
</div>
<StyledSettingsHeader>
<StyledHeaderTittle>{categoryTitle}</StyledHeaderTittle>
</StyledSettingsHeader>
);
};
const StyledSettingsHeader = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: var(--main-view-header-height);
background: var(--color-cell-background);
`;
const StyledHeaderTittle = styled.div`
line-height: var(--main-view-header-height);
font-weight: bold;
font-size: var(--font-size-lg);
text-align: center;
flex-grow: 1;
`;

@ -331,6 +331,9 @@ export const SessionGlobalStyles = createGlobalStyle`
--margins-md: 15px;
--margins-lg: 20px;
/* SIZES */
--main-view-header-height: 63px;
/* ANIMATIONS */
--default-duration: 0.25s;
/* FILTERS */

Loading…
Cancel
Save