mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-20 02:08:57 +00:00
Settings Tab: Better layout for narrow windows
This commit is contained in:
@@ -141,12 +141,17 @@ $NavTabs__ProfileAvatar__size: 28px;
|
|||||||
.NavTabs__ItemUpdateBadge {
|
.NavTabs__ItemUpdateBadge {
|
||||||
background: variables.$color-ultramarine;
|
background: variables.$color-ultramarine;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
border: 1px solid variables.$color-white;
|
|
||||||
height: 8px;
|
height: 8px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
|
@include mixins.light-theme {
|
||||||
|
border: 1px solid variables.$color-white;
|
||||||
|
}
|
||||||
|
@include mixins.dark-theme {
|
||||||
|
border: 1px solid variables.$color-gray-80;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.NavTabs__ItemIcon {
|
.NavTabs__ItemIcon {
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ $secondary-text-color: light-dark(
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
container-type: inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__settings-pane {
|
&__settings-pane {
|
||||||
@@ -374,6 +375,73 @@ $secondary-text-color: light-dark(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__light-icon-label {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__flow-control {
|
||||||
|
display: block;
|
||||||
|
padding-block: 4px;
|
||||||
|
padding-inline: 24px;
|
||||||
|
}
|
||||||
|
&__one-third-flow {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: 33%;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__half-flow {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: 50%;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__two-thirds-flow {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: 66%;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__half-flow--align-right {
|
||||||
|
text-align: end;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__one-third-flow--align-right {
|
||||||
|
text-align: end;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__full-flow {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&__flow-value,
|
||||||
|
&__flow-description {
|
||||||
|
vertical-align: middle;
|
||||||
|
color: $secondary-text-color;
|
||||||
|
}
|
||||||
|
&__device-name-description {
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
&__flow-button {
|
||||||
|
padding-inline-start: 5px;
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
padding-inline-start: 0px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__control {
|
&__control {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -432,9 +500,16 @@ $secondary-text-color: light-dark(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__checkbox__description,
|
||||||
&__description {
|
&__description {
|
||||||
@include mixins.font-subtitle;
|
@include mixins.font-subtitle;
|
||||||
color: $secondary-text-color;
|
// For specificity reasons, we can't use $secondary-text-color. We need the mixins.
|
||||||
|
@include mixins.light-theme {
|
||||||
|
color: variables.$color-gray-60;
|
||||||
|
}
|
||||||
|
@include mixins.dark-theme {
|
||||||
|
color: variables.$color-gray-25;
|
||||||
|
}
|
||||||
&--error {
|
&--error {
|
||||||
color: variables.$color-accent-red !important;
|
color: variables.$color-accent-red !important;
|
||||||
}
|
}
|
||||||
|
|||||||
56
ts/components/NavTabs.stories.tsx
Normal file
56
ts/components/NavTabs.stories.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import type { Meta } from '@storybook/react';
|
||||||
|
import type { NavTabsProps } from './NavTabs';
|
||||||
|
import { NavTabs } from './NavTabs';
|
||||||
|
import { NavTab } from '../state/ducks/nav';
|
||||||
|
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||||
|
import { ThemeType } from '../types/Util';
|
||||||
|
|
||||||
|
const { i18n } = window.SignalContext;
|
||||||
|
|
||||||
|
const createProps = (
|
||||||
|
overrideProps: Partial<NavTabsProps> = {}
|
||||||
|
): NavTabsProps => ({
|
||||||
|
badge: overrideProps.badge,
|
||||||
|
hasFailedStorySends: Boolean(overrideProps.hasFailedStorySends),
|
||||||
|
hasPendingUpdate: Boolean(overrideProps.hasPendingUpdate),
|
||||||
|
i18n,
|
||||||
|
me: getDefaultConversation(),
|
||||||
|
navTabsCollapsed: Boolean(overrideProps.navTabsCollapsed),
|
||||||
|
onChangeLocation: action('onChangeLocation'),
|
||||||
|
onToggleNavTabsCollapse: action('onToggleNavTabsCollapse'),
|
||||||
|
renderCallsTab: () => <div>Calls Tab goes here</div>,
|
||||||
|
renderChatsTab: () => <div>Chats Tab goes here</div>,
|
||||||
|
renderStoriesTab: () => <div>Stories Tab goes here</div>,
|
||||||
|
renderSettingsTab: () => <div>Settings Tab goes here</div>,
|
||||||
|
selectedNavTab: overrideProps.selectedNavTab ?? NavTab.Chats,
|
||||||
|
shouldShowProfileIcon: Boolean(overrideProps.shouldShowProfileIcon),
|
||||||
|
storiesEnabled: Boolean(overrideProps.storiesEnabled),
|
||||||
|
theme: overrideProps.theme ?? ThemeType.light,
|
||||||
|
unreadCallsCount: overrideProps.unreadCallsCount ?? 0,
|
||||||
|
unreadConversationsStats: overrideProps.unreadConversationsStats ?? {
|
||||||
|
unreadCount: 0,
|
||||||
|
unreadMentionsCount: 0,
|
||||||
|
markedUnread: false,
|
||||||
|
},
|
||||||
|
unreadStoriesCount: overrideProps.unreadStoriesCount ?? 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/NavTabs',
|
||||||
|
} satisfies Meta<NavTabsProps>;
|
||||||
|
|
||||||
|
export function HasPendingUpdate(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<NavTabs
|
||||||
|
{...createProps({
|
||||||
|
hasPendingUpdate: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ import { FunSkinTonesList } from './fun/FunSkinTones';
|
|||||||
import { emojiParentKeyConstant, type EmojiSkinTone } from './fun/data/emojis';
|
import { emojiParentKeyConstant, type EmojiSkinTone } from './fun/data/emojis';
|
||||||
import {
|
import {
|
||||||
SettingsControl as Control,
|
SettingsControl as Control,
|
||||||
|
FlowingSettingsControl as FlowingControl,
|
||||||
SettingsRadio,
|
SettingsRadio,
|
||||||
SettingsRow,
|
SettingsRow,
|
||||||
} from './PreferencesUtil';
|
} from './PreferencesUtil';
|
||||||
@@ -672,27 +673,43 @@ export function Preferences({
|
|||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={i18n('icu:Preferences--phone-number')}
|
<div className="Preferences__half-flow">
|
||||||
right={phoneNumber}
|
{i18n('icu:Preferences--phone-number')}
|
||||||
rightStyle={{
|
</div>
|
||||||
maxWidth: '33%',
|
<div
|
||||||
}}
|
className={classNames(
|
||||||
/>
|
'Preferences__flow-value',
|
||||||
<Control
|
'Preferences__half-flow',
|
||||||
left={
|
'Preferences__half-flow--align-right'
|
||||||
<>
|
)}
|
||||||
<div>{i18n('icu:Preferences--device-name')}</div>
|
>
|
||||||
<div className="Preferences__description">
|
{phoneNumber}
|
||||||
{i18n('icu:Preferences--device-name__description')}
|
</div>
|
||||||
</div>
|
</FlowingControl>
|
||||||
</>
|
<FlowingControl>
|
||||||
}
|
<div className="Preferences__half-flow">
|
||||||
right={deviceName}
|
{i18n('icu:Preferences--device-name')}
|
||||||
rightStyle={{
|
</div>
|
||||||
maxWidth: '33%',
|
<div
|
||||||
}}
|
className={classNames(
|
||||||
/>
|
'Preferences__flow-value',
|
||||||
|
'Preferences__half-flow',
|
||||||
|
'Preferences__half-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{deviceName}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__device-name-description',
|
||||||
|
'Preferences__description',
|
||||||
|
'Preferences__full-flow'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{i18n('icu:Preferences--device-name__description')}
|
||||||
|
</div>
|
||||||
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow title={i18n('icu:Preferences--system')}>
|
<SettingsRow title={i18n('icu:Preferences--system')}>
|
||||||
{isAutoLaunchSupported && (
|
{isAutoLaunchSupported && (
|
||||||
@@ -1374,24 +1391,34 @@ export function Preferences({
|
|||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div
|
||||||
<div className="Preferences__pnp">
|
className={classNames(
|
||||||
<h3>{i18n('icu:Preferences__pnp__row--title')}</h3>
|
'Preferences__pnp',
|
||||||
<div className="Preferences__description">
|
'Preferences__two-thirds-flow'
|
||||||
{i18n('icu:Preferences__pnp__row--body')}
|
)}
|
||||||
</div>
|
>
|
||||||
|
<h3>{i18n('icu:Preferences__pnp__row--title')}</h3>
|
||||||
|
<div className="Preferences__description">
|
||||||
|
{i18n('icu:Preferences__pnp__row--body')}
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
right={
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__pnp',
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.PNP)}
|
onClick={() => setPage(Page.PNP)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__pnp__row--button')}
|
{i18n('icu:Preferences__pnp__row--button')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
<Control
|
<Control
|
||||||
@@ -1433,18 +1460,22 @@ export function Preferences({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<SettingsRow title={i18n('icu:disappearingMessages')}>
|
<SettingsRow title={i18n('icu:disappearingMessages')}>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div className="Preferences__two-thirds-flow">
|
||||||
<>
|
<div>
|
||||||
<div>
|
{i18n('icu:settings__DisappearingMessages__timer__label')}
|
||||||
{i18n('icu:settings__DisappearingMessages__timer__label')}
|
</div>
|
||||||
</div>
|
<div className="Preferences__description">
|
||||||
<div className="Preferences__description">
|
{i18n('icu:settings__DisappearingMessages__footer')}
|
||||||
{i18n('icu:settings__DisappearingMessages__footer')}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div
|
||||||
}
|
className={classNames(
|
||||||
right={
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
ariaLabel={i18n(
|
ariaLabel={i18n(
|
||||||
'icu:settings__DisappearingMessages__timer__label'
|
'icu:settings__DisappearingMessages__timer__label'
|
||||||
@@ -1480,8 +1511,8 @@ export function Preferences({
|
|||||||
])}
|
])}
|
||||||
value={universalExpireTimer}
|
value={universalExpireTimer}
|
||||||
/>
|
/>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
{isContentProtectionSupported && (
|
{isContentProtectionSupported && (
|
||||||
<SettingsRow title={i18n('icu:Preferences__Privacy__Application')}>
|
<SettingsRow title={i18n('icu:Preferences__Privacy__Application')}>
|
||||||
@@ -1520,17 +1551,23 @@ export function Preferences({
|
|||||||
</ConfirmationDialog>
|
</ConfirmationDialog>
|
||||||
) : null}
|
) : null}
|
||||||
<SettingsRow title={i18n('icu:Stories__title')}>
|
<SettingsRow title={i18n('icu:Stories__title')}>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div className="Preferences__two-thirds-flow">
|
||||||
<label htmlFor={storiesId}>
|
<label htmlFor={storiesId}>
|
||||||
<div>{i18n('icu:Stories__settings-toggle--title')}</div>
|
<div>{i18n('icu:Stories__settings-toggle--title')}</div>
|
||||||
<div className="Preferences__description">
|
<div className="Preferences__description">
|
||||||
{i18n('icu:Stories__settings-toggle--description')}
|
{i18n('icu:Stories__settings-toggle--description')}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
</div>
|
||||||
right={
|
<div
|
||||||
hasStoriesDisabled ? (
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{hasStoriesDisabled ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onHasStoriesDisabledChanged(false)}
|
onClick={() => onHasStoriesDisabledChanged(false)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
@@ -1545,31 +1582,40 @@ export function Preferences({
|
|||||||
>
|
>
|
||||||
{i18n('icu:Preferences__turn-stories-off')}
|
{i18n('icu:Preferences__turn-stories-off')}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)}
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div
|
||||||
<>
|
className={classNames(
|
||||||
<div>{i18n('icu:clearDataHeader')}</div>
|
'Preferences__pnp',
|
||||||
<div className="Preferences__description">
|
'Preferences__two-thirds-flow'
|
||||||
{i18n('icu:clearDataExplanation')}
|
)}
|
||||||
</div>
|
>
|
||||||
</>
|
<div>{i18n('icu:clearDataHeader')}</div>
|
||||||
}
|
<div className="Preferences__description">
|
||||||
right={
|
{i18n('icu:clearDataExplanation')}
|
||||||
<div className="Preferences__right-button">
|
|
||||||
<Button
|
|
||||||
onClick={() => setConfirmDelete(true)}
|
|
||||||
variant={ButtonVariant.SecondaryDestructive}
|
|
||||||
>
|
|
||||||
{i18n('icu:clearDataButton')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
/>
|
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__pnp',
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => setConfirmDelete(true)}
|
||||||
|
variant={ButtonVariant.SecondaryDestructive}
|
||||||
|
>
|
||||||
|
{i18n('icu:clearDataButton')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
{confirmDelete ? (
|
{confirmDelete ? (
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
@@ -1681,23 +1727,28 @@ export function Preferences({
|
|||||||
</div>
|
</div>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div className="Preferences__two-thirds-flow">
|
||||||
<>
|
<div className="Preferences__option-name">
|
||||||
<div className="Preferences__option-name">
|
{i18n('icu:Preferences__sent-media-quality')}
|
||||||
{i18n('icu:Preferences__sent-media-quality')}
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
className={classNames(
|
||||||
className={classNames(
|
'Preferences__description',
|
||||||
'Preferences__description',
|
'Preferences__description--medium'
|
||||||
'Preferences__description--medium'
|
)}
|
||||||
)}
|
>
|
||||||
>
|
{i18n('icu:Preferences__sent-media-quality__description')}
|
||||||
{i18n('icu:Preferences__sent-media-quality__description')}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
}
|
<div
|
||||||
right={
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
onChange={onSentMediaQualityChange}
|
onChange={onSentMediaQualityChange}
|
||||||
options={[
|
options={[
|
||||||
@@ -1712,8 +1763,8 @@ export function Preferences({
|
|||||||
]}
|
]}
|
||||||
value={sentMediaQualitySetting}
|
value={sentMediaQualitySetting}
|
||||||
/>
|
/>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,13 +2,20 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BackupsSubscriptionType,
|
BackupsSubscriptionType,
|
||||||
BackupStatusType,
|
BackupStatusType,
|
||||||
} from '../types/backups';
|
} from '../types/backups';
|
||||||
import type { LocalizerType } from '../types/I18N';
|
import type { LocalizerType } from '../types/I18N';
|
||||||
import { formatTimestamp } from '../util/formatTimestamp';
|
import { formatTimestamp } from '../util/formatTimestamp';
|
||||||
import { SettingsControl as Control, SettingsRow } from './PreferencesUtil';
|
import {
|
||||||
|
SettingsControl as Control,
|
||||||
|
FlowingSettingsControl as FlowingControl,
|
||||||
|
LightIconLabel,
|
||||||
|
SettingsRow,
|
||||||
|
} from './PreferencesUtil';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
||||||
@@ -97,29 +104,36 @@ export function PreferencesBackups({
|
|||||||
|
|
||||||
{backupSubscriptionStatus ? (
|
{backupSubscriptionStatus ? (
|
||||||
<SettingsRow className="Preferences--BackupsRow">
|
<SettingsRow className="Preferences--BackupsRow">
|
||||||
<Control
|
<FlowingControl>
|
||||||
icon="Preferences__BackupsIcon"
|
<div className="Preferences__two-thirds-flow">
|
||||||
left={
|
<LightIconLabel icon="Preferences__BackupsIcon">
|
||||||
<label>
|
<label>
|
||||||
{i18n('icu:Preferences--signal-backups')}{' '}
|
{i18n('icu:Preferences--signal-backups')}{' '}
|
||||||
<div className="Preferences__description">
|
<div className="Preferences__description">
|
||||||
{renderBackupsSubscriptionSummary({
|
{renderBackupsSubscriptionSummary({
|
||||||
subscriptionStatus: backupSubscriptionStatus,
|
subscriptionStatus: backupSubscriptionStatus,
|
||||||
i18n,
|
i18n,
|
||||||
locale,
|
locale,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
</LightIconLabel>
|
||||||
right={
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.BackupsDetails)}
|
onClick={() => setPage(Page.BackupsDetails)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--manage')}
|
{i18n('icu:Preferences__button--manage')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
) : (
|
) : (
|
||||||
<SettingsRow className="Preferences--BackupsRow">
|
<SettingsRow className="Preferences--BackupsRow">
|
||||||
@@ -148,19 +162,26 @@ export function PreferencesBackups({
|
|||||||
className="Preferences--BackupsRow"
|
className="Preferences--BackupsRow"
|
||||||
title={i18n('icu:Preferences__backup-other-ways')}
|
title={i18n('icu:Preferences__backup-other-ways')}
|
||||||
>
|
>
|
||||||
<Control
|
<FlowingControl>
|
||||||
icon="Preferences__LocalBackupsIcon"
|
<div className="Preferences__two-thirds-flow">
|
||||||
left={
|
<LightIconLabel icon="Preferences__LocalBackupsIcon">
|
||||||
<label>
|
<label>
|
||||||
{i18n('icu:Preferences__local-backups')}{' '}
|
{i18n('icu:Preferences__local-backups')}{' '}
|
||||||
<div className="Preferences__description">
|
<div className="Preferences__description">
|
||||||
{isLocalBackupsSetup
|
{isLocalBackupsSetup
|
||||||
? null
|
? null
|
||||||
: i18n('icu:Preferences--local-backups-off-description')}
|
: i18n('icu:Preferences--local-backups-off-description')}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
</LightIconLabel>
|
||||||
right={
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.LocalBackups)}
|
onClick={() => setPage(Page.LocalBackups)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
@@ -169,8 +190,8 @@ export function PreferencesBackups({
|
|||||||
? i18n('icu:Preferences__button--manage')
|
? i18n('icu:Preferences__button--manage')
|
||||||
: i18n('icu:Preferences__button--set-up')}
|
: i18n('icu:Preferences__button--set-up')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import type { LocalizerType } from '../types/I18N';
|
import type { LocalizerType } from '../types/I18N';
|
||||||
import { toLogFormat } from '../types/errors';
|
import { toLogFormat } from '../types/errors';
|
||||||
import { formatFileSize } from '../util/formatFileSize';
|
import { formatFileSize } from '../util/formatFileSize';
|
||||||
import { SECOND } from '../util/durations';
|
import { SECOND } from '../util/durations';
|
||||||
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
||||||
import { SettingsRow, SettingsControl } from './PreferencesUtil';
|
import { SettingsRow, FlowingSettingsControl } from './PreferencesUtil';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
import type { MessageCountBySchemaVersionType } from '../sql/Interface';
|
import type { MessageCountBySchemaVersionType } from '../sql/Interface';
|
||||||
@@ -124,9 +126,17 @@ export function PreferencesInternal({
|
|||||||
className="Preferences--internal--backups"
|
className="Preferences--internal--backups"
|
||||||
title={i18n('icu:Preferences__button--backups')}
|
title={i18n('icu:Preferences__button--backups')}
|
||||||
>
|
>
|
||||||
<SettingsControl
|
<FlowingSettingsControl>
|
||||||
left={i18n('icu:Preferences__internal__validate-backup--description')}
|
<div className="Preferences__two-thirds-flow">
|
||||||
right={
|
{i18n('icu:Preferences__internal__validate-backup--description')}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
onClick={validateBackup}
|
onClick={validateBackup}
|
||||||
@@ -138,8 +148,8 @@ export function PreferencesInternal({
|
|||||||
i18n('icu:Preferences__internal__validate-backup')
|
i18n('icu:Preferences__internal__validate-backup')
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingSettingsControl>
|
||||||
|
|
||||||
{renderValidationResult(validationResult)}
|
{renderValidationResult(validationResult)}
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
@@ -148,11 +158,19 @@ export function PreferencesInternal({
|
|||||||
className="Preferences--internal--backups"
|
className="Preferences--internal--backups"
|
||||||
title={i18n('icu:Preferences__internal__local-backups')}
|
title={i18n('icu:Preferences__internal__local-backups')}
|
||||||
>
|
>
|
||||||
<SettingsControl
|
<FlowingSettingsControl>
|
||||||
left={i18n(
|
<div className="Preferences__two-thirds-flow">
|
||||||
'icu:Preferences__internal__export-local-backup--description'
|
{i18n(
|
||||||
)}
|
'icu:Preferences__internal__export-local-backup--description'
|
||||||
right={
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
onClick={exportLocalBackup}
|
onClick={exportLocalBackup}
|
||||||
@@ -164,8 +182,8 @@ export function PreferencesInternal({
|
|||||||
i18n('icu:Preferences__internal__export-local-backup')
|
i18n('icu:Preferences__internal__export-local-backup')
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingSettingsControl>
|
||||||
|
|
||||||
{renderValidationResult(exportResult)}
|
{renderValidationResult(exportResult)}
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
@@ -174,9 +192,17 @@ export function PreferencesInternal({
|
|||||||
className="Preferences--internal--message-schemas"
|
className="Preferences--internal--message-schemas"
|
||||||
title="Message schema versions"
|
title="Message schema versions"
|
||||||
>
|
>
|
||||||
<SettingsControl
|
<FlowingSettingsControl>
|
||||||
left="Check message schema versions"
|
<div className="Preferences__two-thirds-flow">
|
||||||
right={
|
Check message schema versions
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -189,8 +215,8 @@ export function PreferencesInternal({
|
|||||||
>
|
>
|
||||||
Fetch data
|
Fetch data
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingSettingsControl>
|
||||||
|
|
||||||
{messageCountBySchemaVersion ? (
|
{messageCountBySchemaVersion ? (
|
||||||
<div className="Preferences--internal--result">
|
<div className="Preferences--internal--result">
|
||||||
|
|||||||
@@ -10,8 +10,13 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { noop } from 'lodash';
|
import { noop } from 'lodash';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import type { LocalizerType } from '../types/I18N';
|
import type { LocalizerType } from '../types/I18N';
|
||||||
import { SettingsControl as Control, SettingsRow } from './PreferencesUtil';
|
import {
|
||||||
|
FlowingSettingsControl as FlowingControl,
|
||||||
|
SettingsRow,
|
||||||
|
} from './PreferencesUtil';
|
||||||
import { Button, ButtonSize, ButtonVariant } from './Button';
|
import { Button, ButtonSize, ButtonVariant } from './Button';
|
||||||
import { SIGNAL_BACKUPS_LEARN_MORE_URL } from './PreferencesBackups';
|
import { SIGNAL_BACKUPS_LEARN_MORE_URL } from './PreferencesBackups';
|
||||||
import { I18n } from './I18n';
|
import { I18n } from './I18n';
|
||||||
@@ -87,42 +92,54 @@ export function PreferencesLocalBackups({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SettingsRow className="Preferences--BackupsRow">
|
<SettingsRow className="Preferences--BackupsRow">
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div className="Preferences__two-thirds-flow">
|
||||||
<label>
|
<label>
|
||||||
{i18n('icu:Preferences__local-backups-folder')}
|
{i18n('icu:Preferences__local-backups-folder')}
|
||||||
<div className="Preferences__description">
|
<div className="Preferences__description">
|
||||||
{localBackupFolder}
|
{localBackupFolder}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
</div>
|
||||||
right={
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={pickLocalBackupFolder}
|
onClick={pickLocalBackupFolder}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__local-backups-folder__change')}
|
{i18n('icu:Preferences__local-backups-folder__change')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
<Control
|
<FlowingControl>
|
||||||
left={
|
<div className="Preferences__two-thirds-flow">
|
||||||
<label>
|
<label>
|
||||||
{i18n('icu:Preferences__backup-key')}
|
{i18n('icu:Preferences__backup-key')}
|
||||||
<div className="Preferences__description">
|
<div className="Preferences__description">
|
||||||
{i18n('icu:Preferences__backup-key-description')}
|
{i18n('icu:Preferences__backup-key-description')}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
</div>
|
||||||
right={
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'Preferences__flow-button',
|
||||||
|
'Preferences__one-third-flow',
|
||||||
|
'Preferences__one-third-flow--align-right'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.LocalBackupsKeyReference)}
|
onClick={() => setPage(Page.LocalBackupsKeyReference)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__view-key')}
|
{i18n('icu:Preferences__view-key')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
</div>
|
||||||
/>
|
</FlowingControl>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow className="Preferences--BackupsRow">
|
<SettingsRow className="Preferences--BackupsRow">
|
||||||
<div className="Preferences__padding">
|
<div className="Preferences__padding">
|
||||||
|
|||||||
@@ -27,19 +27,42 @@ export function SettingsRow({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FlowingSettingsControl({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
}): JSX.Element {
|
||||||
|
return <div className="Preferences__flow-control">{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LightIconLabel({
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
icon: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<label className="Preferences__light-icon-label">
|
||||||
|
<div className={classNames('Preferences__control--icon', icon)} />
|
||||||
|
<div>{children}</div>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function SettingsControl({
|
export function SettingsControl({
|
||||||
icon,
|
icon,
|
||||||
left,
|
left,
|
||||||
onClick,
|
onClick,
|
||||||
right,
|
right,
|
||||||
rightStyle,
|
description,
|
||||||
}: {
|
}: {
|
||||||
/** A className or `true` to leave room for icon */
|
/** A className or `true` to leave room for icon */
|
||||||
icon?: string | true;
|
icon?: string | true;
|
||||||
left: ReactNode;
|
left: ReactNode;
|
||||||
onClick?: () => unknown;
|
onClick?: () => unknown;
|
||||||
right: ReactNode;
|
right: ReactNode;
|
||||||
rightStyle?: React.CSSProperties;
|
description?: boolean;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
@@ -52,9 +75,10 @@ export function SettingsControl({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="Preferences__control--key">{left}</div>
|
<div className="Preferences__control--key">{left}</div>
|
||||||
<div className="Preferences__control--value" style={rightStyle}>
|
<div className="Preferences__control--value">{right}</div>
|
||||||
{right}
|
{description ? (
|
||||||
</div>
|
<div className="Preferences__control--value">{description}</div>
|
||||||
|
) : undefined}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user