Add message schema version section to internal settings
This commit is contained in:
parent
115b79e4ac
commit
46bf933e72
7 changed files with 197 additions and 9 deletions
|
@ -625,21 +625,41 @@ $secondary-text-color: light-dark(
|
||||||
margin-inline: 4px;
|
margin-inline: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pre {
|
||||||
|
max-height: 128px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Preferences--internal--validate-backup--result {
|
.Preferences--internal--result {
|
||||||
padding-inline: 48px 24px;
|
padding-inline: 48px 24px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding-inline: 16px;
|
||||||
|
padding-block: 4px;
|
||||||
|
text-align: start;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
.Preferences--internal--subresult {
|
||||||
|
background-color: variables.$color-white-alpha-06;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Preferences--internal--validate-backup--error {
|
.Preferences--internal--error {
|
||||||
padding-inline: 48px 24px;
|
padding-inline: 48px 24px;
|
||||||
color: variables.$color-accent-red;
|
color: variables.$color-accent-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Preferences--internal--validate-backup--result pre,
|
.Preferences--internal pre,
|
||||||
.Preferences--internal--validate-backup--error pre {
|
.Preferences--internal pre {
|
||||||
max-height: 128px;
|
max-height: 400px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
|
overflow-x: scroll;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { DialogType } from '../types/Dialogs';
|
||||||
|
|
||||||
import type { PropsType } from './Preferences';
|
import type { PropsType } from './Preferences';
|
||||||
import type { WidthBreakpoint } from './_util';
|
import type { WidthBreakpoint } from './_util';
|
||||||
|
import type { MessageAttributesType } from '../model-types';
|
||||||
|
|
||||||
const { i18n } = window.SignalContext;
|
const { i18n } = window.SignalContext;
|
||||||
|
|
||||||
|
@ -200,6 +201,13 @@ export default {
|
||||||
result: exportLocalBackupResult,
|
result: exportLocalBackupResult,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getMessageCountBySchemaVersion: async () => [
|
||||||
|
{ schemaVersion: 10, count: 1024 },
|
||||||
|
{ schemaVersion: 8, count: 256 },
|
||||||
|
],
|
||||||
|
getMessageSampleForSchemaVersion: async () => [
|
||||||
|
{ id: 'messageId' } as MessageAttributesType,
|
||||||
|
],
|
||||||
makeSyncRequest: action('makeSyncRequest'),
|
makeSyncRequest: action('makeSyncRequest'),
|
||||||
onAudioNotificationsChange: action('onAudioNotificationsChange'),
|
onAudioNotificationsChange: action('onAudioNotificationsChange'),
|
||||||
onAutoConvertEmojiChange: action('onAutoConvertEmojiChange'),
|
onAutoConvertEmojiChange: action('onAutoConvertEmojiChange'),
|
||||||
|
|
|
@ -75,6 +75,8 @@ import { PreferencesInternal } from './PreferencesInternal';
|
||||||
import { FunEmojiLocalizationProvider } from './fun/FunEmojiLocalizationProvider';
|
import { FunEmojiLocalizationProvider } from './fun/FunEmojiLocalizationProvider';
|
||||||
import { NavTabsToggle } from './NavTabs';
|
import { NavTabsToggle } from './NavTabs';
|
||||||
import type { UnreadStats } from '../util/countUnreadStats';
|
import type { UnreadStats } from '../util/countUnreadStats';
|
||||||
|
import type { MessageCountBySchemaVersionType } from '../sql/Interface';
|
||||||
|
import type { MessageAttributesType } from '../model-types';
|
||||||
|
|
||||||
type CheckboxChangeHandlerType = (value: boolean) => unknown;
|
type CheckboxChangeHandlerType = (value: boolean) => unknown;
|
||||||
type SelectChangeHandlerType<T = string | number> = (value: T) => unknown;
|
type SelectChangeHandlerType<T = string | number> = (value: T) => unknown;
|
||||||
|
@ -171,6 +173,10 @@ type PropsFunctionType = {
|
||||||
doDeleteAllData: () => unknown;
|
doDeleteAllData: () => unknown;
|
||||||
editCustomColor: (colorId: string, color: CustomColorType) => unknown;
|
editCustomColor: (colorId: string, color: CustomColorType) => unknown;
|
||||||
exportLocalBackup: () => Promise<BackupValidationResultType>;
|
exportLocalBackup: () => Promise<BackupValidationResultType>;
|
||||||
|
getMessageCountBySchemaVersion: () => Promise<MessageCountBySchemaVersionType>;
|
||||||
|
getMessageSampleForSchemaVersion: (
|
||||||
|
version: number
|
||||||
|
) => Promise<Array<MessageAttributesType>>;
|
||||||
getConversationsWithCustomColor: (colorId: string) => Array<ConversationType>;
|
getConversationsWithCustomColor: (colorId: string) => Array<ConversationType>;
|
||||||
makeSyncRequest: () => unknown;
|
makeSyncRequest: () => unknown;
|
||||||
onStartUpdate: () => unknown;
|
onStartUpdate: () => unknown;
|
||||||
|
@ -301,6 +307,8 @@ export function Preferences({
|
||||||
emojiSkinToneDefault,
|
emojiSkinToneDefault,
|
||||||
exportLocalBackup,
|
exportLocalBackup,
|
||||||
getConversationsWithCustomColor,
|
getConversationsWithCustomColor,
|
||||||
|
getMessageCountBySchemaVersion,
|
||||||
|
getMessageSampleForSchemaVersion,
|
||||||
hasAudioNotifications,
|
hasAudioNotifications,
|
||||||
hasAutoConvertEmoji,
|
hasAutoConvertEmoji,
|
||||||
hasAutoDownloadUpdate,
|
hasAutoDownloadUpdate,
|
||||||
|
@ -1800,6 +1808,8 @@ export function Preferences({
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
exportLocalBackup={exportLocalBackup}
|
exportLocalBackup={exportLocalBackup}
|
||||||
validateBackup={validateBackup}
|
validateBackup={validateBackup}
|
||||||
|
getMessageCountBySchemaVersion={getMessageCountBySchemaVersion}
|
||||||
|
getMessageSampleForSchemaVersion={getMessageSampleForSchemaVersion}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,21 +10,35 @@ import type { ValidationResultType as BackupValidationResultType } from '../serv
|
||||||
import { SettingsRow, SettingsControl } from './PreferencesUtil';
|
import { SettingsRow, SettingsControl } from './PreferencesUtil';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
|
import type { MessageCountBySchemaVersionType } from '../sql/Interface';
|
||||||
|
import type { MessageAttributesType } from '../model-types';
|
||||||
|
|
||||||
export function PreferencesInternal({
|
export function PreferencesInternal({
|
||||||
i18n,
|
i18n,
|
||||||
exportLocalBackup: doExportLocalBackup,
|
exportLocalBackup: doExportLocalBackup,
|
||||||
validateBackup: doValidateBackup,
|
validateBackup: doValidateBackup,
|
||||||
|
getMessageCountBySchemaVersion,
|
||||||
|
getMessageSampleForSchemaVersion,
|
||||||
}: {
|
}: {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
exportLocalBackup: () => Promise<BackupValidationResultType>;
|
exportLocalBackup: () => Promise<BackupValidationResultType>;
|
||||||
validateBackup: () => Promise<BackupValidationResultType>;
|
validateBackup: () => Promise<BackupValidationResultType>;
|
||||||
|
getMessageCountBySchemaVersion: () => Promise<MessageCountBySchemaVersionType>;
|
||||||
|
getMessageSampleForSchemaVersion: (
|
||||||
|
version: number
|
||||||
|
) => Promise<Array<MessageAttributesType>>;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const [isExportPending, setIsExportPending] = useState(false);
|
const [isExportPending, setIsExportPending] = useState(false);
|
||||||
const [exportResult, setExportResult] = useState<
|
const [exportResult, setExportResult] = useState<
|
||||||
BackupValidationResultType | undefined
|
BackupValidationResultType | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
const [messageCountBySchemaVersion, setMessageCountBySchemaVersion] =
|
||||||
|
useState<MessageCountBySchemaVersionType>();
|
||||||
|
const [messageSampleForVersions, setMessageSampleForVersions] = useState<{
|
||||||
|
[schemaVersion: number]: Array<MessageAttributesType>;
|
||||||
|
}>();
|
||||||
|
|
||||||
const [isValidationPending, setIsValidationPending] = useState(false);
|
const [isValidationPending, setIsValidationPending] = useState(false);
|
||||||
const [validationResult, setValidationResult] = useState<
|
const [validationResult, setValidationResult] = useState<
|
||||||
BackupValidationResultType | undefined
|
BackupValidationResultType | undefined
|
||||||
|
@ -68,7 +82,7 @@ export function PreferencesInternal({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Preferences--internal--validate-backup--result">
|
<div className="Preferences--internal--result">
|
||||||
{snapshotDirEl}
|
{snapshotDirEl}
|
||||||
<p>Main file size: {formatFileSize(totalBytes)}</p>
|
<p>Main file size: {formatFileSize(totalBytes)}</p>
|
||||||
<p>Duration: {Math.round(duration / SECOND)}s</p>
|
<p>Duration: {Math.round(duration / SECOND)}s</p>
|
||||||
|
@ -82,7 +96,7 @@ export function PreferencesInternal({
|
||||||
const { error } = backupResult;
|
const { error } = backupResult;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Preferences--internal--validate-backup--error">
|
<div className="Preferences--internal--error">
|
||||||
<pre>
|
<pre>
|
||||||
<code>{error}</code>
|
<code>{error}</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -105,7 +119,7 @@ export function PreferencesInternal({
|
||||||
}, [doExportLocalBackup]);
|
}, [doExportLocalBackup]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="Preferences--internal">
|
||||||
<SettingsRow
|
<SettingsRow
|
||||||
className="Preferences--internal--backups"
|
className="Preferences--internal--backups"
|
||||||
title={i18n('icu:Preferences__button--backups')}
|
title={i18n('icu:Preferences__button--backups')}
|
||||||
|
@ -155,6 +169,91 @@ export function PreferencesInternal({
|
||||||
|
|
||||||
{renderValidationResult(exportResult)}
|
{renderValidationResult(exportResult)}
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
</>
|
|
||||||
|
<SettingsRow
|
||||||
|
className="Preferences--internal--message-schemas"
|
||||||
|
title="Message schema versions"
|
||||||
|
>
|
||||||
|
<SettingsControl
|
||||||
|
left="Check message schema versions"
|
||||||
|
right={
|
||||||
|
<Button
|
||||||
|
variant={ButtonVariant.Secondary}
|
||||||
|
onClick={async () => {
|
||||||
|
setMessageCountBySchemaVersion(
|
||||||
|
await getMessageCountBySchemaVersion()
|
||||||
|
);
|
||||||
|
setMessageSampleForVersions({});
|
||||||
|
}}
|
||||||
|
disabled={isExportPending}
|
||||||
|
>
|
||||||
|
Fetch data
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{messageCountBySchemaVersion ? (
|
||||||
|
<div className="Preferences--internal--result">
|
||||||
|
<pre>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Schema version</th>
|
||||||
|
<th># Messages</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{messageCountBySchemaVersion.map(
|
||||||
|
({ schemaVersion, count }) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={schemaVersion}>
|
||||||
|
<tr>
|
||||||
|
<td>{schemaVersion}</td>
|
||||||
|
<td>{count}</td>
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={async () => {
|
||||||
|
const sampleMessages =
|
||||||
|
await getMessageSampleForSchemaVersion(
|
||||||
|
schemaVersion
|
||||||
|
);
|
||||||
|
setMessageSampleForVersions({
|
||||||
|
[schemaVersion]: sampleMessages,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={isExportPending}
|
||||||
|
>
|
||||||
|
Sample
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{messageSampleForVersions?.[schemaVersion] ? (
|
||||||
|
<tr
|
||||||
|
key={`${schemaVersion}_samples`}
|
||||||
|
className="Preferences--internal--subresult"
|
||||||
|
>
|
||||||
|
<td colSpan={3}>
|
||||||
|
<code>
|
||||||
|
{JSON.stringify(
|
||||||
|
messageSampleForVersions[schemaVersion],
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : null}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</SettingsRow>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -548,6 +548,11 @@ export enum AttachmentDownloadSource {
|
||||||
BACKFILL = 'backfill',
|
BACKFILL = 'backfill',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MessageCountBySchemaVersionType = Array<{
|
||||||
|
schemaVersion: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
export const MESSAGE_ATTACHMENT_COLUMNS = [
|
export const MESSAGE_ATTACHMENT_COLUMNS = [
|
||||||
'messageId',
|
'messageId',
|
||||||
'conversationId',
|
'conversationId',
|
||||||
|
@ -888,6 +893,11 @@ type ReadableInterface = {
|
||||||
getAttachmentReferencesForMessages: (
|
getAttachmentReferencesForMessages: (
|
||||||
messageIds: Array<string>
|
messageIds: Array<string>
|
||||||
) => Array<MessageAttachmentDBType>;
|
) => Array<MessageAttachmentDBType>;
|
||||||
|
|
||||||
|
getMessageCountBySchemaVersion: () => MessageCountBySchemaVersionType;
|
||||||
|
getMessageSampleForSchemaVersion: (
|
||||||
|
version: number
|
||||||
|
) => Array<MessageAttributesType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WritableInterface = {
|
type WritableInterface = {
|
||||||
|
|
|
@ -186,6 +186,7 @@ import type {
|
||||||
MessageAttachmentDBType,
|
MessageAttachmentDBType,
|
||||||
MessageTypeUnhydrated,
|
MessageTypeUnhydrated,
|
||||||
ServerMessageSearchResultType,
|
ServerMessageSearchResultType,
|
||||||
|
MessageCountBySchemaVersionType,
|
||||||
} from './Interface';
|
} from './Interface';
|
||||||
import {
|
import {
|
||||||
AttachmentDownloadSource,
|
AttachmentDownloadSource,
|
||||||
|
@ -436,6 +437,8 @@ export const DataReader: ServerReadableInterface = {
|
||||||
getBackupCdnObjectMetadata,
|
getBackupCdnObjectMetadata,
|
||||||
getSizeOfPendingBackupAttachmentDownloadJobs,
|
getSizeOfPendingBackupAttachmentDownloadJobs,
|
||||||
getAttachmentReferencesForMessages,
|
getAttachmentReferencesForMessages,
|
||||||
|
getMessageCountBySchemaVersion,
|
||||||
|
getMessageSampleForSchemaVersion,
|
||||||
|
|
||||||
// Server-only
|
// Server-only
|
||||||
getKnownMessageAttachments,
|
getKnownMessageAttachments,
|
||||||
|
@ -8463,6 +8466,37 @@ function getUnreadEditedMessagesAndMarkRead(
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessageCountBySchemaVersion(
|
||||||
|
db: ReadableDB
|
||||||
|
): MessageCountBySchemaVersionType {
|
||||||
|
const [query, params] = sql`
|
||||||
|
SELECT schemaVersion, COUNT(1) as count from messages
|
||||||
|
GROUP BY schemaVersion;
|
||||||
|
`;
|
||||||
|
const rows = db
|
||||||
|
.prepare(query)
|
||||||
|
.all<{ schemaVersion: number; count: number }>(params);
|
||||||
|
|
||||||
|
return rows.sort((a, b) => a.schemaVersion - b.schemaVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageSampleForSchemaVersion(
|
||||||
|
db: ReadableDB,
|
||||||
|
version: number
|
||||||
|
): Array<MessageAttributesType> {
|
||||||
|
return db.transaction(() => {
|
||||||
|
const [query, params] = sql`
|
||||||
|
SELECT * from messages
|
||||||
|
WHERE schemaVersion = ${version}
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 2;
|
||||||
|
`;
|
||||||
|
const rows = db.prepare(query).all<MessageTypeUnhydrated>(params);
|
||||||
|
|
||||||
|
return hydrateMessages(db, rows);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
function disableMessageInsertTriggers(db: WritableDB): void {
|
function disableMessageInsertTriggers(db: WritableDB): void {
|
||||||
db.transaction(() => {
|
db.transaction(() => {
|
||||||
createOrUpdateItem(db, {
|
createOrUpdateItem(db, {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import {
|
||||||
} from '../selectors/updates';
|
} from '../selectors/updates';
|
||||||
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
||||||
import { getOtherTabsUnreadStats } from '../selectors/nav';
|
import { getOtherTabsUnreadStats } from '../selectors/nav';
|
||||||
|
import { DataReader } from '../../sql/Client';
|
||||||
|
|
||||||
const DEFAULT_NOTIFICATION_SETTING = 'message';
|
const DEFAULT_NOTIFICATION_SETTING = 'message';
|
||||||
|
|
||||||
|
@ -604,6 +605,12 @@ export function SmartPreferences(): JSX.Element {
|
||||||
doDeleteAllData={doDeleteAllData}
|
doDeleteAllData={doDeleteAllData}
|
||||||
editCustomColor={editCustomColor}
|
editCustomColor={editCustomColor}
|
||||||
getConversationsWithCustomColor={getConversationsWithCustomColor}
|
getConversationsWithCustomColor={getConversationsWithCustomColor}
|
||||||
|
getMessageCountBySchemaVersion={
|
||||||
|
DataReader.getMessageCountBySchemaVersion
|
||||||
|
}
|
||||||
|
getMessageSampleForSchemaVersion={
|
||||||
|
DataReader.getMessageSampleForSchemaVersion
|
||||||
|
}
|
||||||
hasAudioNotifications={hasAudioNotifications}
|
hasAudioNotifications={hasAudioNotifications}
|
||||||
hasAutoConvertEmoji={hasAutoConvertEmoji}
|
hasAutoConvertEmoji={hasAutoConvertEmoji}
|
||||||
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
|
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue