From 5d110964b958ea9c2a33f0f7a1c3920936676c27 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:32:19 -0800 Subject: [PATCH] PNP Settings --- _locales/en/messages.json | 62 +++++- about.html | 1 - app/main.ts | 2 +- debug_log.html | 1 - package.json | 2 +- settings.html | 1 - sticker-creator/index.html | 1 - stylesheets/components/CircleCheckbox.scss | 113 ++++++++-- stylesheets/components/Preferences.scss | 53 ++++- ts/OS.ts | 10 + ts/components/CircleCheckbox.stories.tsx | 52 ++++- ts/components/CircleCheckbox.tsx | 24 +- ts/components/Preferences.stories.tsx | 24 +- ts/components/Preferences.tsx | 208 +++++++++++++----- .../ConversationNotificationsModal.tsx | 38 ++-- ts/main/settingsChannel.ts | 5 +- ts/services/storageRecordOps.ts | 5 - ts/state/smart/App.tsx | 16 +- ts/textsecure/WebAPI.ts | 16 ++ ts/util/createIPCEvents.ts | 34 ++- ts/util/isPhoneNumberSharingEnabled.ts | 5 +- ts/util/phoneNumberSharingMode.ts | 2 - ts/windows/context.ts | 10 +- ts/windows/init.ts | 5 + ts/windows/main/start.ts | 3 + ts/windows/preload.ts | 8 +- ts/windows/settings/preload.ts | 10 +- 27 files changed, 562 insertions(+), 149 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 45f07bab4..ed0bc0003 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -5429,23 +5429,75 @@ }, "Preferences__who-can--title": { "message": "Who can...", - "description": "Title for the 'who can do X' setting" + "description": "(deleted 2022/02/14) Title for the 'who can do X' setting" }, "Preferences__privacy--description": { "message": "To change these settings, open the Signal app on your mobile device and navigate to Settings > Privacy", - "description": "Description for the 'who can do X' setting" + "description": "(deleted 2022/02/14) Description for the 'who can do X' setting" }, "Preferences__who-can--everybody": { "message": "Everybody", - "description": "Option for who can see my X select" + "description": "(deleted 2022/02/14) Option for who can see my X select" }, "Preferences__who-can--contacts": { "message": "My Contacts", - "description": "Option for who can see my X select" + "description": "(deleted 2022/02/14) Option for who can see my X select" }, "Preferences__who-can--nobody": { "message": "Nobody", - "description": "Option for who can see my X select" + "description": "(deleted 2022/02/14) Option for who can see my X select" + }, + "icu:Preferences__pnp__row--title": { + "messageformat": "Phone Number", + "description": "Title of Phone Number row in Privacy section of Preferences window" + }, + "icu:Preferences__pnp__row--body": { + "messageformat": "Choose who can see your phone number and who can contact you on Signal with it.", + "description": "Body of Phone Number row in Privacy section of Preferences window" + }, + "icu:Preferences__pnp__sharing--title": { + "messageformat": "Who can see my number", + "description": "Title for the phone number sharing setting row" + }, + "icu:Preferences__pnp__sharing--description--everyone": { + "messageformat": "Your phone number will be visible to people and groups you message. People who have your number in their phone contacts will also see it on Signal.", + "description": "Description for the phone number sharing setting row when the value is Everyone" + }, + "icu:Preferences__pnp__sharing--description--nobody": { + "messageformat": "Nobody will see your phone number on Signal.", + "description": "Description for the phone number sharing setting row when the value is Nobody" + }, + "icu:Preferences__pnp--page-title": { + "messageformat": "Phone Number", + "description": "Title of the page in Phone Number Privacy settings" + }, + "icu:Preferences__pnp__sharing__everyone": { + "messageformat": "Everyone", + "description": "Option for sharing phone number with everyone" + }, + "icu:Preferences__pnp__sharing__nobody": { + "messageformat": "Nobody", + "description": "Option for sharing phone number with nobody" + }, + "icu:Preferences__pnp__discoverability--title": { + "messageformat": "Who can find me by number", + "description": "Title for the phone number discoverability setting row" + }, + "icu:Preferences__pnp__discoverability--description--everyone": { + "messageformat": "Anyone who has your phone number in their contacts will see you as a contact on Signal. Others will be able to reach you with your phone number when they start a new chat or group.", + "description": "Description for the phone number discoverability setting row wth the value is everyone" + }, + "icu:Preferences__pnp__discoverability--description--nobody": { + "messageformat": "Nobody on Signal will be able to reach you with your phone number.", + "description": "Description for the phone number discoverability setting row wth the value is nobody" + }, + "icu:Preferences__pnp__discoverability__everyone": { + "messageformat": "Everyone", + "description": "Option for letting everyone discover you by phone number" + }, + "icu:Preferences__pnp__discoverability__nobody": { + "messageformat": "Nobody", + "description": "Option for letting nobody discover you by phone number" }, "Preferences--messaging": { "message": "Messaging", diff --git a/about.html b/about.html index ad24f1228..81185ccef 100644 --- a/about.html +++ b/about.html @@ -29,7 +29,6 @@
- diff --git a/app/main.ts b/app/main.ts index 6ebc76ce4..6ba03a91b 100644 --- a/app/main.ts +++ b/app/main.ts @@ -1268,7 +1268,7 @@ async function showSettingsWindow() { frame: true, resizable: false, title: getResolvedMessagesLocale().i18n('signalDesktopPreferences'), - titleBarStyle: nonMainTitleBarStyle, + titleBarStyle: mainTitleBarStyle, titleBarOverlay, autoHideMenuBar: true, backgroundColor: await getBackgroundColor(), diff --git a/debug_log.html b/debug_log.html index 093cd03dd..8ec8f075c 100644 --- a/debug_log.html +++ b/debug_log.html @@ -33,7 +33,6 @@ type="application/javascript" src="ts/windows/applyTheme.js" > - diff --git a/package.json b/package.json index cfcbdf266..9a66b7d3a 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "danger:local": "./danger/danger.sh local --base main", "danger:ci": "./danger/danger.sh ci --base origin/main", "format": "pprettier --write '**/*.{ts,tsx,d.ts,js,json,html,scss,md,yml,yaml}' '!node_modules/**'", - "svgo": "svgo images/**/*.svg", + "svgo": "svgo --multipass images/**/*.svg", "transpile": "run-p check:types build:esbuild", "check:types": "tsc --noEmit", "clean-transpile-once": "rimraf app/**/*.js app/*.js sticker-creator/**/*.js sticker-creator/*.js ts/**/*.js ts/*.js tsconfig.tsbuildinfo", diff --git a/settings.html b/settings.html index a1a26d341..4cec0187b 100644 --- a/settings.html +++ b/settings.html @@ -33,7 +33,6 @@ type="application/javascript" src="ts/windows/applyTheme.js" > - diff --git a/sticker-creator/index.html b/sticker-creator/index.html index e1363b394..b5aeb8c25 100644 --- a/sticker-creator/index.html +++ b/sticker-creator/index.html @@ -14,6 +14,5 @@
- diff --git a/stylesheets/components/CircleCheckbox.scss b/stylesheets/components/CircleCheckbox.scss index cff1aacb3..b92774e82 100644 --- a/stylesheets/components/CircleCheckbox.scss +++ b/stylesheets/components/CircleCheckbox.scss @@ -7,7 +7,7 @@ height: 20px; width: 20px; - input[type='checkbox'] { + input { cursor: pointer; height: 0; position: absolute; @@ -39,25 +39,6 @@ } } - &:checked { - &::before { - background: $color-ultramarine; - border: 1.5px solid $color-ultramarine; - } - - &::after { - border: solid $color-white; - border-width: 0 2px 2px 0; - content: ''; - display: block; - height: 11px; - left: 7px; - position: absolute; - top: 3px; - transform: rotate(45deg); - width: 6px; - } - } &:disabled { cursor: inherit; } @@ -87,6 +68,98 @@ } } } + + &:checked { + &::after { + content: ''; + display: block; + position: absolute; + } + } + } + + input[type='checkbox'] { + &:checked { + &::before { + background: $color-ultramarine; + border: 1.5px solid $color-ultramarine; + } + + &::after { + border: solid $color-white; + border-width: 0 2px 2px 0; + height: 11px; + left: 7px; + top: 3px; + transform: rotate(45deg); + width: 6px; + } + } + } + + input[type='radio'] { + &:checked { + &::before { + border: 2px solid $color-ultramarine; + } + + &::after { + background: $color-ultramarine; + top: 4px; + left: 4px; + width: 12px; + height: 12px; + border-radius: 6px; + } + } + } + + &--small { + height: 18px; + width: 18px; + + input { + &::before { + height: 18px; + width: 18px; + } + } + + input[type='checkbox'] { + &:checked { + &::before { + background: $color-ultramarine; + border: 1.5px solid $color-ultramarine; + } + + &::after { + border: solid $color-white; + border-width: 0 2px 2px 0; + height: 10px; + left: 7px; + top: 3px; + transform: rotate(45deg); + width: 5px; + } + } + } + + input[type='radio'] { + &:checked { + &::before { + border: 2px solid $color-ultramarine; + } + + &::after { + background: $color-ultramarine; + top: 4px; + left: 4px; + width: 10px; + height: 10px; + border-radius: 5px; + } + } + } } } } diff --git a/stylesheets/components/Preferences.scss b/stylesheets/components/Preferences.scss index 2b20f44c1..e8c38949c 100644 --- a/stylesheets/components/Preferences.scss +++ b/stylesheets/components/Preferences.scss @@ -15,6 +15,7 @@ .Preferences { display: flex; overflow: hidden; + user-select: none; @include light-theme { background: $color-white; } @@ -23,7 +24,7 @@ } &__page-selector { - padding-top: 76px; + padding-top: calc(24px + var(--title-bar-drag-area-height)); min-width: 240px; @include light-theme { background: $color-gray-02; @@ -133,10 +134,19 @@ @include font-body-1-bold; align-items: center; display: flex; - height: 76px; - padding: 42px 0 14px 0; + height: 48px; + margin-top: var(--title-bar-drag-area-height); + margin-bottom: 24px; text-align: center; + border-bottom: 1px solid $color-gray-15; + @include light-theme { + border-color: $color-gray-15; + } + @include dark-theme { + border-color: $color-gray-65; + } + &--header { flex-grow: 1; text-align: center; @@ -144,7 +154,7 @@ } &__settings-row { - padding-bottom: 12px; + padding-bottom: 20px; h3 { @include font-body-1-bold; @@ -164,6 +174,30 @@ margin-bottom: 24px; } + &__link { + @include button-reset; + padding: 0px 0 28px 0; + width: 100%; + + h3 { + @include font-body-1; + font-weight: 400; + margin: 0; + margin-bottom: 8px; + } + } + + &__link:not(:last-child) { + border-bottom: 1px solid $color-gray-15; + @include light-theme { + border-color: $color-gray-15; + } + @include dark-theme { + border-color: $color-gray-65; + } + margin-bottom: 24px; + } + &__control { align-items: center; display: flex; @@ -255,4 +289,15 @@ &__stories-off { min-width: 140px; } + + &__settings-radio__label { + display: flex; + flex-direction: row; + gap: 16px; + height: 40px; + align-items: center; + &:last-child { + margin-bottom: 8px; + } + } } diff --git a/ts/OS.ts b/ts/OS.ts index 1964ef36d..617f2979d 100644 --- a/ts/OS.ts +++ b/ts/OS.ts @@ -36,3 +36,13 @@ export const getName = (): string => { } return 'Linux'; }; + +export const getClassName = (): string => { + if (isMacOS()) { + return 'os-macos'; + } + if (isWindows()) { + return 'os-windows'; + } + return 'os-linux'; +}; diff --git a/ts/components/CircleCheckbox.stories.tsx b/ts/components/CircleCheckbox.stories.tsx index 12f776058..0a7260251 100644 --- a/ts/components/CircleCheckbox.stories.tsx +++ b/ts/components/CircleCheckbox.stories.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; import type { Props } from './CircleCheckbox'; -import { CircleCheckbox } from './CircleCheckbox'; +import { CircleCheckbox, Variant } from './CircleCheckbox'; const createProps = (): Props => ({ checked: false, @@ -28,3 +28,53 @@ export function Checked(): JSX.Element { export function Disabled(): JSX.Element { return ; } + +export function SmallNormal(): JSX.Element { + return ; +} + +export function SmallChecked(): JSX.Element { + return ; +} + +export function SmallDisabled(): JSX.Element { + return ; +} + +export function RadioNormal(): JSX.Element { + return ; +} + +export function RadioChecked(): JSX.Element { + return ; +} + +export function RadioDisabled(): JSX.Element { + return ; +} + +export function SmallRadioNormal(): JSX.Element { + return ; +} + +export function SmallRadioChecked(): JSX.Element { + return ( + + ); +} + +export function SmallRadioDisabled(): JSX.Element { + return ( + + ); +} diff --git a/ts/components/CircleCheckbox.tsx b/ts/components/CircleCheckbox.tsx index 2bbd4c348..213fbc351 100644 --- a/ts/components/CircleCheckbox.tsx +++ b/ts/components/CircleCheckbox.tsx @@ -2,15 +2,24 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; +import classNames from 'classnames'; import { getClassNamesFor } from '../util/getClassNamesFor'; +import { missingCaseError } from '../util/missingCaseError'; + +export enum Variant { + Normal = 'Normal', + Small = 'Small', +} export type Props = { id?: string; + variant?: Variant; checked?: boolean; disabled?: boolean; isRadio?: boolean; name?: string; + moduleClassName?: string; onChange?: (value: boolean) => unknown; onClick?: () => unknown; }; @@ -24,17 +33,28 @@ export type Props = { */ export function CircleCheckbox({ id, + variant = Variant.Normal, checked, disabled, isRadio, + moduleClassName, name, onChange, onClick, }: Props): JSX.Element { - const getClassName = getClassNamesFor('CircleCheckbox'); + const getClassName = getClassNamesFor('CircleCheckbox', moduleClassName); + + let variantModifier: string; + if (variant === Variant.Normal) { + variantModifier = getClassName('__checkbox--normal'); + } else if (variant === Variant.Small) { + variantModifier = getClassName('__checkbox--small'); + } else { + throw missingCaseError(variant); + } return ( -
+
({ isAutoLaunchSupported: true, isHideMenuBarSupported: true, isNotificationAttentionSupported: true, - isPhoneNumberSharingSupported: false, + isPhoneNumberSharingSupported: true, isSyncSupported: true, isSystemTraySupported: true, isMinimizeToAndStartInSystemTraySupported: true, @@ -163,6 +163,8 @@ export default { onSpellCheckChange: { action: true }, onThemeChange: { action: true }, onUniversalExpireTimerChange: { action: true }, + onWhoCanSeeMeChange: { action: true }, + onWhoCanFindMeChange: { action: true }, onZoomFactorChange: { action: true }, removeCustomColor: { action: true }, removeCustomColorOnConversations: { action: true }, @@ -195,3 +197,23 @@ CustomUniversalExpireTimer.args = { CustomUniversalExpireTimer.story = { name: 'Custom universalExpireTimer', }; + +export const PNPSharingDisabled = Template.bind({}); +PNPSharingDisabled.args = { + whoCanSeeMe: PhoneNumberSharingMode.Nobody, + whoCanFindMe: PhoneNumberDiscoverability.Discoverable, + isPhoneNumberSharingSupported: true, +}; +PNPSharingDisabled.story = { + name: 'PNP Sharing Disabled', +}; + +export const PNPDiscoverabilityDisabled = Template.bind({}); +PNPDiscoverabilityDisabled.args = { + whoCanSeeMe: PhoneNumberSharingMode.Nobody, + whoCanFindMe: PhoneNumberDiscoverability.NotDiscoverable, + isPhoneNumberSharingSupported: true, +}; +PNPDiscoverabilityDisabled.story = { + name: 'PNP Discoverability Disabled', +}; diff --git a/ts/components/Preferences.tsx b/ts/components/Preferences.tsx index 36dc7b02d..acfc94b5e 100644 --- a/ts/components/Preferences.tsx +++ b/ts/components/Preferences.tsx @@ -2,10 +2,11 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { ReactNode } from 'react'; -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import { noop } from 'lodash'; import classNames from 'classnames'; import type { AudioDevice } from '@signalapp/ringrtc'; +import uuid from 'uuid'; import type { MediaDeviceSettings } from '../types/Calling'; import type { @@ -17,6 +18,10 @@ import type { ThemeSettingType } from '../types/StorageUIKeys'; import { Button, ButtonVariant } from './Button'; import { ChatColorPicker } from './ChatColorPicker'; import { Checkbox } from './Checkbox'; +import { + CircleCheckbox, + Variant as CircleCheckboxVariant, +} from './CircleCheckbox'; import { ConfirmationDialog } from './ConfirmationDialog'; import type { ConversationType } from '../state/ducks/conversations'; import type { @@ -159,6 +164,8 @@ type PropsFunctionType = { onSpellCheckChange: CheckboxChangeHandlerType; onThemeChange: SelectChangeHandlerType; onUniversalExpireTimerChange: SelectChangeHandlerType; + onWhoCanSeeMeChange: SelectChangeHandlerType; + onWhoCanFindMeChange: SelectChangeHandlerType; onZoomFactorChange: SelectChangeHandlerType; // Localization @@ -178,6 +185,7 @@ enum Page { // Sub pages ChatColor = 'ChatColor', + PNP = 'PNP', } const DEFAULT_ZOOM_FACTORS = [ @@ -278,6 +286,8 @@ export function Preferences({ onSpellCheckChange, onThemeChange, onUniversalExpireTimerChange, + onWhoCanSeeMeChange, + onWhoCanFindMeChange, onZoomFactorChange, removeCustomColor, removeCustomColorOnConversations, @@ -845,6 +855,20 @@ export function Preferences({ {i18n('Preferences__button--privacy')}
+ {isPhoneNumberSharingSupported ? ( + + ) : null} - {isPhoneNumberSharingSupported ? ( - - - } - /> - - } - /> -
-
- {i18n('Preferences__privacy--description')} -
-
-
- ) : null} ); + } else if (page === Page.PNP) { + settings = ( + <> +
+
+ + + +
+
+ {whoCanSeeMe === PhoneNumberSharingMode.Everybody + ? i18n('icu:Preferences__pnp__sharing--description--everyone') + : i18n('icu:Preferences__pnp__sharing--description--nobody')} +
+
+
+ + + +
+
+ {whoCanFindMe === PhoneNumberDiscoverability.Discoverable + ? i18n( + 'icu:Preferences__pnp__discoverability--description--everyone' + ) + : i18n( + 'icu:Preferences__pnp__discoverability--description--nobody' + )} +
+
+
+ + ); } return ( @@ -1128,6 +1173,7 @@ export function Preferences({ theme={theme} executeMenuRole={executeMenuRole} > +