| 1 | import type { ImagePack, QuickEntry } from './types'; |
| 2 | |
| 3 | export type DisabledPackImages = Record<string, string[]>; |
| 4 | |
| 5 | function isRecord(value: unknown): value is Record<string, unknown> { |
| 6 | return typeof value === 'object' && value !== null && !Array.isArray(value); |
| 7 | } |
| 8 | |
| 9 | function setOwn<T>(record: Record<string, T>, key: string, value: T): void { |
| 10 | Object.defineProperty(record, key, { |
| 11 | configurable: true, |
| 12 | enumerable: true, |
| 13 | value, |
| 14 | writable: true |
| 15 | }); |
| 16 | } |
| 17 | |
| 18 | function hasOwn(record: object, key: string): boolean { |
| 19 | return Object.prototype.hasOwnProperty.call(record, key); |
| 20 | } |
| 21 | |
| 22 | export function parseDisabledPackImages(raw: unknown): DisabledPackImages { |
| 23 | if (!isRecord(raw) || !isRecord(raw['disabled'])) return {}; |
| 24 | const disabled: DisabledPackImages = {}; |
| 25 | for (const [packId, rawShortcodes] of Object.entries(raw['disabled'])) { |
| 26 | if (!Array.isArray(rawShortcodes)) continue; |
| 27 | const seen = new Set<string>(); |
| 28 | const shortcodes = rawShortcodes.filter((value): value is string => { |
| 29 | if (typeof value !== 'string' || value.length === 0 || seen.has(value)) return false; |
| 30 | seen.add(value); |
| 31 | return true; |
| 32 | }); |
| 33 | if (shortcodes.length > 0) setOwn(disabled, packId, shortcodes); |
| 34 | } |
| 35 | return disabled; |
| 36 | } |
| 37 | |
| 38 | export function imageEnabled(disabled: DisabledPackImages, packId: string, shortcode: string): boolean { |
| 39 | return !hasOwn(disabled, packId) || !disabled[packId].includes(shortcode); |
| 40 | } |
| 41 | |
| 42 | export function withImageEnabled( |
| 43 | disabled: DisabledPackImages, |
| 44 | packId: string, |
| 45 | shortcode: string, |
| 46 | enabled: boolean |
| 47 | ): DisabledPackImages { |
| 48 | const current = hasOwn(disabled, packId) ? disabled[packId] : []; |
| 49 | const nextShortcodes = enabled |
| 50 | ? current.filter((item) => item !== shortcode) |
| 51 | : current.includes(shortcode) ? [...current] : [...current, shortcode]; |
| 52 | if (nextShortcodes.length === current.length && nextShortcodes.every((item, index) => item === current[index])) { |
| 53 | return disabled; |
| 54 | } |
| 55 | const next = { ...disabled }; |
| 56 | if (nextShortcodes.length === 0) delete next[packId]; |
| 57 | else setOwn(next, packId, nextShortcodes); |
| 58 | return next; |
| 59 | } |
| 60 | |
| 61 | export function enabledPacks(packs: ImagePack[], disabled: DisabledPackImages): ImagePack[] { |
| 62 | return packs.map((pack) => ({ |
| 63 | ...pack, |
| 64 | images: pack.images.filter((image) => imageEnabled(disabled, pack.id, image.shortcode)) |
| 65 | })); |
| 66 | } |
| 67 | |
| 68 | export function enabledQuickReactions( |
| 69 | quick: QuickEntry[], |
| 70 | packs: ImagePack[], |
| 71 | disabled: DisabledPackImages |
| 72 | ): QuickEntry[] { |
| 73 | const hidden = new Set<string>(); |
| 74 | const enabled = new Set<string>(); |
| 75 | for (const pack of packs) { |
| 76 | for (const image of pack.images) { |
| 77 | (imageEnabled(disabled, pack.id, image.shortcode) ? enabled : hidden).add(image.reaction_key); |
| 78 | } |
| 79 | } |
| 80 | return quick.filter((entry) => 'separator' in entry || !hidden.has(entry.key) || enabled.has(entry.key)); |
| 81 | } |