ConversationView: Improve types

This commit is contained in:
Scott Nonnenberg 2021-08-30 14:32:56 -07:00 committed by GitHub
parent c765d3202c
commit dcf29078f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1101 additions and 941 deletions

View File

@ -44,6 +44,7 @@ const rules = {
],
'no-continue': 'off',
'lines-between-class-members': 'off',
// Prettier overrides:
'arrow-parens': 'off',

View File

@ -57,19 +57,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="conversation-loading-screen">
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--128"></div>
<div class='container'>
<span class='dot'></span>
<span class='dot'></span>
<span class='dot'></span>
</div>
</div>
</script>
<script type="text/x-tmpl-mustache" id="two-column">
<div class='module-title-bar-drag-area'></div>
@ -94,14 +81,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="banner">
<div class='body'>
<span class='icon warning'></span>
{{ message }}
<span class='icon dismiss'></span>
</div>
</script>
<script type="text/x-tmpl-mustache" id="toast">
{{ toastMessage }}
</script>
@ -121,11 +100,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="message-list">
<div class='messages'></div>
<div class='typing-container'></div>
</script>
<script type="text/x-tmpl-mustache" id="recorder">
<button class='close' tabIndex='2'><span class='icon'></span></button>
<span class='time'>0:00</span>
@ -145,10 +119,6 @@
({{ limit }}{{ units }})
</script>
<script type="text/x-tmpl-mustache" id="attachment-type-modal">
Sorry, your attachment has a type, {{type}}, that is not currently supported.
</script>
<script type="text/x-tmpl-mustache" id="group-member-list">
<div class='container' tabindex='0'>
{{ #summary }} <div class='summary'>{{ summary }}</div>{{ /summary }}

View File

@ -181,7 +181,7 @@
"@storybook/addon-knobs": "5.1.11",
"@storybook/addons": "5.1.11",
"@storybook/react": "5.1.11",
"@types/backbone": "1.4.3",
"@types/backbone": "1.4.5",
"@types/better-sqlite3": "7.4.0",
"@types/blueimp-load-image": "5.14.1",
"@types/chai": "4.2.18",
@ -195,7 +195,7 @@
"@types/got": "9.4.1",
"@types/history": "4.7.2",
"@types/humanize-duration": "^3.18.1",
"@types/jquery": "3.5.0",
"@types/jquery": "3.5.6",
"@types/js-yaml": "3.12.0",
"@types/linkify-it": "2.1.0",
"@types/lodash": "4.14.106",
@ -204,6 +204,7 @@
"@types/memoizee": "0.4.2",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.0.0",
"@types/mustache": "4.1.2",
"@types/node": "14.14.37",
"@types/node-fetch": "2.5.7",
"@types/node-forge": "0.9.5",

View File

@ -110,16 +110,6 @@
overflow-y: auto;
}
.discussion-container {
@include light-theme {
background-color: $color-white;
}
@include dark-theme {
background-color: $color-gray-95;
}
}
.typing-bubble-wrapper {
margin-bottom: 20px;
}

View File

@ -26,19 +26,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="conversation-loading-screen">
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--128"></div>
<div class='container'>
<span class='dot'></span>
<span class='dot'></span>
<span class='dot'></span>
</div>
</div>
</script>
<script type="text/x-tmpl-mustache" id="two-column">
<div class='module-title-bar-drag-area'></div>
@ -63,27 +50,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="scroll-down-button-view">
<button class='text module-scroll-down__button {{ buttonClass }}' alt='{{ moreBelow }}'>
<div class='module-scroll-down__icon'></div>
</button>
</script>
<script type="text/x-tmpl-mustache" id="last-seen-indicator-view">
<div class='module-last-seen-indicator__bar'/>
<div class='module-last-seen-indicator__text'>
{{ unreadMessages }}
</div>
</script>
<script type="text/x-tmpl-mustache" id="banner">
<div class='body'>
<span class='icon warning'></span>
{{ message }}
<span class='icon dismiss'></span>
</div>
</script>
<script type="text/x-tmpl-mustache" id="toast">
{{ toastMessage }}
</script>
@ -103,11 +69,6 @@
</div>
</script>
<script type="text/x-tmpl-mustache" id="message-list">
<div class='messages'></div>
<div class='typing-container'></div>
</script>
<script type="text/x-tmpl-mustache" id="recorder">
<button class='finish'><span class='icon'></span></button>
<span class='time'>0:00</span>
@ -127,10 +88,6 @@
({{ limit }}{{ units }})
</script>
<script type="text/x-tmpl-mustache" id="attachment-type-modal">
Sorry, your attachment has a type, {{type}}, that is not currently supported.
</script>
<script type="text/x-tmpl-mustache" id="group-member-list">
<div class='container'>
{{ #summary }} <div class='summary'>{{ summary }}</div>{{ /summary }}

View File

@ -40,6 +40,16 @@ import { LinkPreviewWithDomain } from '../types/LinkPreview';
import { ConversationType } from '../state/ducks/conversations';
import { AnnouncementsOnlyGroupBanner } from './AnnouncementsOnlyGroupBanner';
export type CompositionAPIType = {
focusInput: () => void;
isDirty: () => boolean;
setDisabled: (disabled: boolean) => void;
setShowMic: (showMic: boolean) => void;
setMicActive: (micActive: boolean) => void;
reset: InputApi['reset'];
resetEmojiResults: InputApi['resetEmojiResults'];
};
export type OwnProps = {
readonly i18n: LocalizerType;
readonly areWePending?: boolean;
@ -55,15 +65,7 @@ export type OwnProps = {
readonly left?: boolean;
readonly messageRequestsEnabled?: boolean;
readonly acceptedMessageRequest?: boolean;
readonly compositionApi?: React.MutableRefObject<{
focusInput: () => void;
isDirty: () => boolean;
setDisabled: (disabled: boolean) => void;
setShowMic: (showMic: boolean) => void;
setMicActive: (micActive: boolean) => void;
reset: InputApi['reset'];
resetEmojiResults: InputApi['resetEmojiResults'];
}>;
readonly compositionApi?: React.MutableRefObject<CompositionAPIType>;
readonly micCellEl?: HTMLElement;
readonly draftAttachments: Array<AttachmentType>;
readonly shouldSendHighQualityAttachments: boolean;

View File

@ -66,10 +66,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
checkForAccount: action('checkForAccount'),
clearSelectedMessage: action('clearSelectedMessage'),
deleteMessage: action('deleteMessage'),
deleteMessageForEveryone: action('deleteMessageForEveryone'),
displayTapToViewMessage: action('displayTapToViewMessage'),
downloadAttachment: action('downloadAttachment'),
doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'),
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),

View File

@ -15,7 +15,6 @@ import {
} from './Message';
import { LocalizerType } from '../../types/Util';
import { ConversationType } from '../../state/ducks/conversations';
import { assert } from '../../util/assert';
import { groupBy } from '../../util/mapUtil';
import { ContactNameColorType } from '../../types/Colors';
import { SendStatus } from '../../messages/MessageSendState';
@ -42,7 +41,7 @@ export type Contact = Pick<
errors?: Array<Error>;
};
export type Props = {
export type PropsData = {
// An undefined status means they were the sender and it's an incoming message. If
// `undefined` is a status, there should be no other items in the array; if there are
// any defined statuses, `undefined` shouldn't be present.
@ -57,16 +56,11 @@ export type Props = {
sendAnyway: (contactId: string, messageId: string) => unknown;
showSafetyNumber: (contactId: string) => void;
i18n: LocalizerType;
} & Pick<
} & Pick<MessagePropsType, 'interactionMode'>;
export type PropsBackboneActions = Pick<
MessagePropsType,
| 'checkForAccount'
| 'clearSelectedMessage'
| 'deleteMessage'
| 'deleteMessageForEveryone'
| 'displayTapToViewMessage'
| 'downloadAttachment'
| 'doubleCheckMissingQuoteReference'
| 'interactionMode'
| 'kickOffAttachmentDownload'
| 'markAttachmentAsCorrupted'
| 'markViewed'
@ -85,6 +79,16 @@ export type Props = {
| 'showVisualAttachment'
>;
export type PropsReduxActions = Pick<
MessagePropsType,
| 'clearSelectedMessage'
| 'doubleCheckMissingQuoteReference'
| 'checkForAccount'
>;
export type ExternalProps = PropsData & PropsBackboneActions;
export type Props = PropsData & PropsBackboneActions & PropsReduxActions;
const contactSortCollator = new Intl.Collator();
const _keyForError = (error: Error): string => {
@ -263,10 +267,7 @@ export class MessageDetail extends React.Component<Props> {
checkForAccount,
clearSelectedMessage,
contactNameColor,
deleteMessage,
deleteMessageForEveryone,
displayTapToViewMessage,
downloadAttachment,
doubleCheckMissingQuoteReference,
i18n,
interactionMode,
@ -302,12 +303,18 @@ export class MessageDetail extends React.Component<Props> {
clearSelectedMessage={clearSelectedMessage}
contactNameColor={contactNameColor}
containerElementRef={this.messageContainerRef}
deleteMessage={deleteMessage}
deleteMessageForEveryone={deleteMessageForEveryone}
deleteMessage={() =>
window.log.warn('MessageDetail: deleteMessage called!')
}
deleteMessageForEveryone={() =>
window.log.warn('MessageDetail: deleteMessageForEveryone called!')
}
disableMenu
disableScroll
displayTapToViewMessage={displayTapToViewMessage}
downloadAttachment={downloadAttachment}
downloadAttachment={() =>
window.log.warn('MessageDetail: deleteMessageForEveryone called!')
}
doubleCheckMissingQuoteReference={doubleCheckMissingQuoteReference}
i18n={i18n}
interactionMode={interactionMode}
@ -324,10 +331,7 @@ export class MessageDetail extends React.Component<Props> {
retrySend={retrySend}
showForwardMessageModal={showForwardMessageModal}
scrollToQuotedMessage={() => {
assert(
false,
'scrollToQuotedMessage should never be called because scrolling is disabled'
);
window.log.warn('MessageDetail: scrollToQuotedMessage called!');
}}
showContactDetail={showContactDetail}
showContactModal={showContactModal}
@ -338,9 +342,8 @@ export class MessageDetail extends React.Component<Props> {
showExpiredOutgoingTapToViewToast
}
showMessageDetail={() => {
assert(
false,
"showMessageDetail should never be called because the menu is disabled (and we're already in the message detail!)"
window.log.warn(
'MessageDetail: deleteMessageForEveryone called!'
);
}}
showVisualAttachment={showVisualAttachment}

View File

@ -341,7 +341,9 @@ export async function joinViaLink(hash: string): Promise<void> {
window.log.info(`joinViaLink/${logId}: Showing modal`);
let groupV2InfoDialog = new window.Whisper.ReactWrapperView({
let groupV2InfoDialog:
| Backbone.View
| undefined = new window.Whisper.ReactWrapperView({
className: 'group-v2-join-dialog-wrapper',
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
join,

14
ts/model-types.d.ts vendored
View File

@ -22,7 +22,11 @@ import {
} from './messages/MessageSendState';
import { GroupNameCollisionsWithIdsByTitle } from './util/groupMemberNameCollisions';
import { ConversationColorType } from './types/Colors';
import { AttachmentType, ThumbnailType } from './types/Attachment';
import {
AttachmentType,
ThumbnailType,
OnDiskAttachmentDraftType,
} from './types/Attachment';
import { EmbeddedContactType } from './types/EmbeddedContact';
import { SignalService as Proto } from './protobuf';
import { AvatarDataType } from './types/Avatar';
@ -209,12 +213,8 @@ export type ConversationAttributesType = {
customColor?: CustomColorType;
customColorId?: string;
discoveredUnregisteredAt?: number;
draftAttachments?: Array<{
fileName?: string;
path?: string;
pending?: boolean;
screenshotPath?: string;
}>;
draftChanged?: boolean;
draftAttachments?: Array<OnDiskAttachmentDraftType>;
draftBodyRanges?: Array<BodyRangeType>;
draftTimestamp?: number | null;
inbox_position: number;

View File

@ -6,6 +6,7 @@
import { compact } from 'lodash';
import {
ConversationAttributesType,
ConversationModelCollectionType,
MessageAttributesType,
MessageModelCollectionType,
QuotedMessageType,
@ -163,12 +164,14 @@ export class ConversationModel extends window.Backbone
storeName?: string | null;
throttledBumpTyping: unknown;
throttledBumpTyping?: () => void;
throttledFetchSMSOnlyUUID?: () => Promise<void> | void;
throttledMaybeMigrateV1Group?: () => Promise<void> | void;
throttledGetProfiles?: () => Promise<void>;
typingRefreshTimer?: NodeJS.Timer | null;
typingPauseTimer?: NodeJS.Timer | null;
@ -2329,13 +2332,13 @@ export class ConversationModel extends window.Backbone
});
}
getUnverified(): Backbone.Collection<ConversationModel> {
getUnverified(): ConversationModelCollectionType {
if (isDirectConversation(this.attributes)) {
return this.isUnverified()
? new window.Backbone.Collection([this])
: new window.Backbone.Collection();
? new window.Whisper.ConversationCollection([this])
: new window.Whisper.ConversationCollection();
}
return new window.Backbone.Collection(
return new window.Whisper.ConversationCollection(
this.contactCollection?.filter(contact => {
if (isMe(contact.attributes)) {
return false;
@ -2382,15 +2385,15 @@ export class ConversationModel extends window.Backbone
});
}
getUntrusted(): Backbone.Collection<ConversationModel> {
getUntrusted(): ConversationModelCollectionType {
if (isDirectConversation(this.attributes)) {
if (this.isUntrusted()) {
return new window.Backbone.Collection([this]);
return new window.Whisper.ConversationCollection([this]);
}
return new window.Backbone.Collection();
return new window.Whisper.ConversationCollection();
}
return new window.Backbone.Collection(
return new window.Whisper.ConversationCollection(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.contactCollection!.filter(contact => {
if (isMe(contact.attributes)) {
@ -2470,7 +2473,7 @@ export class ConversationModel extends window.Backbone
readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -2510,7 +2513,7 @@ export class ConversationModel extends window.Backbone
readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -2546,7 +2549,7 @@ export class ConversationModel extends window.Backbone
schemaVersion: Message.VERSION_NEEDED_FOR_DISPLAY,
// TODO: DESKTOP-722
// this type does not fully implement the interface it is expected to
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -2604,7 +2607,7 @@ export class ConversationModel extends window.Backbone
local: options.local,
readStatus: ReadStatus.Unread,
// TODO: DESKTOP-722
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -2663,7 +2666,7 @@ export class ConversationModel extends window.Backbone
readStatus: unread ? ReadStatus.Unread : ReadStatus.Read,
callHistoryDetails: detailsToSave,
// TODO: DESKTOP-722
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -2714,7 +2717,7 @@ export class ConversationModel extends window.Backbone
changedId: conversationId || this.id,
profileChange,
// TODO: DESKTOP-722
} as unknown) as typeof window.Whisper.MessageAttributesType;
} as unknown) as MessageAttributesType;
const id = await window.Signal.Data.saveMessage(message);
const model = window.MessageController.register(
@ -3185,9 +3188,7 @@ export class ConversationModel extends window.Backbone
return [];
}
async makeQuote(
quotedMessage: typeof window.Whisper.MessageType
): Promise<QuotedMessageType> {
async makeQuote(quotedMessage: MessageModel): Promise<QuotedMessageType> {
const { getName } = EmbeddedContact;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const contact = quotedMessage.getContact()!;
@ -4452,10 +4453,10 @@ export class ConversationModel extends window.Backbone
}
}
getProfiles(): Promise<Array<void>> {
async getProfiles(): Promise<void> {
// request all conversation members' keys
const conversations = (this.getMembers() as unknown) as Array<ConversationModel>;
return Promise.all(
await Promise.all(
window._.map(conversations, conversation =>
getProfile(conversation.get('uuid'), conversation.get('e164'))
)

View File

@ -5624,6 +5624,10 @@ function getExternalDraftFilesForConversation(
const files: Array<string> = [];
forEach(draftAttachments, attachment => {
if (attachment.pending) {
return;
}
const { path: file, screenshotPath } = attachment;
if (file) {
files.push(file);

View File

@ -27,7 +27,7 @@ import {
type ExternalProps = {
id: string;
onClickQuotedMessage: (id?: string) => unknown;
onClickQuotedMessage: (id: string) => unknown;
};
const mapStateToProps = (state: StateType, props: ExternalProps) => {
@ -92,8 +92,12 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
ourConversationId: getUserConversationId(state),
})
: undefined,
onClickQuotedMessage: () =>
onClickQuotedMessage(quotedMessage?.quote?.messageId),
onClickQuotedMessage: () => {
const messageId = quotedMessage?.quote?.messageId;
if (messageId) {
onClickQuotedMessage(messageId);
}
},
// Emojis
recentEmojis,
skinTone: get(state, ['items', 'skinTone'], 0),

View File

@ -1,10 +1,12 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { ComponentProps } from 'react';
import { connect } from 'react-redux';
import { MessageDetail } from '../../components/conversation/MessageDetail';
import {
MessageDetail,
ExternalProps as MessageDetailProps,
} from '../../components/conversation/MessageDetail';
import { mapDispatchToProps } from '../actions';
import { StateType } from '../reducer';
@ -13,40 +15,10 @@ import { renderAudioAttachment } from './renderAudioAttachment';
import { renderEmojiPicker } from './renderEmojiPicker';
import { getContactNameColorSelector } from '../selectors/conversations';
type MessageDetailProps = ComponentProps<typeof MessageDetail>;
export { Contact } from '../../components/conversation/MessageDetail';
export type OwnProps = Pick<
export type OwnProps = Omit<
MessageDetailProps,
| 'clearSelectedMessage'
| 'checkForAccount'
| 'contacts'
| 'deleteMessage'
| 'deleteMessageForEveryone'
| 'displayTapToViewMessage'
| 'downloadAttachment'
| 'doubleCheckMissingQuoteReference'
| 'errors'
| 'kickOffAttachmentDownload'
| 'markAttachmentAsCorrupted'
| 'markViewed'
| 'message'
| 'openConversation'
| 'openLink'
| 'reactToMessage'
| 'receivedAt'
| 'replyToMessage'
| 'retrySend'
| 'sendAnyway'
| 'sentAt'
| 'showContactDetail'
| 'showContactModal'
| 'showExpiredIncomingTapToViewToast'
| 'showExpiredOutgoingTapToViewToast'
| 'showForwardMessageModal'
| 'showSafetyNumber'
| 'showVisualAttachment'
'i18n' | 'interactionMode' | 'renderAudioAttachment' | 'renderEmojiPicker'
>;
const mapStateToProps = (
@ -63,13 +35,7 @@ const mapStateToProps = (
sendAnyway,
showSafetyNumber,
checkForAccount,
clearSelectedMessage,
deleteMessage,
deleteMessageForEveryone,
displayTapToViewMessage,
downloadAttachment,
doubleCheckMissingQuoteReference,
kickOffAttachmentDownload,
markAttachmentAsCorrupted,
markViewed,
@ -108,13 +74,7 @@ const mapStateToProps = (
sendAnyway,
showSafetyNumber,
checkForAccount,
clearSelectedMessage,
deleteMessage,
deleteMessageForEveryone,
displayTapToViewMessage,
downloadAttachment,
doubleCheckMissingQuoteReference,
kickOffAttachmentDownload,
markAttachmentAsCorrupted,
markViewed,

View File

@ -73,28 +73,55 @@ export type DownloadedAttachmentType = AttachmentType & {
data: ArrayBuffer;
};
type BaseAttachmentDraftType = {
export type BaseAttachmentDraftType = {
blurHash?: string;
contentType: MIME.MIMEType;
fileName: string;
path: string;
screenshotContentType?: string;
screenshotSize?: number;
size: number;
};
export type InMemoryAttachmentDraftType = {
data?: ArrayBuffer;
screenshotData?: ArrayBuffer;
} & BaseAttachmentDraftType;
export type InMemoryAttachmentDraftType =
| ({
data?: ArrayBuffer;
pending: false;
screenshotData?: ArrayBuffer;
} & BaseAttachmentDraftType)
| {
contentType: MIME.MIMEType;
fileName: string;
path: string;
pending: true;
};
export type OnDiskAttachmentDraftType = {
path?: string;
screenshotPath?: string;
} & BaseAttachmentDraftType;
export type OnDiskAttachmentDraftType =
| ({
caption?: string;
pending: false;
screenshotPath?: string;
} & BaseAttachmentDraftType)
| {
contentType: MIME.MIMEType;
fileName: string;
path: string;
pending: true;
};
export type AttachmentDraftType = {
url: string;
} & BaseAttachmentDraftType;
export type AttachmentDraftType =
| ({
url: string;
screenshotPath?: string;
caption?: string;
pending: false;
} & BaseAttachmentDraftType)
| {
contentType: MIME.MIMEType;
fileName: string;
path: string;
pending: true;
};
export type ThumbnailType = {
height: number;

View File

@ -69,9 +69,13 @@ export function findLinks(text: string, caretLocation?: number): Array<string> {
);
}
export function getDomain(href: string): string | undefined {
export function getDomain(href: string): string {
const url = maybeParseUrl(href);
return url ? url.hostname : undefined;
if (!url || !url.hostname) {
throw new Error('getDomain: Unable to extract hostname from href');
}
return url.hostname;
}
// See <https://tools.ietf.org/html/rfc3986>.

View File

@ -48,11 +48,13 @@ export async function handleImageAttachment(
const blurHash = await imageToBlurHash(resizedBlob);
return {
fileName: fileName || file.name,
blurHash,
contentType,
data,
fileName: fileName || file.name,
path: file.name,
pending: false,
size: data.byteLength,
blurHash,
};
}

View File

@ -16,7 +16,7 @@ export async function longRunningTaskWrapper<T>({
const ONE_SECOND = 1000;
const TWO_SECONDS = 2000;
let progressView: typeof window.Whisper.ReactWrapperView | undefined;
let progressView: Backbone.View | undefined;
let spinnerStart;
let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => {
window.log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`);
@ -76,11 +76,13 @@ export async function longRunningTaskWrapper<T>({
// Note: this component uses a portal to render itself into the top-level DOM. No
// need to attach it to the DOM here.
const errorView = new window.Whisper.ReactWrapperView({
const errorView: Backbone.View = new window.Whisper.ReactWrapperView({
className: 'error-modal-wrapper',
Component: window.Signal.Components.ErrorModal,
props: {
onClose: () => errorView.remove(),
onClose: (): void => {
errorView.remove();
},
},
});
}

File diff suppressed because it is too large Load Diff

151
ts/window.d.ts vendored
View File

@ -120,6 +120,7 @@ import { StateType } from './state/reducer';
import { SystemTraySetting } from './types/SystemTraySetting';
import { CI } from './CI';
import { IPCEventsType } from './util/createIPCEvents';
import { ConversationView } from './views/conversation_view';
export { Long } from 'long';
@ -576,8 +577,33 @@ export type LoggerType = {
export type LogFunctionType = (...args: Array<unknown>) => void;
export class AnyViewClass extends window.Backbone.View<any> {
public headerTitle?: string;
static show(view: typeof AnyViewClass, element: Element): void;
constructor(options?: any);
}
export class BasicReactWrapperViewClass extends AnyViewClass {
public update(options: any): void;
}
export type WhisperType = {
Conversation: typeof ConversationModel;
ConversationCollection: typeof ConversationModelCollectionType;
Message: typeof MessageModel;
MessageCollection: typeof MessageModelCollectionType;
GroupMemberConversation: WhatIsThis;
KeyChangeListener: WhatIsThis;
RotateSignedPreKeyListener: WhatIsThis;
WallClockListener: WhatIsThis;
deliveryReceiptQueue: PQueue;
deliveryReceiptBatcher: BatcherType<DeliveryReceiptBatcherItemType>;
events: Backbone.Events;
activeConfirmationView: WhatIsThis;
Database: {
open: () => Promise<IDBDatabase>;
handleDOMException: (
@ -586,38 +612,6 @@ export type WhisperType = {
reject: Function
) => void;
};
ConversationCollection: typeof ConversationModelCollectionType;
ConversationCollectionType: ConversationModelCollectionType;
Conversation: typeof ConversationModel;
ConversationType: ConversationModel;
MessageCollection: typeof MessageModelCollectionType;
MessageCollectionType: MessageModelCollectionType;
MessageAttributesType: MessageAttributesType;
Message: typeof MessageModel;
MessageType: MessageModel;
GroupMemberConversation: WhatIsThis;
KeyChangeListener: WhatIsThis;
ClearDataView: WhatIsThis;
ReactWrapperView: WhatIsThis;
activeConfirmationView: WhatIsThis;
ToastView: typeof window.Whisper.View & {
show: (view: typeof Backbone.View, el: Element) => void;
};
ConversationArchivedToast: WhatIsThis;
ConversationUnarchivedToast: WhatIsThis;
ConversationMarkedUnreadToast: WhatIsThis;
WallClockListener: WhatIsThis;
BannerView: any;
RecorderView: any;
GroupMemberList: any;
GroupLinkCopiedToast: typeof Backbone.View;
InboxView: typeof window.Whisper.View;
InstallView: typeof window.Whisper.View;
StandaloneRegistrationView: typeof window.Whisper.View;
KeyVerificationPanelView: any;
SafetyNumberChangeDialogView: any;
BodyRangesType: BodyRangesType;
BodyRangeType: BodyRangeType;
Notifications: {
isEnabled: boolean;
@ -643,45 +637,60 @@ export type WhisperType = {
update: () => void;
};
deliveryReceiptQueue: PQueue;
deliveryReceiptBatcher: BatcherType<DeliveryReceiptBatcherItemType>;
RotateSignedPreKeyListener: WhatIsThis;
// Backbone views
AlreadyGroupMemberToast: typeof window.Whisper.ToastView;
AlreadyRequestedToJoinToast: typeof window.Whisper.ToastView;
BlockedGroupToast: typeof window.Whisper.ToastView;
BlockedToast: typeof window.Whisper.ToastView;
CannotMixImageAndNonImageAttachmentsToast: typeof window.Whisper.ToastView;
CaptchaSolvedToast: typeof window.Whisper.ToastView;
CaptchaFailedToast: typeof window.Whisper.ToastView;
CannotStartGroupCallToast: typeof window.Whisper.ToastView;
DangerousFileTypeToast: typeof window.Whisper.ToastView;
DecryptionErrorToast: typeof window.Whisper.ToastView;
ExpiredToast: typeof window.Whisper.ToastView;
FileSavedToast: typeof window.Whisper.ToastView;
FileSizeToast: any;
FoundButNotLoadedToast: typeof window.Whisper.ToastView;
InvalidConversationToast: typeof window.Whisper.ToastView;
LeftGroupToast: typeof window.Whisper.ToastView;
MaxAttachmentsToast: typeof window.Whisper.ToastView;
MessageBodyTooLongToast: typeof window.Whisper.ToastView;
OneNonImageAtATimeToast: typeof window.Whisper.ToastView;
OriginalNoLongerAvailableToast: typeof window.Whisper.ToastView;
OriginalNotFoundToast: typeof window.Whisper.ToastView;
PinnedConversationsFullToast: typeof window.Whisper.ToastView;
ReactionFailedToast: typeof window.Whisper.ToastView;
DeleteForEveryoneFailedToast: typeof window.Whisper.ToastView;
TapToViewExpiredIncomingToast: typeof window.Whisper.ToastView;
TapToViewExpiredOutgoingToast: typeof window.Whisper.ToastView;
TimerConflictToast: typeof window.Whisper.ToastView;
UnableToLoadToast: typeof window.Whisper.ToastView;
VoiceNoteLimit: typeof window.Whisper.ToastView;
VoiceNoteMustBeOnlyAttachmentToast: typeof window.Whisper.ToastView;
// Modernized
ConversationView: typeof ConversationView;
ConversationLoadingScreen: typeof window.Whisper.View;
ConversationView: typeof window.Whisper.View;
View: typeof Backbone.View & {
Templates: Record<string, string>;
};
DisappearingTimeDialog: typeof window.Whisper.View | undefined;
// Note: we can no longer use 'View.extend' once we've moved to Typescript's preferred
// 'extend View' syntax. Thus, we'll need to typescriptify most of it at once.
// Toast
AlreadyGroupMemberToast: typeof AnyViewClass;
AlreadyRequestedToJoinToast: typeof AnyViewClass;
BlockedGroupToast: typeof AnyViewClass;
BlockedToast: typeof AnyViewClass;
CannotMixImageAndNonImageAttachmentsToast: typeof AnyViewClass;
CaptchaSolvedToast: typeof AnyViewClass;
CaptchaFailedToast: typeof AnyViewClass;
CannotStartGroupCallToast: typeof AnyViewClass;
ConversationArchivedToast: typeof AnyViewClass;
ConversationUnarchivedToast: typeof AnyViewClass;
ConversationMarkedUnreadToast: typeof AnyViewClass;
DangerousFileTypeToast: typeof AnyViewClass;
DecryptionErrorToast: typeof AnyViewClass;
ExpiredToast: typeof AnyViewClass;
FileSavedToast: typeof AnyViewClass;
FileSizeToast: typeof AnyViewClass;
FoundButNotLoadedToast: typeof AnyViewClass;
GroupLinkCopiedToast: typeof AnyViewClass;
InvalidConversationToast: typeof AnyViewClass;
LeftGroupToast: typeof AnyViewClass;
MaxAttachmentsToast: typeof AnyViewClass;
MessageBodyTooLongToast: typeof AnyViewClass;
OneNonImageAtATimeToast: typeof AnyViewClass;
OriginalNoLongerAvailableToast: typeof AnyViewClass;
OriginalNotFoundToast: typeof AnyViewClass;
PinnedConversationsFullToast: typeof AnyViewClass;
ReactionFailedToast: typeof AnyViewClass;
DeleteForEveryoneFailedToast: typeof AnyViewClass;
TapToViewExpiredIncomingToast: typeof AnyViewClass;
TapToViewExpiredOutgoingToast: typeof AnyViewClass;
TimerConflictToast: typeof AnyViewClass;
UnableToLoadToast: typeof AnyViewClass;
VoiceNoteLimit: typeof AnyViewClass;
VoiceNoteMustBeOnlyAttachmentToast: typeof AnyViewClass;
ClearDataView: typeof AnyViewClass;
ConversationLoadingScreen: typeof AnyViewClass;
GroupMemberList: typeof AnyViewClass;
InboxView: typeof AnyViewClass;
InstallView: typeof AnyViewClass;
KeyVerificationPanelView: typeof AnyViewClass;
ReactWrapperView: typeof BasicReactWrapperViewClass;
RecorderView: typeof AnyViewClass;
SafetyNumberChangeDialogView: typeof AnyViewClass;
StandaloneRegistrationView: typeof AnyViewClass;
ToastView: typeof AnyViewClass;
View: typeof AnyViewClass;
};

View File

@ -2363,10 +2363,10 @@
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
"@types/backbone@1.4.3":
version "1.4.3"
resolved "https://registry.yarnpkg.com/@types/backbone/-/backbone-1.4.3.tgz#75dc6e55382e226788db8d796de346891d6b2256"
integrity sha512-PZVw2FckEbEJ+qh2hvtgpI/4p8yD3sRbA8FEO72k01/90SSH73GcLW3CqcYP5epwDpLl3cKrgK0yypQY4qiuEw==
"@types/backbone@1.4.5":
version "1.4.5"
resolved "https://registry.yarnpkg.com/@types/backbone/-/backbone-1.4.5.tgz#057d89987fb672a20b896b1df5cc802f7b87c624"
integrity sha512-pSqM0eryp6V3G0srBtndUd9IJmiG2BAwYLQGPDcEPMjbfbgitlrN40+Lc1rrMjNMbV5QWywe6WPmNjdqyNTyIw==
dependencies:
"@types/jquery" "*"
"@types/underscore" "*"
@ -2574,13 +2574,20 @@
resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.18.1.tgz#10090d596053703e7de0ac43a37b96cd9fc78309"
integrity sha512-MUgbY3CF7hg/a/jogixmAufLjJBQT7WEf8Q+kYJkOc47ytngg1IuZobCngdTjAgY83JWEogippge5O5fplaQlw==
"@types/jquery@*", "@types/jquery@3.5.0":
"@types/jquery@*":
version "3.5.0"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.0.tgz#ccb7dfd317d02d4227dd3803c75297d0c10dad68"
integrity sha512-C7qQUjpMWDUNYQRTXsP5nbYYwCwwgy84yPgoTT7fPN69NH92wLeCtFaMsWeolJD1AF/6uQw3pYt62rzv83sMmw==
dependencies:
"@types/sizzle" "*"
"@types/jquery@3.5.6":
version "3.5.6"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.6.tgz#97ac8e36dccd8ad8ed3f3f3b48933614d9fd8cf0"
integrity sha512-SmgCQRzGPId4MZQKDj9Hqc6kSXFNWZFHpELkyK8AQhf8Zr6HKfCzFv9ZC1Fv3FyQttJZOlap3qYb12h61iZAIg==
dependencies:
"@types/sizzle" "*"
"@types/js-yaml@3.12.0":
version "3.12.0"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6"
@ -2658,6 +2665,11 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.0.0.tgz#a3014921991066193f6c8e47290d4d598dfd19e6"
integrity sha512-ZS0vBV7Jn5Z/Q4T3VXauEKMDCV8nWOtJJg90OsDylkYJiQwcWtKuLzohWzrthBkerUF7DLMmJcwOPEP0i/AOXw==
"@types/mustache@4.1.2":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.1.2.tgz#d0e158013c81674a5b6d8780bc3fe234e1804eaf"
integrity sha512-c4OVMMcyodKQ9dpwBwh3ofK9P6U9ZktKU9S+p33UqwMNN1vlv2P0zJZUScTshnx7OEoIIRcCFNQ904sYxZz8kg==
"@types/node-fetch@2.5.7":
version "2.5.7"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c"