Clean up inviteLink handling, harden compose input

This commit is contained in:
Scott Nonnenberg 2021-05-06 12:06:20 -07:00 committed by GitHub
parent 986d8a66bc
commit fc12d02a8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 59 additions and 3 deletions

View file

@ -593,6 +593,12 @@ ipc.on('show-window', () => {
showWindow(); showWindow();
}); });
ipc.on('set-secure-input', (_sender, enabled) => {
if (app.setSecureKeyboardEntryEnabled) {
app.setSecureKeyboardEntryEnabled(enabled);
}
});
ipc.on('title-bar-double-click', () => { ipc.on('title-bar-double-click', () => {
if (!mainWindow) { if (!mainWindow) {
return; return;

View file

@ -138,6 +138,9 @@ try {
window.log.info('show window'); window.log.info('show window');
ipc.send('show-window'); ipc.send('show-window');
}; };
window.setSecureInput = enabled => {
ipc.send('set-secure-input', enabled);
};
window.titleBarDoubleClick = () => { window.titleBarDoubleClick = () => {
ipc.send('title-bar-double-click'); ipc.send('title-bar-double-click');

View file

@ -40,6 +40,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
clearQuotedMessage: action('clearQuotedMessage'), clearQuotedMessage: action('clearQuotedMessage'),
getQuotedMessage: action('getQuotedMessage'), getQuotedMessage: action('getQuotedMessage'),
sortedGroupMembers: [], sortedGroupMembers: [],
setSecureInput: action('setSecureInput'),
// EmojiButton // EmojiButton
onPickEmoji: action('onPickEmoji'), onPickEmoji: action('onPickEmoji'),
onSetSkinTone: action('onSetSkinTone'), onSetSkinTone: action('onSetSkinTone'),

View file

@ -67,6 +67,7 @@ export type Props = Pick<
| 'draftBodyRanges' | 'draftBodyRanges'
| 'clearQuotedMessage' | 'clearQuotedMessage'
| 'getQuotedMessage' | 'getQuotedMessage'
| 'setSecureInput'
> & > &
Pick< Pick<
EmojiButtonProps, EmojiButtonProps,
@ -113,6 +114,7 @@ export const CompositionArea = ({
clearQuotedMessage, clearQuotedMessage,
getQuotedMessage, getQuotedMessage,
sortedGroupMembers, sortedGroupMembers,
setSecureInput,
// EmojiButton // EmojiButton
onPickEmoji, onPickEmoji,
onSetSkinTone, onSetSkinTone,
@ -470,6 +472,7 @@ export const CompositionArea = ({
clearQuotedMessage={clearQuotedMessage} clearQuotedMessage={clearQuotedMessage}
getQuotedMessage={getQuotedMessage} getQuotedMessage={getQuotedMessage}
sortedGroupMembers={sortedGroupMembers} sortedGroupMembers={sortedGroupMembers}
setSecureInput={setSecureInput}
/> />
</div> </div>
{!large ? ( {!large ? (

View file

@ -28,6 +28,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
getQuotedMessage: action('getQuotedMessage'), getQuotedMessage: action('getQuotedMessage'),
onPickEmoji: action('onPickEmoji'), onPickEmoji: action('onPickEmoji'),
large: boolean('large', overrideProps.large || false), large: boolean('large', overrideProps.large || false),
setSecureInput: action('setSecureInput'),
sortedGroupMembers: overrideProps.sortedGroupMembers || [], sortedGroupMembers: overrideProps.sortedGroupMembers || [],
skinTone: select( skinTone: select(
'skinTone', 'skinTone',

View file

@ -76,6 +76,7 @@ export type Props = {
onSubmit(message: string, mentions: Array<BodyRangeType>): unknown; onSubmit(message: string, mentions: Array<BodyRangeType>): unknown;
getQuotedMessage(): unknown; getQuotedMessage(): unknown;
clearQuotedMessage(): unknown; clearQuotedMessage(): unknown;
setSecureInput(enabled: boolean): unknown;
}; };
const MAX_LENGTH = 64 * 1024; const MAX_LENGTH = 64 * 1024;
@ -103,6 +104,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
skinTone, skinTone,
draftText, draftText,
draftBodyRanges, draftBodyRanges,
setSecureInput,
getQuotedMessage, getQuotedMessage,
clearQuotedMessage, clearQuotedMessage,
sortedGroupMembers, sortedGroupMembers,
@ -259,6 +261,20 @@ export const CompositionInput: React.ComponentType<Props> = props => {
return false; return false;
}; };
const onFocus = (): void => {
setSecureInput(true);
};
const onBlur = (): void => {
setSecureInput(false);
};
React.useEffect(() => {
return () => {
setSecureInput(false);
};
}, [setSecureInput]);
const onEnter = (): boolean => { const onEnter = (): boolean => {
const quill = quillRef.current; const quill = quillRef.current;
const emojiCompletion = emojiCompletionRef.current; const emojiCompletion = emojiCompletionRef.current;
@ -494,6 +510,8 @@ export const CompositionInput: React.ComponentType<Props> = props => {
onChange, onChange,
onEnter, onEnter,
onEscape, onEscape,
onFocus,
onBlur,
onPickEmoji, onPickEmoji,
onShortKeyEnter, onShortKeyEnter,
onTab, onTab,
@ -508,6 +526,8 @@ export const CompositionInput: React.ComponentType<Props> = props => {
return ( return (
<ReactQuill <ReactQuill
className="module-composition-input__quill" className="module-composition-input__quill"
onFocus={() => callbacksRef.current.onFocus()}
onBlur={() => callbacksRef.current.onBlur()}
onChange={() => callbacksRef.current.onChange()} onChange={() => callbacksRef.current.onChange()}
defaultValue={delta} defaultValue={delta}
modules={{ modules={{

View file

@ -53,6 +53,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
onSetSkinTone: action('onSetSkinTone'), onSetSkinTone: action('onSetSkinTone'),
recentEmojis: [], recentEmojis: [],
removeLinkPreview: action('removeLinkPreview'), removeLinkPreview: action('removeLinkPreview'),
setSecureInput: action('setSecureInput'),
skinTone: 0, skinTone: 0,
}); });

View file

@ -49,6 +49,7 @@ export type DataPropsType = {
caretLocation?: number caretLocation?: number
) => unknown; ) => unknown;
onTextTooLong: () => void; onTextTooLong: () => void;
setSecureInput: (enabled: boolean) => void;
} & Pick<EmojiButtonProps, 'recentEmojis' | 'skinTone'>; } & Pick<EmojiButtonProps, 'recentEmojis' | 'skinTone'>;
type ActionPropsType = Pick< type ActionPropsType = Pick<
@ -78,6 +79,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
recentEmojis, recentEmojis,
removeLinkPreview, removeLinkPreview,
skinTone, skinTone,
setSecureInput,
}) => { }) => {
const inputRef = useRef<null | HTMLInputElement>(null); const inputRef = useRef<null | HTMLInputElement>(null);
const inputApiRef = React.useRef<InputApi | undefined>(); const inputApiRef = React.useRef<InputApi | undefined>();
@ -306,6 +308,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
onPickEmoji={onPickEmoji} onPickEmoji={onPickEmoji}
onSubmit={forwardMessage} onSubmit={forwardMessage}
onTextTooLong={onTextTooLong} onTextTooLong={onTextTooLong}
setSecureInput={setSecureInput}
/> />
<div className="module-ForwardMessageModal__emoji"> <div className="module-ForwardMessageModal__emoji">
<EmojiButton <EmojiButton

View file

@ -34,6 +34,7 @@ export type SmartForwardMessageModalProps = {
caretLocation?: number caretLocation?: number
) => unknown; ) => unknown;
onTextTooLong: () => void; onTextTooLong: () => void;
setSecureInput: (enabled: boolean) => void;
}; };
const mapStateToProps = ( const mapStateToProps = (
@ -48,6 +49,7 @@ const mapStateToProps = (
onClose, onClose,
onEditorStateChange, onEditorStateChange,
onTextTooLong, onTextTooLong,
setSecureInput,
} = props; } = props;
const candidateConversations = getAllComposableConversations(state); const candidateConversations = getAllComposableConversations(state);
@ -68,6 +70,7 @@ const mapStateToProps = (
recentEmojis, recentEmojis,
skinTone, skinTone,
onTextTooLong, onTextTooLong,
setSecureInput,
}; };
}; };

View file

@ -2119,6 +2119,7 @@ export function initialize({
auth.groupPublicParamsHex, auth.groupPublicParamsHex,
auth.authCredentialPresentationHex auth.authCredentialPresentationHex
); );
const safeInviteLinkPassword = toWebSafeBase64(inviteLinkPassword);
const response: ArrayBuffer = await _ajax({ const response: ArrayBuffer = await _ajax({
basicAuth, basicAuth,
@ -2127,7 +2128,8 @@ export function initialize({
host: storageUrl, host: storageUrl,
httpType: 'GET', httpType: 'GET',
responseType: 'arraybuffer', responseType: 'arraybuffer',
urlParameters: `/${toWebSafeBase64(inviteLinkPassword)}`, urlParameters: `/${safeInviteLinkPassword}`,
redactUrl: _createRedactor(safeInviteLinkPassword),
}); });
return window.textsecure.protobuf.GroupJoinInfo.decode(response); return window.textsecure.protobuf.GroupJoinInfo.decode(response);
@ -2143,6 +2145,9 @@ export function initialize({
options.authCredentialPresentationHex options.authCredentialPresentationHex
); );
const data = changes.toArrayBuffer(); const data = changes.toArrayBuffer();
const safeInviteLinkPassword = inviteLinkBase64
? toWebSafeBase64(inviteLinkBase64)
: undefined;
const response: ArrayBuffer = await _ajax({ const response: ArrayBuffer = await _ajax({
basicAuth, basicAuth,
@ -2152,8 +2157,11 @@ export function initialize({
host: storageUrl, host: storageUrl,
httpType: 'PATCH', httpType: 'PATCH',
responseType: 'arraybuffer', responseType: 'arraybuffer',
urlParameters: inviteLinkBase64 urlParameters: safeInviteLinkPassword
? `?inviteLinkPassword=${toWebSafeBase64(inviteLinkBase64)}` ? `?inviteLinkPassword=${safeInviteLinkPassword}`
: undefined,
redactUrl: safeInviteLinkPassword
? _createRedactor(safeInviteLinkPassword)
: undefined, : undefined,
}); });

View file

@ -631,6 +631,9 @@ Whisper.ConversationView = Whisper.View.extend({
bodyRanges: Array<typeof window.Whisper.BodyRangeType>, bodyRanges: Array<typeof window.Whisper.BodyRangeType>,
caretLocation?: number caretLocation?: number
) => this.onEditorStateChange(msg, bodyRanges, caretLocation), ) => this.onEditorStateChange(msg, bodyRanges, caretLocation),
setSecureInput: (enabled: boolean) => {
window.setSecureInput(enabled);
},
onTextTooLong: () => this.showToast(Whisper.MessageBodyTooLongToast), onTextTooLong: () => this.showToast(Whisper.MessageBodyTooLongToast),
onChooseAttachment: this.onChooseAttachment.bind(this), onChooseAttachment: this.onChooseAttachment.bind(this),
getQuotedMessage: () => this.model.get('quotedMessageId'), getQuotedMessage: () => this.model.get('quotedMessageId'),
@ -2255,6 +2258,9 @@ Whisper.ConversationView = Whisper.View.extend({
{}, {},
document.querySelector('.module-ForwardMessageModal') document.querySelector('.module-ForwardMessageModal')
), ),
setSecureInput: (enabled: boolean) => {
window.setSecureInput(enabled);
},
} }
), ),
}); });

1
ts/window.d.ts vendored
View file

@ -224,6 +224,7 @@ declare global {
setAutoHideMenuBar: (value: WhatIsThis) => void; setAutoHideMenuBar: (value: WhatIsThis) => void;
setBadgeCount: (count: number) => void; setBadgeCount: (count: number) => void;
setMenuBarVisibility: (value: WhatIsThis) => void; setMenuBarVisibility: (value: WhatIsThis) => void;
setSecureInput: (enabled: boolean) => void;
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void; showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
showKeyboardShortcuts: () => void; showKeyboardShortcuts: () => void;
storage: { storage: {