Use POJO instead of MessageModel in ConversationView quote logic
This commit is contained in:
parent
c9b1ce6655
commit
6b2dfeb9f5
8 changed files with 92 additions and 119 deletions
|
@ -137,7 +137,6 @@ const Errors = require('./types/errors');
|
||||||
const MediaGalleryMessage = require('../../ts/components/conversation/media-gallery/types/Message');
|
const MediaGalleryMessage = require('../../ts/components/conversation/media-gallery/types/Message');
|
||||||
const MessageType = require('./types/message');
|
const MessageType = require('./types/message');
|
||||||
const MIME = require('../../ts/types/MIME');
|
const MIME = require('../../ts/types/MIME');
|
||||||
const PhoneNumber = require('../../ts/types/PhoneNumber');
|
|
||||||
const SettingsType = require('../../ts/types/Settings');
|
const SettingsType = require('../../ts/types/Settings');
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
|
@ -417,7 +416,6 @@ exports.setup = (options = {}) => {
|
||||||
Errors,
|
Errors,
|
||||||
Message: MessageType,
|
Message: MessageType,
|
||||||
MIME,
|
MIME,
|
||||||
PhoneNumber,
|
|
||||||
Settings: SettingsType,
|
Settings: SettingsType,
|
||||||
VisualAttachment,
|
VisualAttachment,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2831,7 +2831,13 @@ export class ConversationModel extends window.Backbone
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return number.error || 'Invalid phone number';
|
let errorMessage: undefined | string;
|
||||||
|
if (number.error instanceof Error) {
|
||||||
|
errorMessage = number.error.message;
|
||||||
|
} else if (typeof number.error === 'string') {
|
||||||
|
errorMessage = number.error;
|
||||||
|
}
|
||||||
|
return errorMessage || 'Invalid phone number';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
import { find } from '../util/iterables';
|
import { find } from '../util/iterables';
|
||||||
import { DataMessageClass } from '../textsecure.d';
|
import { DataMessageClass } from '../textsecure.d';
|
||||||
import { ConversationModel } from './conversations';
|
import { ConversationModel } from './conversations';
|
||||||
import { ConversationType } from '../state/ducks/conversations';
|
|
||||||
import { MessageStatusType } from '../components/conversation/Message';
|
import { MessageStatusType } from '../components/conversation/Message';
|
||||||
import {
|
import {
|
||||||
OwnProps as SmartMessageDetailPropsType,
|
OwnProps as SmartMessageDetailPropsType,
|
||||||
|
@ -36,6 +35,7 @@ import {
|
||||||
} from '../util/whatTypeOfConversation';
|
} from '../util/whatTypeOfConversation';
|
||||||
import { handleMessageSend } from '../util/handleMessageSend';
|
import { handleMessageSend } from '../util/handleMessageSend';
|
||||||
import { getSendOptions } from '../util/getSendOptions';
|
import { getSendOptions } from '../util/getSendOptions';
|
||||||
|
import { findAndFormatContact } from '../util/findAndFormatContact';
|
||||||
import {
|
import {
|
||||||
getLastChallengeError,
|
getLastChallengeError,
|
||||||
getMessagePropStatus,
|
getMessagePropStatus,
|
||||||
|
@ -83,27 +83,6 @@ type PropsForMessageDetail = Pick<
|
||||||
'sentAt' | 'receivedAt' | 'message' | 'errors' | 'contacts'
|
'sentAt' | 'receivedAt' | 'message' | 'errors' | 'contacts'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type FormattedContact = Partial<ConversationType> &
|
|
||||||
Pick<
|
|
||||||
ConversationType,
|
|
||||||
| 'acceptedMessageRequest'
|
|
||||||
| 'id'
|
|
||||||
| 'isMe'
|
|
||||||
| 'sharedGroupNames'
|
|
||||||
| 'title'
|
|
||||||
| 'type'
|
|
||||||
| 'unblurredAvatarPath'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const PLACEHOLDER_CONTACT: FormattedContact = {
|
|
||||||
acceptedMessageRequest: false,
|
|
||||||
id: 'placeholder-contact',
|
|
||||||
isMe: false,
|
|
||||||
sharedGroupNames: [],
|
|
||||||
title: window.i18n('unknownContact'),
|
|
||||||
type: 'direct',
|
|
||||||
};
|
|
||||||
|
|
||||||
declare const _: typeof window._;
|
declare const _: typeof window._;
|
||||||
|
|
||||||
window.Whisper = window.Whisper || {};
|
window.Whisper = window.Whisper || {};
|
||||||
|
@ -113,7 +92,6 @@ const {
|
||||||
Attachment,
|
Attachment,
|
||||||
MIME,
|
MIME,
|
||||||
Contact,
|
Contact,
|
||||||
PhoneNumber,
|
|
||||||
Errors,
|
Errors,
|
||||||
} = window.Signal.Types;
|
} = window.Signal.Types;
|
||||||
const {
|
const {
|
||||||
|
@ -181,9 +159,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
quotedMessage: any;
|
|
||||||
|
|
||||||
syncPromise?: Promise<unknown>;
|
syncPromise?: Promise<unknown>;
|
||||||
|
|
||||||
initialize(attributes: unknown): void {
|
initialize(attributes: unknown): void {
|
||||||
|
@ -203,7 +178,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
this.OUR_NUMBER = window.textsecure.storage.user.getNumber();
|
this.OUR_NUMBER = window.textsecure.storage.user.getNumber();
|
||||||
this.OUR_UUID = window.textsecure.storage.user.getUuid();
|
this.OUR_UUID = window.textsecure.storage.user.getUuid();
|
||||||
|
|
||||||
this.on('unload', this.unload);
|
|
||||||
this.on('change', this.notifyRedux);
|
this.on('change', this.notifyRedux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +301,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
this.isUnidentifiedDelivery(id, unidentifiedLookup);
|
this.isUnidentifiedDelivery(id, unidentifiedLookup);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...this.findAndFormatContact(id),
|
...findAndFormatContact(id),
|
||||||
|
|
||||||
status: this.getStatus(id),
|
status: this.getStatus(id),
|
||||||
errors: errorsForContact,
|
errors: errorsForContact,
|
||||||
|
@ -363,7 +337,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
receivedAt: this.getReceivedAt(),
|
receivedAt: this.getReceivedAt(),
|
||||||
message: getPropsForMessage(
|
message: getPropsForMessage(
|
||||||
this.attributes,
|
this.attributes,
|
||||||
(id?: string | undefined) => this.findAndFormatContact(id),
|
findAndFormatContact,
|
||||||
window.ConversationController.getOurConversationIdOrThrow(),
|
window.ConversationController.getOurConversationIdOrThrow(),
|
||||||
this.OUR_NUMBER,
|
this.OUR_NUMBER,
|
||||||
this.OUR_UUID,
|
this.OUR_UUID,
|
||||||
|
@ -383,44 +357,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dependencies of prop-generation functions
|
// Dependencies of prop-generation functions
|
||||||
findAndFormatContact(identifier?: string): FormattedContact {
|
|
||||||
if (!identifier) {
|
|
||||||
return PLACEHOLDER_CONTACT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contactModel = this.findContact(identifier);
|
|
||||||
if (contactModel) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
return contactModel.format()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { format, isValidNumber } = PhoneNumber;
|
|
||||||
const regionCode = window.storage.get('regionCode');
|
|
||||||
|
|
||||||
if (!isValidNumber(identifier, { regionCode })) {
|
|
||||||
return PLACEHOLDER_CONTACT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const phoneNumber = format(identifier, {
|
|
||||||
ourRegionCode: regionCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
acceptedMessageRequest: false,
|
|
||||||
id: 'phone-only',
|
|
||||||
isMe: false,
|
|
||||||
phoneNumber,
|
|
||||||
sharedGroupNames: [],
|
|
||||||
title: phoneNumber,
|
|
||||||
type: 'direct',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
|
||||||
findContact(identifier?: string): ConversationModel | undefined {
|
|
||||||
return window.ConversationController.get(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
getConversation(): ConversationModel | undefined {
|
getConversation(): ConversationModel | undefined {
|
||||||
return window.ConversationController.get(this.get('conversationId'));
|
return window.ConversationController.get(this.get('conversationId'));
|
||||||
}
|
}
|
||||||
|
@ -484,7 +420,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
if (isProfileChange(attributes)) {
|
if (isProfileChange(attributes)) {
|
||||||
const change = this.get('profileChange');
|
const change = this.get('profileChange');
|
||||||
const changedId = this.get('changedId');
|
const changedId = this.get('changedId');
|
||||||
const changedContact = this.findAndFormatContact(changedId);
|
const changedContact = findAndFormatContact(changedId);
|
||||||
if (!change) {
|
if (!change) {
|
||||||
throw new Error('getNotificationData: profileChange was missing!');
|
throw new Error('getNotificationData: profileChange was missing!');
|
||||||
}
|
}
|
||||||
|
@ -694,7 +630,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
const state = window.reduxStore.getState();
|
const state = window.reduxStore.getState();
|
||||||
const callingNotification = getPropsForCallHistory(
|
const callingNotification = getPropsForCallHistory(
|
||||||
attributes,
|
attributes,
|
||||||
(id?: string | undefined) => this.findAndFormatContact(id),
|
findAndFormatContact,
|
||||||
getCallSelector(state),
|
getCallSelector(state),
|
||||||
getActiveCall(state)
|
getActiveCall(state)
|
||||||
);
|
);
|
||||||
|
@ -724,7 +660,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
if (isKeyChange(attributes)) {
|
if (isKeyChange(attributes)) {
|
||||||
const identifier = this.get('key_changed');
|
const identifier = this.get('key_changed');
|
||||||
const conversation = this.findContact(identifier);
|
const conversation = window.ConversationController.get(identifier);
|
||||||
return {
|
return {
|
||||||
text: window.i18n('safetyNumberChangedGroup', [
|
text: window.i18n('safetyNumberChangedGroup', [
|
||||||
conversation ? conversation.getTitle() : null,
|
conversation ? conversation.getTitle() : null,
|
||||||
|
@ -752,7 +688,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
const bodyRanges = processBodyRanges(
|
const bodyRanges = processBodyRanges(
|
||||||
attributes.bodyRanges,
|
attributes.bodyRanges,
|
||||||
(id?: string | undefined) => this.findAndFormatContact(id)
|
findAndFormatContact
|
||||||
);
|
);
|
||||||
if (bodyRanges) {
|
if (bodyRanges) {
|
||||||
return getTextWithMentions(bodyRanges, body);
|
return getTextWithMentions(bodyRanges, body);
|
||||||
|
@ -769,7 +705,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
const bodyRanges = processBodyRanges(
|
const bodyRanges = processBodyRanges(
|
||||||
attributes.bodyRanges,
|
attributes.bodyRanges,
|
||||||
(id?: string | undefined) => this.findAndFormatContact(id)
|
findAndFormatContact
|
||||||
);
|
);
|
||||||
|
|
||||||
if (bodyRanges && bodyRanges.length) {
|
if (bodyRanges && bodyRanges.length) {
|
||||||
|
@ -839,7 +775,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
this.getConversation()?.debouncedUpdateLastMessage?.();
|
this.getConversation()?.debouncedUpdateLastMessage?.();
|
||||||
|
|
||||||
window.MessageController.unregister(this.id);
|
window.MessageController.unregister(this.id);
|
||||||
this.unload();
|
|
||||||
await this.deleteData();
|
await this.deleteData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,7 +898,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { authorUuid, author, id: sentAt, referencedMessageNotFound } = quote;
|
const { authorUuid, author, id: sentAt, referencedMessageNotFound } = quote;
|
||||||
const contact = this.findContact(authorUuid || author);
|
const contact = window.ConversationController.get(authorUuid || author);
|
||||||
|
|
||||||
// Is the quote really without a reference? Check with our in memory store
|
// Is the quote really without a reference? Check with our in memory store
|
||||||
// first to make sure it's not there.
|
// first to make sure it's not there.
|
||||||
|
@ -1114,12 +1049,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
return !hasSomethingToDisplay;
|
return !hasSomethingToDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
unload(): void {
|
|
||||||
if (this.quotedMessage) {
|
|
||||||
this.quotedMessage = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnidentifiedDelivery(
|
isUnidentifiedDelivery(
|
||||||
contactId: string,
|
contactId: string,
|
||||||
lookup: Record<string, unknown>
|
lookup: Record<string, unknown>
|
||||||
|
|
|
@ -291,7 +291,7 @@ export function getContact(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConversation(
|
export function getConversation(
|
||||||
message: MessageAttributesType,
|
message: Pick<MessageAttributesType, 'conversationId'>,
|
||||||
conversationSelector: GetConversationByIdType
|
conversationSelector: GetConversationByIdType
|
||||||
): ConversationType {
|
): ConversationType {
|
||||||
return conversationSelector(message.conversationId);
|
return conversationSelector(message.conversationId);
|
||||||
|
@ -987,7 +987,7 @@ function getPropsForPreview(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPropsForQuote(
|
export function getPropsForQuote(
|
||||||
message: MessageAttributesType,
|
message: Pick<MessageAttributesType, 'conversationId' | 'quote'>,
|
||||||
conversationSelector: GetConversationByIdType,
|
conversationSelector: GetConversationByIdType,
|
||||||
ourConversationId: string | undefined
|
ourConversationId: string | undefined
|
||||||
): PropsData['quote'] {
|
): PropsData['quote'] {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { instance, PhoneNumberFormat } from '../util/libphonenumberInstance';
|
||||||
function _format(
|
function _format(
|
||||||
phoneNumber: string,
|
phoneNumber: string,
|
||||||
options: {
|
options: {
|
||||||
ourRegionCode: string;
|
ourRegionCode?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|
55
ts/util/findAndFormatContact.ts
Normal file
55
ts/util/findAndFormatContact.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
import { format, isValidNumber } from '../types/PhoneNumber';
|
||||||
|
|
||||||
|
type FormattedContact = Partial<ConversationType> &
|
||||||
|
Pick<
|
||||||
|
ConversationType,
|
||||||
|
| 'acceptedMessageRequest'
|
||||||
|
| 'id'
|
||||||
|
| 'isMe'
|
||||||
|
| 'sharedGroupNames'
|
||||||
|
| 'title'
|
||||||
|
| 'type'
|
||||||
|
| 'unblurredAvatarPath'
|
||||||
|
>;
|
||||||
|
|
||||||
|
const PLACEHOLDER_CONTACT: FormattedContact = {
|
||||||
|
acceptedMessageRequest: false,
|
||||||
|
id: 'placeholder-contact',
|
||||||
|
isMe: false,
|
||||||
|
sharedGroupNames: [],
|
||||||
|
title: window.i18n('unknownContact'),
|
||||||
|
type: 'direct',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function findAndFormatContact(identifier?: string): FormattedContact {
|
||||||
|
if (!identifier) {
|
||||||
|
return PLACEHOLDER_CONTACT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contactModel = window.ConversationController.get(identifier);
|
||||||
|
if (contactModel) {
|
||||||
|
return contactModel.format();
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionCode = window.storage.get('regionCode');
|
||||||
|
|
||||||
|
if (!isValidNumber(identifier, { regionCode })) {
|
||||||
|
return PLACEHOLDER_CONTACT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const phoneNumber = format(identifier, { ourRegionCode: regionCode });
|
||||||
|
|
||||||
|
return {
|
||||||
|
acceptedMessageRequest: false,
|
||||||
|
id: 'phone-only',
|
||||||
|
isMe: false,
|
||||||
|
phoneNumber,
|
||||||
|
sharedGroupNames: [],
|
||||||
|
title: phoneNumber,
|
||||||
|
type: 'direct',
|
||||||
|
};
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import {
|
||||||
isGroupV1,
|
isGroupV1,
|
||||||
isMe,
|
isMe,
|
||||||
} from '../util/whatTypeOfConversation';
|
} from '../util/whatTypeOfConversation';
|
||||||
|
import { findAndFormatContact } from '../util/findAndFormatContact';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
import {
|
import {
|
||||||
canReply,
|
canReply,
|
||||||
|
@ -3745,12 +3746,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (
|
if (message && !canReply(message.attributes, findAndFormatContact)) {
|
||||||
message &&
|
|
||||||
!canReply(message.attributes, (id?: string) =>
|
|
||||||
message.findAndFormatContact(id)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3760,7 +3756,6 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
|
|
||||||
this.quote = null;
|
this.quote = null;
|
||||||
this.quotedMessage = null;
|
this.quotedMessage = null;
|
||||||
this.quoteHolder = null;
|
|
||||||
|
|
||||||
const existing = model.get('quotedMessageId');
|
const existing = model.get('quotedMessageId');
|
||||||
if (existing !== messageId) {
|
if (existing !== messageId) {
|
||||||
|
@ -3806,16 +3801,12 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = new Whisper.Message({
|
const props = getPropsForQuote(
|
||||||
|
{
|
||||||
conversationId: model.id,
|
conversationId: model.id,
|
||||||
quote: this.quote,
|
quote: this.quote,
|
||||||
} as any);
|
},
|
||||||
message.quotedMessage = this.quotedMessage;
|
findAndFormatContact,
|
||||||
this.quoteHolder = message;
|
|
||||||
|
|
||||||
const props = getPropsForQuote(
|
|
||||||
message.attributes,
|
|
||||||
(id?: string) => message.findAndFormatContact(id),
|
|
||||||
window.ConversationController.getOurConversationIdOrThrow()
|
window.ConversationController.getOurConversationIdOrThrow()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3829,7 +3820,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
props: {
|
props: {
|
||||||
...props,
|
...props,
|
||||||
withContentAbove: true,
|
withContentAbove: true,
|
||||||
onClick: () => this.scrollToMessage(message.quotedMessage.id),
|
onClick: () => this.scrollToMessage(this.quotedMessage.id),
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
// This can't be the normal 'onClose' because that is always run when this
|
// This can't be the normal 'onClose' because that is always run when this
|
||||||
// view is removed from the DOM, and would clear the draft quote.
|
// view is removed from the DOM, and would clear the draft quote.
|
||||||
|
|
26
ts/window.d.ts
vendored
26
ts/window.d.ts
vendored
|
@ -220,8 +220,16 @@ declare global {
|
||||||
getRegionCodeForNumber: (number: string) => string;
|
getRegionCodeForNumber: (number: string) => string;
|
||||||
parseNumber: (
|
parseNumber: (
|
||||||
e164: string,
|
e164: string,
|
||||||
regionCode: string
|
defaultRegionCode: string
|
||||||
) => typeof window.Signal.Types.PhoneNumber;
|
) =>
|
||||||
|
| { isValidNumber: false; error: unknown }
|
||||||
|
| {
|
||||||
|
isValidNumber: true;
|
||||||
|
regionCode: string | undefined;
|
||||||
|
countryCode: string;
|
||||||
|
nationalNumber: string;
|
||||||
|
e164: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
parse: (number: string) => string;
|
parse: (number: string) => string;
|
||||||
getRegionCodeForNumber: (number: string) => string;
|
getRegionCodeForNumber: (number: string) => string;
|
||||||
|
@ -400,20 +408,6 @@ declare global {
|
||||||
options: unknown
|
options: unknown
|
||||||
) => Promise<WhatIsThis>;
|
) => Promise<WhatIsThis>;
|
||||||
};
|
};
|
||||||
PhoneNumber: {
|
|
||||||
format: (
|
|
||||||
identifier: string,
|
|
||||||
options: Record<string, unknown>
|
|
||||||
) => string;
|
|
||||||
isValidNumber(
|
|
||||||
phoneNumber: string,
|
|
||||||
options?: {
|
|
||||||
regionCode?: string;
|
|
||||||
}
|
|
||||||
): boolean;
|
|
||||||
e164: string;
|
|
||||||
error: string;
|
|
||||||
};
|
|
||||||
Errors: typeof Errors;
|
Errors: typeof Errors;
|
||||||
Message: {
|
Message: {
|
||||||
CURRENT_SCHEMA_VERSION: number;
|
CURRENT_SCHEMA_VERSION: number;
|
||||||
|
|
Loading…
Reference in a new issue