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 {
|
||||
background: variables.$color-ultramarine;
|
||||
border-radius: 100%;
|
||||
border: 1px solid variables.$color-white;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
top: 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 {
|
||||
|
||||
@@ -286,6 +286,7 @@ $secondary-text-color: light-dark(
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
overflow-y: scroll;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
&__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 {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -432,9 +500,16 @@ $secondary-text-color: light-dark(
|
||||
}
|
||||
}
|
||||
|
||||
&__checkbox__description,
|
||||
&__description {
|
||||
@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 {
|
||||
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 {
|
||||
SettingsControl as Control,
|
||||
FlowingSettingsControl as FlowingControl,
|
||||
SettingsRadio,
|
||||
SettingsRow,
|
||||
} from './PreferencesUtil';
|
||||
@@ -672,27 +673,43 @@ export function Preferences({
|
||||
const pageContents = (
|
||||
<>
|
||||
<SettingsRow>
|
||||
<Control
|
||||
left={i18n('icu:Preferences--phone-number')}
|
||||
right={phoneNumber}
|
||||
rightStyle={{
|
||||
maxWidth: '33%',
|
||||
}}
|
||||
/>
|
||||
<Control
|
||||
left={
|
||||
<>
|
||||
<div>{i18n('icu:Preferences--device-name')}</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:Preferences--device-name__description')}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
right={deviceName}
|
||||
rightStyle={{
|
||||
maxWidth: '33%',
|
||||
}}
|
||||
/>
|
||||
<FlowingControl>
|
||||
<div className="Preferences__half-flow">
|
||||
{i18n('icu:Preferences--phone-number')}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-value',
|
||||
'Preferences__half-flow',
|
||||
'Preferences__half-flow--align-right'
|
||||
)}
|
||||
>
|
||||
{phoneNumber}
|
||||
</div>
|
||||
</FlowingControl>
|
||||
<FlowingControl>
|
||||
<div className="Preferences__half-flow">
|
||||
{i18n('icu:Preferences--device-name')}
|
||||
</div>
|
||||
<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 title={i18n('icu:Preferences--system')}>
|
||||
{isAutoLaunchSupported && (
|
||||
@@ -1374,24 +1391,34 @@ export function Preferences({
|
||||
const pageContents = (
|
||||
<>
|
||||
<SettingsRow>
|
||||
<Control
|
||||
left={
|
||||
<div className="Preferences__pnp">
|
||||
<h3>{i18n('icu:Preferences__pnp__row--title')}</h3>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:Preferences__pnp__row--body')}
|
||||
</div>
|
||||
<FlowingControl>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__pnp',
|
||||
'Preferences__two-thirds-flow'
|
||||
)}
|
||||
>
|
||||
<h3>{i18n('icu:Preferences__pnp__row--title')}</h3>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:Preferences__pnp__row--body')}
|
||||
</div>
|
||||
}
|
||||
right={
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__pnp',
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setPage(Page.PNP)}
|
||||
variant={ButtonVariant.Secondary}
|
||||
>
|
||||
{i18n('icu:Preferences__pnp__row--button')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
<SettingsRow>
|
||||
<Control
|
||||
@@ -1433,18 +1460,22 @@ export function Preferences({
|
||||
/>
|
||||
)}
|
||||
<SettingsRow title={i18n('icu:disappearingMessages')}>
|
||||
<Control
|
||||
left={
|
||||
<>
|
||||
<div>
|
||||
{i18n('icu:settings__DisappearingMessages__timer__label')}
|
||||
</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:settings__DisappearingMessages__footer')}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
right={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<div>
|
||||
{i18n('icu:settings__DisappearingMessages__timer__label')}
|
||||
</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:settings__DisappearingMessages__footer')}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Select
|
||||
ariaLabel={i18n(
|
||||
'icu:settings__DisappearingMessages__timer__label'
|
||||
@@ -1480,8 +1511,8 @@ export function Preferences({
|
||||
])}
|
||||
value={universalExpireTimer}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
{isContentProtectionSupported && (
|
||||
<SettingsRow title={i18n('icu:Preferences__Privacy__Application')}>
|
||||
@@ -1520,17 +1551,23 @@ export function Preferences({
|
||||
</ConfirmationDialog>
|
||||
) : null}
|
||||
<SettingsRow title={i18n('icu:Stories__title')}>
|
||||
<Control
|
||||
left={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<label htmlFor={storiesId}>
|
||||
<div>{i18n('icu:Stories__settings-toggle--title')}</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:Stories__settings-toggle--description')}
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
right={
|
||||
hasStoriesDisabled ? (
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
{hasStoriesDisabled ? (
|
||||
<Button
|
||||
onClick={() => onHasStoriesDisabledChanged(false)}
|
||||
variant={ButtonVariant.Secondary}
|
||||
@@ -1545,31 +1582,40 @@ export function Preferences({
|
||||
>
|
||||
{i18n('icu:Preferences__turn-stories-off')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
<SettingsRow>
|
||||
<Control
|
||||
left={
|
||||
<>
|
||||
<div>{i18n('icu:clearDataHeader')}</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:clearDataExplanation')}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
right={
|
||||
<div className="Preferences__right-button">
|
||||
<Button
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
variant={ButtonVariant.SecondaryDestructive}
|
||||
>
|
||||
{i18n('icu:clearDataButton')}
|
||||
</Button>
|
||||
<FlowingControl>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__pnp',
|
||||
'Preferences__two-thirds-flow'
|
||||
)}
|
||||
>
|
||||
<div>{i18n('icu:clearDataHeader')}</div>
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:clearDataExplanation')}
|
||||
</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>
|
||||
{confirmDelete ? (
|
||||
<ConfirmationDialog
|
||||
@@ -1681,23 +1727,28 @@ export function Preferences({
|
||||
</div>
|
||||
</SettingsRow>
|
||||
<SettingsRow>
|
||||
<Control
|
||||
left={
|
||||
<>
|
||||
<div className="Preferences__option-name">
|
||||
{i18n('icu:Preferences__sent-media-quality')}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__description',
|
||||
'Preferences__description--medium'
|
||||
)}
|
||||
>
|
||||
{i18n('icu:Preferences__sent-media-quality__description')}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
right={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<div className="Preferences__option-name">
|
||||
{i18n('icu:Preferences__sent-media-quality')}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__description',
|
||||
'Preferences__description--medium'
|
||||
)}
|
||||
>
|
||||
{i18n('icu:Preferences__sent-media-quality__description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Select
|
||||
onChange={onSentMediaQualityChange}
|
||||
options={[
|
||||
@@ -1712,8 +1763,8 @@ export function Preferences({
|
||||
]}
|
||||
value={sentMediaQualitySetting}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,13 +2,20 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type {
|
||||
BackupsSubscriptionType,
|
||||
BackupStatusType,
|
||||
} from '../types/backups';
|
||||
import type { LocalizerType } from '../types/I18N';
|
||||
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 { Button, ButtonVariant } from './Button';
|
||||
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
||||
@@ -97,29 +104,36 @@ export function PreferencesBackups({
|
||||
|
||||
{backupSubscriptionStatus ? (
|
||||
<SettingsRow className="Preferences--BackupsRow">
|
||||
<Control
|
||||
icon="Preferences__BackupsIcon"
|
||||
left={
|
||||
<label>
|
||||
{i18n('icu:Preferences--signal-backups')}{' '}
|
||||
<div className="Preferences__description">
|
||||
{renderBackupsSubscriptionSummary({
|
||||
subscriptionStatus: backupSubscriptionStatus,
|
||||
i18n,
|
||||
locale,
|
||||
})}
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
right={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<LightIconLabel icon="Preferences__BackupsIcon">
|
||||
<label>
|
||||
{i18n('icu:Preferences--signal-backups')}{' '}
|
||||
<div className="Preferences__description">
|
||||
{renderBackupsSubscriptionSummary({
|
||||
subscriptionStatus: backupSubscriptionStatus,
|
||||
i18n,
|
||||
locale,
|
||||
})}
|
||||
</div>
|
||||
</label>
|
||||
</LightIconLabel>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setPage(Page.BackupsDetails)}
|
||||
variant={ButtonVariant.Secondary}
|
||||
>
|
||||
{i18n('icu:Preferences__button--manage')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
) : (
|
||||
<SettingsRow className="Preferences--BackupsRow">
|
||||
@@ -148,19 +162,26 @@ export function PreferencesBackups({
|
||||
className="Preferences--BackupsRow"
|
||||
title={i18n('icu:Preferences__backup-other-ways')}
|
||||
>
|
||||
<Control
|
||||
icon="Preferences__LocalBackupsIcon"
|
||||
left={
|
||||
<label>
|
||||
{i18n('icu:Preferences__local-backups')}{' '}
|
||||
<div className="Preferences__description">
|
||||
{isLocalBackupsSetup
|
||||
? null
|
||||
: i18n('icu:Preferences--local-backups-off-description')}
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
right={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<LightIconLabel icon="Preferences__LocalBackupsIcon">
|
||||
<label>
|
||||
{i18n('icu:Preferences__local-backups')}{' '}
|
||||
<div className="Preferences__description">
|
||||
{isLocalBackupsSetup
|
||||
? null
|
||||
: i18n('icu:Preferences--local-backups-off-description')}
|
||||
</div>
|
||||
</label>
|
||||
</LightIconLabel>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setPage(Page.LocalBackups)}
|
||||
variant={ButtonVariant.Secondary}
|
||||
@@ -169,8 +190,8 @@ export function PreferencesBackups({
|
||||
? i18n('icu:Preferences__button--manage')
|
||||
: i18n('icu:Preferences__button--set-up')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { LocalizerType } from '../types/I18N';
|
||||
import { toLogFormat } from '../types/errors';
|
||||
import { formatFileSize } from '../util/formatFileSize';
|
||||
import { SECOND } from '../util/durations';
|
||||
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
||||
import { SettingsRow, SettingsControl } from './PreferencesUtil';
|
||||
import { SettingsRow, FlowingSettingsControl } from './PreferencesUtil';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { Spinner } from './Spinner';
|
||||
import type { MessageCountBySchemaVersionType } from '../sql/Interface';
|
||||
@@ -124,9 +126,17 @@ export function PreferencesInternal({
|
||||
className="Preferences--internal--backups"
|
||||
title={i18n('icu:Preferences__button--backups')}
|
||||
>
|
||||
<SettingsControl
|
||||
left={i18n('icu:Preferences__internal__validate-backup--description')}
|
||||
right={
|
||||
<FlowingSettingsControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
{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
|
||||
variant={ButtonVariant.Secondary}
|
||||
onClick={validateBackup}
|
||||
@@ -138,8 +148,8 @@ export function PreferencesInternal({
|
||||
i18n('icu:Preferences__internal__validate-backup')
|
||||
)}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingSettingsControl>
|
||||
|
||||
{renderValidationResult(validationResult)}
|
||||
</SettingsRow>
|
||||
@@ -148,11 +158,19 @@ export function PreferencesInternal({
|
||||
className="Preferences--internal--backups"
|
||||
title={i18n('icu:Preferences__internal__local-backups')}
|
||||
>
|
||||
<SettingsControl
|
||||
left={i18n(
|
||||
'icu:Preferences__internal__export-local-backup--description'
|
||||
)}
|
||||
right={
|
||||
<FlowingSettingsControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
{i18n(
|
||||
'icu:Preferences__internal__export-local-backup--description'
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
variant={ButtonVariant.Secondary}
|
||||
onClick={exportLocalBackup}
|
||||
@@ -164,8 +182,8 @@ export function PreferencesInternal({
|
||||
i18n('icu:Preferences__internal__export-local-backup')
|
||||
)}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingSettingsControl>
|
||||
|
||||
{renderValidationResult(exportResult)}
|
||||
</SettingsRow>
|
||||
@@ -174,9 +192,17 @@ export function PreferencesInternal({
|
||||
className="Preferences--internal--message-schemas"
|
||||
title="Message schema versions"
|
||||
>
|
||||
<SettingsControl
|
||||
left="Check message schema versions"
|
||||
right={
|
||||
<FlowingSettingsControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
Check message schema versions
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
variant={ButtonVariant.Secondary}
|
||||
onClick={async () => {
|
||||
@@ -189,8 +215,8 @@ export function PreferencesInternal({
|
||||
>
|
||||
Fetch data
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingSettingsControl>
|
||||
|
||||
{messageCountBySchemaVersion ? (
|
||||
<div className="Preferences--internal--result">
|
||||
|
||||
@@ -10,8 +10,13 @@ import React, {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
|
||||
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 { SIGNAL_BACKUPS_LEARN_MORE_URL } from './PreferencesBackups';
|
||||
import { I18n } from './I18n';
|
||||
@@ -87,42 +92,54 @@ export function PreferencesLocalBackups({
|
||||
</div>
|
||||
</div>
|
||||
<SettingsRow className="Preferences--BackupsRow">
|
||||
<Control
|
||||
left={
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<label>
|
||||
{i18n('icu:Preferences__local-backups-folder')}
|
||||
<div className="Preferences__description">
|
||||
{localBackupFolder}
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
right={
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={pickLocalBackupFolder}
|
||||
variant={ButtonVariant.Secondary}
|
||||
>
|
||||
{i18n('icu:Preferences__local-backups-folder__change')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Control
|
||||
left={
|
||||
</div>
|
||||
</FlowingControl>
|
||||
<FlowingControl>
|
||||
<div className="Preferences__two-thirds-flow">
|
||||
<label>
|
||||
{i18n('icu:Preferences__backup-key')}
|
||||
<div className="Preferences__description">
|
||||
{i18n('icu:Preferences__backup-key-description')}
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
right={
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'Preferences__flow-button',
|
||||
'Preferences__one-third-flow',
|
||||
'Preferences__one-third-flow--align-right'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setPage(Page.LocalBackupsKeyReference)}
|
||||
variant={ButtonVariant.Secondary}
|
||||
>
|
||||
{i18n('icu:Preferences__view-key')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FlowingControl>
|
||||
</SettingsRow>
|
||||
<SettingsRow className="Preferences--BackupsRow">
|
||||
<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({
|
||||
icon,
|
||||
left,
|
||||
onClick,
|
||||
right,
|
||||
rightStyle,
|
||||
description,
|
||||
}: {
|
||||
/** A className or `true` to leave room for icon */
|
||||
icon?: string | true;
|
||||
left: ReactNode;
|
||||
onClick?: () => unknown;
|
||||
right: ReactNode;
|
||||
rightStyle?: React.CSSProperties;
|
||||
description?: boolean;
|
||||
}): JSX.Element {
|
||||
const content = (
|
||||
<>
|
||||
@@ -52,9 +75,10 @@ export function SettingsControl({
|
||||
/>
|
||||
)}
|
||||
<div className="Preferences__control--key">{left}</div>
|
||||
<div className="Preferences__control--value" style={rightStyle}>
|
||||
{right}
|
||||
</div>
|
||||
<div className="Preferences__control--value">{right}</div>
|
||||
{description ? (
|
||||
<div className="Preferences__control--value">{description}</div>
|
||||
) : undefined}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user