Display user badges

This commit is contained in:
Evan Hahn 2021-11-02 18:01:13 -05:00 committed by GitHub
parent 927c22ef73
commit f647c4e053
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
95 changed files with 2891 additions and 424 deletions

View file

@ -34,6 +34,7 @@ import { cleanDataForIpc } from './cleanDataForIpc';
import type { ReactionType } from '../types/Reactions';
import type { ConversationColorType, CustomColorType } from '../types/Colors';
import type { UUIDStringType } from '../types/UUID';
import type { BadgeType } from '../badges/types';
import type { ProcessGroupCallRingRequestResult } from '../types/Calling';
import type { RemoveAllConfiguration } from '../types/RemoveAllConfiguration';
import createTaskWithTimeout from '../textsecure/TaskWithTimeout';
@ -272,6 +273,10 @@ const dataInterface: ClientInterface = {
updateEmojiUsage,
getRecentEmojis,
getAllBadges,
updateOrCreateBadges,
badgeImageFileDownloaded,
removeAll,
removeAllConfiguration,
@ -1575,6 +1580,27 @@ async function getRecentEmojis(limit = 32) {
return channels.getRecentEmojis(limit);
}
// Badges
function getAllBadges(): Promise<Array<BadgeType>> {
return channels.getAllBadges();
}
async function updateOrCreateBadges(
badges: ReadonlyArray<BadgeType>
): Promise<void> {
if (badges.length) {
await channels.updateOrCreateBadges(badges);
}
}
function badgeImageFileDownloaded(
url: string,
localPath: string
): Promise<void> {
return channels.badgeImageFileDownloaded(url, localPath);
}
// Other
async function removeAll() {

View file

@ -21,6 +21,7 @@ import type { AttachmentType } from '../types/Attachment';
import type { BodyRangesType } from '../types/Util';
import type { QualifiedAddressStringType } from '../types/QualifiedAddress';
import type { UUIDStringType } from '../types/UUID';
import type { BadgeType } from '../badges/types';
import type { RemoveAllConfiguration } from '../types/RemoveAllConfiguration';
import type { LoggerType } from '../types/Logging';
@ -446,6 +447,10 @@ export type DataInterface = {
updateEmojiUsage: (shortName: string, timeUsed?: number) => Promise<void>;
getRecentEmojis: (limit?: number) => Promise<Array<EmojiType>>;
getAllBadges(): Promise<Array<BadgeType>>;
updateOrCreateBadges(badges: ReadonlyArray<BadgeType>): Promise<void>;
badgeImageFileDownloaded(url: string, localPath: string): Promise<void>;
removeAll: () => Promise<void>;
removeAllConfiguration: (type?: RemoveAllConfiguration) => Promise<void>;
@ -572,6 +577,7 @@ export type ServerInterface = DataInterface & {
removeKnownDraftAttachments: (
allStickers: Array<string>
) => Promise<Array<string>>;
getAllBadgeImageFileLocalPaths: () => Promise<Set<string>>;
};
export type ClientInterface = DataInterface & {

View file

@ -44,6 +44,9 @@ import { formatCountForLogging } from '../logging/formatCountForLogging';
import type { ConversationColorType, CustomColorType } from '../types/Colors';
import { ProcessGroupCallRingRequestResult } from '../types/Calling';
import { RemoveAllConfiguration } from '../types/RemoveAllConfiguration';
import type { BadgeType, BadgeImageType } from '../badges/types';
import { parseBadgeCategory } from '../badges/BadgeCategory';
import { parseBadgeImageTheme } from '../badges/BadgeImageTheme';
import type { LoggerType } from '../types/Logging';
import * as log from '../logging/log';
import type { EmptyQuery, ArrayQuery, Query, JSONRows } from './util';
@ -260,6 +263,10 @@ const dataInterface: ServerInterface = {
updateEmojiUsage,
getRecentEmojis,
getAllBadges,
updateOrCreateBadges,
badgeImageFileDownloaded,
removeAll,
removeAllConfiguration,
@ -289,6 +296,7 @@ const dataInterface: ServerInterface = {
removeKnownAttachments,
removeKnownStickers,
removeKnownDraftAttachments,
getAllBadgeImageFileLocalPaths,
};
export default dataInterface;
@ -3571,12 +3579,149 @@ async function getRecentEmojis(limit = 32): Promise<Array<EmojiType>> {
return rows || [];
}
async function getAllBadges(): Promise<Array<BadgeType>> {
const db = getInstance();
const [badgeRows, badgeImageFileRows] = db.transaction(() => [
db.prepare<EmptyQuery>('SELECT * FROM badges').all(),
db.prepare<EmptyQuery>('SELECT * FROM badgeImageFiles').all(),
])();
const badgeImagesByBadge = new Map<
string,
Array<undefined | BadgeImageType>
>();
for (const badgeImageFileRow of badgeImageFileRows) {
const { badgeId, order, localPath, url, theme } = badgeImageFileRow;
const badgeImages = badgeImagesByBadge.get(badgeId) || [];
badgeImages[order] = {
...(badgeImages[order] || {}),
[parseBadgeImageTheme(theme)]: {
localPath: dropNull(localPath),
url,
},
};
badgeImagesByBadge.set(badgeId, badgeImages);
}
return badgeRows.map(badgeRow => ({
id: badgeRow.id,
category: parseBadgeCategory(badgeRow.category),
name: badgeRow.name,
descriptionTemplate: badgeRow.descriptionTemplate,
images: (badgeImagesByBadge.get(badgeRow.id) || []).filter(isNotNil),
}));
}
// This should match the logic in the badges Redux reducer.
async function updateOrCreateBadges(
badges: ReadonlyArray<BadgeType>
): Promise<void> {
const db = getInstance();
const insertBadge = prepare<Query>(
db,
`
INSERT OR REPLACE INTO badges (
id,
category,
name,
descriptionTemplate
) VALUES (
$id,
$category,
$name,
$descriptionTemplate
);
`
);
const getImageFilesForBadge = prepare<Query>(
db,
'SELECT url, localPath FROM badgeImageFiles WHERE badgeId = $badgeId'
);
const insertBadgeImageFile = prepare<Query>(
db,
`
INSERT INTO badgeImageFiles (
badgeId,
'order',
url,
localPath,
theme
) VALUES (
$badgeId,
$order,
$url,
$localPath,
$theme
);
`
);
db.transaction(() => {
badges.forEach(badge => {
const { id: badgeId } = badge;
const oldLocalPaths = new Map<string, string>();
for (const { url, localPath } of getImageFilesForBadge.all({ badgeId })) {
if (localPath) {
oldLocalPaths.set(url, localPath);
}
}
insertBadge.run({
id: badgeId,
category: badge.category,
name: badge.name,
descriptionTemplate: badge.descriptionTemplate,
});
for (const [order, image] of badge.images.entries()) {
for (const [theme, imageFile] of Object.entries(image)) {
insertBadgeImageFile.run({
badgeId,
localPath:
imageFile.localPath || oldLocalPaths.get(imageFile.url) || null,
order,
theme,
url: imageFile.url,
});
}
}
});
})();
}
async function badgeImageFileDownloaded(
url: string,
localPath: string
): Promise<void> {
const db = getInstance();
prepare<Query>(
db,
'UPDATE badgeImageFiles SET localPath = $localPath WHERE url = $url'
).run({ url, localPath });
}
async function getAllBadgeImageFileLocalPaths(): Promise<Set<string>> {
const db = getInstance();
const localPaths = db
.prepare<EmptyQuery>(
'SELECT localPath FROM badgeImageFiles WHERE localPath IS NOT NULL'
)
.pluck()
.all();
return new Set(localPaths);
}
// All data in database
async function removeAll(): Promise<void> {
const db = getInstance();
db.transaction(() => {
db.exec(`
DELETE FROM badges;
DELETE FROM badgeImageFiles;
DELETE FROM conversations;
DELETE FROM identityKeys;
DELETE FROM items;

View file

@ -0,0 +1,43 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from 'better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export default function updateToSchemaVersion44(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 44) {
return;
}
db.transaction(() => {
db.exec(
`
CREATE TABLE badges(
id TEXT PRIMARY KEY,
category TEXT NOT NULL,
name TEXT NOT NULL,
descriptionTemplate TEXT NOT NULL
);
CREATE TABLE badgeImageFiles(
badgeId TEXT REFERENCES badges(id)
ON DELETE CASCADE
ON UPDATE CASCADE,
'order' INTEGER NOT NULL,
url TEXT NOT NULL,
localPath TEXT,
theme TEXT NOT NULL
);
`
);
db.pragma('user_version = 44');
})();
logger.info('updateToSchemaVersion44: success!');
}

View file

@ -19,6 +19,7 @@ import type { Query, EmptyQuery } from '../util';
import updateToSchemaVersion41 from './41-uuid-keys';
import updateToSchemaVersion42 from './42-stale-reactions';
import updateToSchemaVersion43 from './43-gv2-uuid';
import updateToSchemaVersion44 from './44-badges';
function updateToSchemaVersion1(
currentVersion: number,
@ -1901,6 +1902,7 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion41,
updateToSchemaVersion42,
updateToSchemaVersion43,
updateToSchemaVersion44,
];
export function updateSchema(db: Database, logger: LoggerType): void {