Several text formatting fixes

This commit is contained in:
Scott Nonnenberg 2023-04-17 18:16:41 -07:00 committed by GitHub
parent f93b4235d5
commit 5395741f11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 72 additions and 50 deletions

View file

@ -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', 'Source Han Sans CN', 'Hiragino Sans GB', 'Hiragino Kaku Gothic',
'Microsoft Yahei UI', Helvetica, Arial, sans-serif; 'Microsoft Yahei UI', Helvetica, Arial, sans-serif;
$monospace: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo,
Consolas, monospace;
// -- V3 Colors // -- V3 Colors
$color-accent-blue: #2c6bed; $color-accent-blue: #2c6bed;

View file

@ -395,7 +395,7 @@ button.CompositionInput__link-preview__close-button {
.quill { .quill {
&--monospace { &--monospace {
font-family: monospace; font-family: $monospace;
} }
&--spoiler { &--spoiler {
@include light-theme { @include light-theme {

View file

@ -30,7 +30,7 @@
} }
&__text { &__text {
font-family: Monaco, Consolas, 'Courier New', Courier, monospace; font-family: $monospace;
font-size: 12px; font-size: 12px;
margin: 0; margin: 0;
user-select: none; user-select: none;

View file

@ -8,7 +8,7 @@
// strikethrough is handled by <s> element // strikethrough is handled by <s> element
&--monospace { &--monospace {
font-family: monospace; font-family: $monospace;
} }
// Note: only used in the left pane for search results, not in message bubbles // Note: only used in the left pane for search results, not in message bubbles
@ -47,7 +47,7 @@
} }
&--spoiler--noninteractive { &--spoiler--noninteractive {
cursor: default; cursor: inherit;
box-shadow: none; box-shadow: none;
} }

View file

@ -55,7 +55,7 @@
&__number { &__number {
border-radius: 8px; border-radius: 8px;
font-family: monospace; font-family: $monospace;
height: 100px; height: 100px;
margin: 24px auto; margin: 24px auto;
padding: 24px; padding: 24px;

View file

@ -48,7 +48,28 @@ export class FormattingMenu {
this.quill.on('editor-change', this.onEditorChange.bind(this)); 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.quill.keyboard.addBinding({ key: 'E', shortKey: true }, () =>
this.toggleForStyle(QuillFormattingStyle.monospace) 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 // 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. // visible editor. Important for the 'Cmd-A' scenario when scrolled down.
const updatedY = Math.max( const updatedY = Math.max(
editorElement?.getClientRects()[0]?.y || 0, (editorElement?.getClientRects()[0]?.y || 0) - 10,
rect.y (rect?.y || 0) - 10
); );
return DOMRect.fromRect({ return DOMRect.fromRect({

View file

@ -6,11 +6,7 @@ import { createSelector } from 'reselect';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import type { ComposerStateType, QuotedMessageType } from '../ducks/composer'; import type { ComposerStateType, QuotedMessageType } from '../ducks/composer';
import { getComposerStateForConversation } from '../ducks/composer'; import { getComposerStateForConversation } from '../ducks/composer';
import { import { getRemoteConfig, isRemoteConfigFlagEnabled } from './items';
getRemoteConfig,
getTextFormattingEnabled,
isRemoteConfigFlagEnabled,
} from './items';
export const getComposerState = (state: StateType): ComposerStateType => export const getComposerState = (state: StateType): ComposerStateType =>
state.composer; state.composer;
@ -28,27 +24,19 @@ export const getQuotedMessageSelector = createSelector(
composerStateForConversationIdSelector(conversationId).quotedMessage composerStateForConversationIdSelector(conversationId).quotedMessage
); );
export const getIsFormattingEnabled = createSelector( export const getIsFormattingFlagEnabled = createSelector(
getTextFormattingEnabled,
getRemoteConfig, getRemoteConfig,
(isOptionEnabled, remoteConfig) => { remoteConfig => {
return ( return isRemoteConfigFlagEnabled(remoteConfig, 'desktop.textFormatting');
isOptionEnabled &&
isRemoteConfigFlagEnabled(remoteConfig, 'desktop.textFormatting')
);
} }
); );
export const getIsFormattingSpoilersEnabled = createSelector( export const getIsFormattingSpoilersFlagEnabled = createSelector(
getTextFormattingEnabled,
getRemoteConfig, getRemoteConfig,
(isOptionEnabled, remoteConfig) => { remoteConfig => {
return ( return isRemoteConfigFlagEnabled(
isOptionEnabled && remoteConfig,
isRemoteConfigFlagEnabled( 'desktop.textFormatting.spoilerSend'
remoteConfig,
'desktop.textFormatting.spoilerSend'
)
); );
} }
); );

View file

@ -15,7 +15,7 @@ import { imageToBlurHash } from '../../util/imageToBlurHash';
import { getPreferredBadgeSelector } from '../selectors/badges'; import { getPreferredBadgeSelector } from '../selectors/badges';
import { selectRecentEmojis } from '../selectors/emojis'; import { selectRecentEmojis } from '../selectors/emojis';
import { getIntl, getTheme, getUserConversationId } from '../selectors/user'; import { getIntl, getTheme, getUserConversationId } from '../selectors/user';
import { getEmojiSkinTone } from '../selectors/items'; import { getEmojiSkinTone, getTextFormattingEnabled } from '../selectors/items';
import { import {
getConversationSelector, getConversationSelector,
getGroupAdminsSelector, getGroupAdminsSelector,
@ -34,8 +34,8 @@ import {
import { isSignalConversation } from '../../util/isSignalConversation'; import { isSignalConversation } from '../../util/isSignalConversation';
import { import {
getComposerStateForConversationIdSelector, getComposerStateForConversationIdSelector,
getIsFormattingEnabled, getIsFormattingFlagEnabled,
getIsFormattingSpoilersEnabled, getIsFormattingSpoilersFlagEnabled,
} from '../selectors/composer'; } from '../selectors/composer';
import type { SmartCompositionRecordingProps } from './CompositionRecording'; import type { SmartCompositionRecordingProps } from './CompositionRecording';
import { SmartCompositionRecording } from './CompositionRecording'; import { SmartCompositionRecording } from './CompositionRecording';
@ -98,8 +98,11 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
const selectedMessageIds = getSelectedMessageIds(state); const selectedMessageIds = getSelectedMessageIds(state);
const isFormattingEnabled = getIsFormattingEnabled(state); const isFormattingEnabled =
const isFormattingSpoilersEnabled = getIsFormattingSpoilersEnabled(state); getIsFormattingFlagEnabled(state) && getTextFormattingEnabled(state);
const isFormattingSpoilersEnabled =
getIsFormattingSpoilersFlagEnabled(state) &&
getTextFormattingEnabled(state);
return { return {
// Base // Base

View file

@ -13,9 +13,10 @@ import { useActions as useItemsActions } from '../ducks/items';
import { getPreferredBadgeSelector } from '../selectors/badges'; import { getPreferredBadgeSelector } from '../selectors/badges';
import { useComposerActions } from '../ducks/composer'; import { useComposerActions } from '../ducks/composer';
import { import {
getIsFormattingEnabled, getIsFormattingFlagEnabled,
getIsFormattingSpoilersEnabled, getIsFormattingSpoilersFlagEnabled,
} from '../selectors/composer'; } from '../selectors/composer';
import { getTextFormattingEnabled } from '../selectors/items';
export type SmartCompositionTextAreaProps = Pick< export type SmartCompositionTextAreaProps = Pick<
CompositionTextAreaProps, CompositionTextAreaProps,
@ -41,10 +42,12 @@ export function SmartCompositionTextArea(
const { onTextTooLong } = useComposerActions(); const { onTextTooLong } = useComposerActions();
const getPreferredBadge = useSelector(getPreferredBadgeSelector); const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const isFormattingEnabled = useSelector(getIsFormattingEnabled); const isFormattingOptionEnabled = useSelector(getTextFormattingEnabled);
const isFormattingSpoilersEnabled = useSelector( const isFormattingEnabled =
getIsFormattingSpoilersEnabled useSelector(getIsFormattingFlagEnabled) && isFormattingOptionEnabled;
); const isFormattingSpoilersEnabled =
useSelector(getIsFormattingSpoilersFlagEnabled) &&
isFormattingOptionEnabled;
return ( return (
<CompositionTextArea <CompositionTextArea

View file

@ -15,8 +15,8 @@ import {
getReceivedStickerPacks, getReceivedStickerPacks,
} from '../selectors/stickers'; } from '../selectors/stickers';
import { import {
getIsFormattingEnabled, getIsFormattingFlagEnabled,
getIsFormattingSpoilersEnabled, getIsFormattingSpoilersFlagEnabled,
} from '../selectors/composer'; } from '../selectors/composer';
const mapStateToProps = (state: StateType) => { const mapStateToProps = (state: StateType) => {
@ -25,8 +25,9 @@ const mapStateToProps = (state: StateType) => {
const knownPacks = getKnownStickerPacks(state); const knownPacks = getKnownStickerPacks(state);
const receivedPacks = getReceivedStickerPacks(state); const receivedPacks = getReceivedStickerPacks(state);
const isFormattingFlagEnabled = getIsFormattingEnabled(state); const isFormattingFlagEnabled = getIsFormattingFlagEnabled(state);
const isFormattingSpoilersFlagEnabled = getIsFormattingSpoilersEnabled(state); const isFormattingSpoilersFlagEnabled =
getIsFormattingSpoilersFlagEnabled(state);
const hasInstalledStickers = const hasInstalledStickers =
countStickers({ countStickers({

View file

@ -16,6 +16,7 @@ import {
getEmojiSkinTone, getEmojiSkinTone,
getHasStoryViewReceiptSetting, getHasStoryViewReceiptSetting,
getPreferredReactionEmoji, getPreferredReactionEmoji,
getTextFormattingEnabled,
isInternalUser, isInternalUser,
} from '../selectors/items'; } from '../selectors/items';
import { getIntl, getPlatform } from '../selectors/user'; import { getIntl, getPlatform } from '../selectors/user';
@ -40,8 +41,8 @@ import { useGlobalModalActions } from '../ducks/globalModals';
import { useStoriesActions } from '../ducks/stories'; import { useStoriesActions } from '../ducks/stories';
import { useIsWindowActive } from '../../hooks/useIsWindowActive'; import { useIsWindowActive } from '../../hooks/useIsWindowActive';
import { import {
getIsFormattingEnabled, getIsFormattingFlagEnabled,
getIsFormattingSpoilersEnabled, getIsFormattingSpoilersFlagEnabled,
} from '../selectors/composer'; } from '../selectors/composer';
export function SmartStoryViewer(): JSX.Element | null { export function SmartStoryViewer(): JSX.Element | null {
@ -93,10 +94,12 @@ export function SmartStoryViewer(): JSX.Element | null {
getHasStoryViewReceiptSetting getHasStoryViewReceiptSetting
); );
const isFormattingEnabled = useSelector(getIsFormattingEnabled); const isFormattingOptionEnabled = useSelector(getTextFormattingEnabled);
const isFormattingSpoilersEnabled = useSelector( const isFormattingEnabled =
getIsFormattingSpoilersEnabled useSelector(getIsFormattingFlagEnabled) && isFormattingOptionEnabled;
); const isFormattingSpoilersEnabled =
useSelector(getIsFormattingSpoilersFlagEnabled) &&
isFormattingOptionEnabled;
const { pauseVoiceNotePlayer } = useAudioPlayerActions(); const { pauseVoiceNotePlayer } = useAudioPlayerActions();