mirror of
https://github.com/vector-im/element-web.git
synced 2026-04-18 03:52:39 +02:00
Add support for m.recent_emoji account data event
Maintains read-compatibility with `io.element.recent_emoji`
This commit is contained in:
parent
7537d33817
commit
f7a3977f19
3
apps/web/src/@types/matrix-js-sdk.d.ts
vendored
3
apps/web/src/@types/matrix-js-sdk.d.ts
vendored
@ -16,6 +16,7 @@ import type { DeviceClientInformation } from "../utils/device/types.ts";
|
||||
import type { UserWidget } from "../utils/WidgetUtils-types.ts";
|
||||
import { type MediaPreviewConfig } from "./media_preview.ts";
|
||||
import { type INVITE_RULES_ACCOUNT_DATA_TYPE, type InviteConfigAccountData } from "./invite-rules.ts";
|
||||
import { type LegacyRecentEmojiData } from "../emojipicker/recent.ts";
|
||||
|
||||
// Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types
|
||||
declare module "matrix-js-sdk/src/types" {
|
||||
@ -71,7 +72,7 @@ declare module "matrix-js-sdk/src/types" {
|
||||
[key: `io.element.matrix_client_information.${string}`]: DeviceClientInformation;
|
||||
// Element settings account data events
|
||||
"im.vector.setting.breadcrumbs": { recent_rooms: string[] };
|
||||
"io.element.recent_emoji": { recent_emoji: string[] };
|
||||
"io.element.recent_emoji": { recent_emoji: LegacyRecentEmojiData };
|
||||
"im.vector.setting.integration_provisioning": { enabled: boolean };
|
||||
"im.vector.riot.breadcrumb_rooms": { rooms: string[] };
|
||||
"im.vector.web.settings": Record<string, any>;
|
||||
|
||||
@ -8,16 +8,13 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { orderBy } from "lodash";
|
||||
import { type AccountDataEvents } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
|
||||
interface ILegacyFormat {
|
||||
[emoji: string]: [number, number]; // [count, date]
|
||||
}
|
||||
|
||||
// New format tries to be more space efficient for synchronization. Ordered by Date descending.
|
||||
export type RecentEmojiData = [emoji: string, count: number][];
|
||||
export type RecentEmojiData = AccountDataEvents["m.recent_emoji"]["recent_emoji"];
|
||||
export type LegacyRecentEmojiData = [emoji: string, count: number][];
|
||||
|
||||
const SETTING_NAME = "recent_emoji";
|
||||
|
||||
@ -25,43 +22,57 @@ const SETTING_NAME = "recent_emoji";
|
||||
// even if you haven't used your typically favourite emoji for a little while.
|
||||
const STORAGE_LIMIT = 100;
|
||||
|
||||
// TODO remove this after some time
|
||||
function migrate(): void {
|
||||
const data: ILegacyFormat = JSON.parse(window.localStorage.mx_reaction_count || "{}");
|
||||
const sorted = Object.entries(data).sort(([, [count1, date1]], [, [count2, date2]]) => date2 - date1);
|
||||
const newFormat = sorted.map(([emoji, [count, date]]) => [emoji, count]);
|
||||
SettingsStore.setValue(SETTING_NAME, null, SettingLevel.ACCOUNT, newFormat.slice(0, STORAGE_LIMIT));
|
||||
}
|
||||
|
||||
function getRecentEmoji(): RecentEmojiData {
|
||||
return SettingsStore.getValue(SETTING_NAME) || [];
|
||||
}
|
||||
|
||||
export function translateLegacyEmojiData(legacyData: LegacyRecentEmojiData): RecentEmojiData {
|
||||
return legacyData.map(([emoji, total]) => ({
|
||||
emoji,
|
||||
total,
|
||||
}));
|
||||
}
|
||||
|
||||
export function mergeEmojiData(data1: RecentEmojiData, data2?: RecentEmojiData): RecentEmojiData {
|
||||
if (!data2) return data1;
|
||||
|
||||
return Object.values(
|
||||
[...data1, ...data2].reduce(
|
||||
(acc, item) => {
|
||||
const existing = acc[item.emoji];
|
||||
|
||||
// If it doesn't exist or the current total is higher, update it
|
||||
if (!existing || item.total > existing.total) {
|
||||
acc[item.emoji] = item;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, RecentEmojiData[number]>,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function add(emoji: string): void {
|
||||
const recents = getRecentEmoji();
|
||||
const i = recents.findIndex(([e]) => e === emoji);
|
||||
const i = recents.findIndex((entry) => entry.emoji === emoji);
|
||||
|
||||
let newEntry;
|
||||
let newEntry: RecentEmojiData[number];
|
||||
if (i >= 0) {
|
||||
// first remove the existing tuple so that we can increment it and push it to the front
|
||||
[newEntry] = recents.splice(i, 1);
|
||||
newEntry[1]++; // increment the usage count
|
||||
newEntry.total++; // increment the usage count
|
||||
} else {
|
||||
newEntry = [emoji, 1];
|
||||
newEntry = { emoji, total: 1 };
|
||||
}
|
||||
|
||||
SettingsStore.setValue(SETTING_NAME, null, SettingLevel.ACCOUNT, [newEntry, ...recents].slice(0, STORAGE_LIMIT));
|
||||
}
|
||||
|
||||
export function get(limit = 24): string[] {
|
||||
let recents = getRecentEmoji();
|
||||
|
||||
if (recents.length < 1) {
|
||||
migrate();
|
||||
recents = getRecentEmoji();
|
||||
}
|
||||
const recents = getRecentEmoji();
|
||||
|
||||
// perform a stable sort on `count` to keep the recent (date) order as a secondary sort factor
|
||||
const sorted = orderBy(recents, "1", "desc");
|
||||
return sorted.slice(0, limit).map(([emoji]) => emoji);
|
||||
return sorted.slice(0, limit).map(({ emoji }) => emoji);
|
||||
}
|
||||
|
||||
@ -16,11 +16,13 @@ import { SettingLevel } from "../SettingLevel";
|
||||
import { type WatchManager } from "../WatchManager";
|
||||
import { MEDIA_PREVIEW_ACCOUNT_DATA_TYPE } from "../../@types/media_preview";
|
||||
import { type SettingKey, type Settings } from "../Settings.tsx";
|
||||
import { mergeEmojiData, type RecentEmojiData, translateLegacyEmojiData } from "../../emojipicker/recent.ts";
|
||||
|
||||
const BREADCRUMBS_LEGACY_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms";
|
||||
const BREADCRUMBS_EVENT_TYPE = "im.vector.setting.breadcrumbs";
|
||||
const BREADCRUMBS_EVENT_TYPES = [BREADCRUMBS_LEGACY_EVENT_TYPE, BREADCRUMBS_EVENT_TYPE];
|
||||
const RECENT_EMOJI_EVENT_TYPE = "io.element.recent_emoji";
|
||||
const LEGACY_RECENT_EMOJI_EVENT_TYPE = "io.element.recent_emoji";
|
||||
const RECENT_EMOJI_EVENT_TYPE = "m.recent_emoji";
|
||||
const INTEG_PROVISIONING_EVENT_TYPE = "im.vector.setting.integration_provisioning";
|
||||
const ANALYTICS_EVENT_TYPE = "im.vector.analytics";
|
||||
const DEFAULT_SETTINGS_EVENT_TYPE = "im.vector.web.settings";
|
||||
@ -66,8 +68,8 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
||||
} else if (event.getType() === INTEG_PROVISIONING_EVENT_TYPE) {
|
||||
const val = event.getContent()["enabled"];
|
||||
this.watchers.notifyUpdate("integrationProvisioning", null, SettingLevel.ACCOUNT, val);
|
||||
} else if (event.getType() === RECENT_EMOJI_EVENT_TYPE) {
|
||||
const val = event.getContent()["enabled"];
|
||||
} else if (event.getType() === RECENT_EMOJI_EVENT_TYPE || event.getType() === LEGACY_RECENT_EMOJI_EVENT_TYPE) {
|
||||
const val = this.getRecentEmoji();
|
||||
this.watchers.notifyUpdate("recent_emoji", null, SettingLevel.ACCOUNT, val);
|
||||
} else if (event.getType() === MEDIA_PREVIEW_ACCOUNT_DATA_TYPE) {
|
||||
this.watchers.notifyUpdate("mediaPreviewConfig", null, SettingLevel.ROOM_ACCOUNT, event.getContent());
|
||||
@ -102,8 +104,8 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
||||
|
||||
// Special case recent emoji
|
||||
if (settingName === "recent_emoji") {
|
||||
const content = this.getSettings(RECENT_EMOJI_EVENT_TYPE);
|
||||
return content ? content["recent_emoji"] : null;
|
||||
const val = this.getRecentEmoji();
|
||||
return val ?? null;
|
||||
}
|
||||
|
||||
// Special case integration manager provisioning
|
||||
@ -265,4 +267,15 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
||||
}
|
||||
this.watchers.notifyUpdate("breadcrumb_rooms", null, SettingLevel.ACCOUNT, val || []);
|
||||
}
|
||||
|
||||
private getRecentEmoji(): RecentEmojiData {
|
||||
let val = this.getSettings(RECENT_EMOJI_EVENT_TYPE)?.recent_emoji || [];
|
||||
|
||||
const legacyVal = this.getSettings(LEGACY_RECENT_EMOJI_EVENT_TYPE)?.recent_emoji;
|
||||
if (legacyVal) {
|
||||
val = mergeEmojiData(val, translateLegacyEmojiData(legacyVal));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user