From 5395741f11b31040fae0c3c830cbe701dd6b8ea5 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Mon, 17 Apr 2023 18:16:41 -0700 Subject: [PATCH] Several text formatting fixes --- stylesheets/_variables.scss | 3 ++ stylesheets/components/CompositionInput.scss | 2 +- stylesheets/components/DebugLogWindow.scss | 2 +- .../components/MessageTextRenderer.scss | 4 +-- .../components/SafetyNumberViewer.scss | 2 +- ts/quill/formatting/menu.tsx | 27 +++++++++++++++-- ts/state/selectors/composer.ts | 30 ++++++------------- ts/state/smart/CompositionArea.tsx | 13 ++++---- ts/state/smart/CompositionTextArea.tsx | 15 ++++++---- ts/state/smart/ShortcutGuideModal.tsx | 9 +++--- ts/state/smart/StoryViewer.tsx | 15 ++++++---- 11 files changed, 72 insertions(+), 50 deletions(-) diff --git a/stylesheets/_variables.scss b/stylesheets/_variables.scss index 2944f390f5c..fa582498ab0 100644 --- a/stylesheets/_variables.scss +++ b/stylesheets/_variables.scss @@ -5,6 +5,9 @@ $inter: Inter, 'Helvetica Neue', 'Source Sans Pro', 'Source Han Sans SC', 'Source Han Sans CN', 'Hiragino Sans GB', 'Hiragino Kaku Gothic', 'Microsoft Yahei UI', Helvetica, Arial, sans-serif; +$monospace: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo, + Consolas, monospace; + // -- V3 Colors $color-accent-blue: #2c6bed; diff --git a/stylesheets/components/CompositionInput.scss b/stylesheets/components/CompositionInput.scss index 3c6bf8d3dc7..3c270944404 100644 --- a/stylesheets/components/CompositionInput.scss +++ b/stylesheets/components/CompositionInput.scss @@ -395,7 +395,7 @@ button.CompositionInput__link-preview__close-button { .quill { &--monospace { - font-family: monospace; + font-family: $monospace; } &--spoiler { @include light-theme { diff --git a/stylesheets/components/DebugLogWindow.scss b/stylesheets/components/DebugLogWindow.scss index e919ae94bdc..c01d53792d6 100644 --- a/stylesheets/components/DebugLogWindow.scss +++ b/stylesheets/components/DebugLogWindow.scss @@ -30,7 +30,7 @@ } &__text { - font-family: Monaco, Consolas, 'Courier New', Courier, monospace; + font-family: $monospace; font-size: 12px; margin: 0; user-select: none; diff --git a/stylesheets/components/MessageTextRenderer.scss b/stylesheets/components/MessageTextRenderer.scss index b59525b3a66..822abc49f40 100644 --- a/stylesheets/components/MessageTextRenderer.scss +++ b/stylesheets/components/MessageTextRenderer.scss @@ -8,7 +8,7 @@ // strikethrough is handled by element &--monospace { - font-family: monospace; + font-family: $monospace; } // Note: only used in the left pane for search results, not in message bubbles @@ -47,7 +47,7 @@ } &--spoiler--noninteractive { - cursor: default; + cursor: inherit; box-shadow: none; } diff --git a/stylesheets/components/SafetyNumberViewer.scss b/stylesheets/components/SafetyNumberViewer.scss index 77a04339de3..5e9dc54dde0 100644 --- a/stylesheets/components/SafetyNumberViewer.scss +++ b/stylesheets/components/SafetyNumberViewer.scss @@ -55,7 +55,7 @@ &__number { border-radius: 8px; - font-family: monospace; + font-family: $monospace; height: 100px; margin: 24px auto; padding: 24px; diff --git a/ts/quill/formatting/menu.tsx b/ts/quill/formatting/menu.tsx index 16054b1fc57..82174344f56 100644 --- a/ts/quill/formatting/menu.tsx +++ b/ts/quill/formatting/menu.tsx @@ -48,7 +48,28 @@ export class FormattingMenu { this.quill.on('editor-change', this.onEditorChange.bind(this)); - // Note: Bold and Italic are built-in + // We override these keybindings, which means that we need to move their priority + // above the built-in shortcuts, which don't exactly do what we want. + + const boldChar = 'B'; + const boldCharCode = boldChar.charCodeAt(0); + this.quill.keyboard.addBinding({ key: boldChar, shortKey: true }, () => + this.toggleForStyle(QuillFormattingStyle.bold) + ); + quill.keyboard.bindings[boldCharCode].unshift( + quill.keyboard.bindings[boldCharCode].pop() + ); + + const italicChar = 'I'; + const italicCharCode = italicChar.charCodeAt(0); + this.quill.keyboard.addBinding({ key: italicChar, shortKey: true }, () => + this.toggleForStyle(QuillFormattingStyle.italic) + ); + quill.keyboard.bindings[italicCharCode].unshift( + quill.keyboard.bindings[italicCharCode].pop() + ); + + // No need for changing priority for these new keybindings this.quill.keyboard.addBinding({ key: 'E', shortKey: true }, () => this.toggleForStyle(QuillFormattingStyle.monospace) @@ -134,8 +155,8 @@ export class FormattingMenu { // where the editor ends, we fix the popover so it stays connected to the // visible editor. Important for the 'Cmd-A' scenario when scrolled down. const updatedY = Math.max( - editorElement?.getClientRects()[0]?.y || 0, - rect.y + (editorElement?.getClientRects()[0]?.y || 0) - 10, + (rect?.y || 0) - 10 ); return DOMRect.fromRect({ diff --git a/ts/state/selectors/composer.ts b/ts/state/selectors/composer.ts index 09539041d65..d4a30d84e82 100644 --- a/ts/state/selectors/composer.ts +++ b/ts/state/selectors/composer.ts @@ -6,11 +6,7 @@ import { createSelector } from 'reselect'; import type { StateType } from '../reducer'; import type { ComposerStateType, QuotedMessageType } from '../ducks/composer'; import { getComposerStateForConversation } from '../ducks/composer'; -import { - getRemoteConfig, - getTextFormattingEnabled, - isRemoteConfigFlagEnabled, -} from './items'; +import { getRemoteConfig, isRemoteConfigFlagEnabled } from './items'; export const getComposerState = (state: StateType): ComposerStateType => state.composer; @@ -28,27 +24,19 @@ export const getQuotedMessageSelector = createSelector( composerStateForConversationIdSelector(conversationId).quotedMessage ); -export const getIsFormattingEnabled = createSelector( - getTextFormattingEnabled, +export const getIsFormattingFlagEnabled = createSelector( getRemoteConfig, - (isOptionEnabled, remoteConfig) => { - return ( - isOptionEnabled && - isRemoteConfigFlagEnabled(remoteConfig, 'desktop.textFormatting') - ); + remoteConfig => { + return isRemoteConfigFlagEnabled(remoteConfig, 'desktop.textFormatting'); } ); -export const getIsFormattingSpoilersEnabled = createSelector( - getTextFormattingEnabled, +export const getIsFormattingSpoilersFlagEnabled = createSelector( getRemoteConfig, - (isOptionEnabled, remoteConfig) => { - return ( - isOptionEnabled && - isRemoteConfigFlagEnabled( - remoteConfig, - 'desktop.textFormatting.spoilerSend' - ) + remoteConfig => { + return isRemoteConfigFlagEnabled( + remoteConfig, + 'desktop.textFormatting.spoilerSend' ); } ); diff --git a/ts/state/smart/CompositionArea.tsx b/ts/state/smart/CompositionArea.tsx index 889e861243b..93b7f23ec22 100644 --- a/ts/state/smart/CompositionArea.tsx +++ b/ts/state/smart/CompositionArea.tsx @@ -15,7 +15,7 @@ import { imageToBlurHash } from '../../util/imageToBlurHash'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { selectRecentEmojis } from '../selectors/emojis'; import { getIntl, getTheme, getUserConversationId } from '../selectors/user'; -import { getEmojiSkinTone } from '../selectors/items'; +import { getEmojiSkinTone, getTextFormattingEnabled } from '../selectors/items'; import { getConversationSelector, getGroupAdminsSelector, @@ -34,8 +34,8 @@ import { import { isSignalConversation } from '../../util/isSignalConversation'; import { getComposerStateForConversationIdSelector, - getIsFormattingEnabled, - getIsFormattingSpoilersEnabled, + getIsFormattingFlagEnabled, + getIsFormattingSpoilersFlagEnabled, } from '../selectors/composer'; import type { SmartCompositionRecordingProps } from './CompositionRecording'; import { SmartCompositionRecording } from './CompositionRecording'; @@ -98,8 +98,11 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { const selectedMessageIds = getSelectedMessageIds(state); - const isFormattingEnabled = getIsFormattingEnabled(state); - const isFormattingSpoilersEnabled = getIsFormattingSpoilersEnabled(state); + const isFormattingEnabled = + getIsFormattingFlagEnabled(state) && getTextFormattingEnabled(state); + const isFormattingSpoilersEnabled = + getIsFormattingSpoilersFlagEnabled(state) && + getTextFormattingEnabled(state); return { // Base diff --git a/ts/state/smart/CompositionTextArea.tsx b/ts/state/smart/CompositionTextArea.tsx index 9bac7063d44..10de8388c5a 100644 --- a/ts/state/smart/CompositionTextArea.tsx +++ b/ts/state/smart/CompositionTextArea.tsx @@ -13,9 +13,10 @@ import { useActions as useItemsActions } from '../ducks/items'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { useComposerActions } from '../ducks/composer'; import { - getIsFormattingEnabled, - getIsFormattingSpoilersEnabled, + getIsFormattingFlagEnabled, + getIsFormattingSpoilersFlagEnabled, } from '../selectors/composer'; +import { getTextFormattingEnabled } from '../selectors/items'; export type SmartCompositionTextAreaProps = Pick< CompositionTextAreaProps, @@ -41,10 +42,12 @@ export function SmartCompositionTextArea( const { onTextTooLong } = useComposerActions(); const getPreferredBadge = useSelector(getPreferredBadgeSelector); - const isFormattingEnabled = useSelector(getIsFormattingEnabled); - const isFormattingSpoilersEnabled = useSelector( - getIsFormattingSpoilersEnabled - ); + const isFormattingOptionEnabled = useSelector(getTextFormattingEnabled); + const isFormattingEnabled = + useSelector(getIsFormattingFlagEnabled) && isFormattingOptionEnabled; + const isFormattingSpoilersEnabled = + useSelector(getIsFormattingSpoilersFlagEnabled) && + isFormattingOptionEnabled; return ( { @@ -25,8 +25,9 @@ const mapStateToProps = (state: StateType) => { const knownPacks = getKnownStickerPacks(state); const receivedPacks = getReceivedStickerPacks(state); - const isFormattingFlagEnabled = getIsFormattingEnabled(state); - const isFormattingSpoilersFlagEnabled = getIsFormattingSpoilersEnabled(state); + const isFormattingFlagEnabled = getIsFormattingFlagEnabled(state); + const isFormattingSpoilersFlagEnabled = + getIsFormattingSpoilersFlagEnabled(state); const hasInstalledStickers = countStickers({ diff --git a/ts/state/smart/StoryViewer.tsx b/ts/state/smart/StoryViewer.tsx index 2aa56d607b5..b1ec89c10b2 100644 --- a/ts/state/smart/StoryViewer.tsx +++ b/ts/state/smart/StoryViewer.tsx @@ -16,6 +16,7 @@ import { getEmojiSkinTone, getHasStoryViewReceiptSetting, getPreferredReactionEmoji, + getTextFormattingEnabled, isInternalUser, } from '../selectors/items'; import { getIntl, getPlatform } from '../selectors/user'; @@ -40,8 +41,8 @@ import { useGlobalModalActions } from '../ducks/globalModals'; import { useStoriesActions } from '../ducks/stories'; import { useIsWindowActive } from '../../hooks/useIsWindowActive'; import { - getIsFormattingEnabled, - getIsFormattingSpoilersEnabled, + getIsFormattingFlagEnabled, + getIsFormattingSpoilersFlagEnabled, } from '../selectors/composer'; export function SmartStoryViewer(): JSX.Element | null { @@ -93,10 +94,12 @@ export function SmartStoryViewer(): JSX.Element | null { getHasStoryViewReceiptSetting ); - const isFormattingEnabled = useSelector(getIsFormattingEnabled); - const isFormattingSpoilersEnabled = useSelector( - getIsFormattingSpoilersEnabled - ); + const isFormattingOptionEnabled = useSelector(getTextFormattingEnabled); + const isFormattingEnabled = + useSelector(getIsFormattingFlagEnabled) && isFormattingOptionEnabled; + const isFormattingSpoilersEnabled = + useSelector(getIsFormattingSpoilersFlagEnabled) && + isFormattingOptionEnabled; const { pauseVoiceNotePlayer } = useAudioPlayerActions();