feat,refactor,chore(MoreStickers) (#82)

* feat: sticker pack export
refactor: rename sticker pack ids
chore: add link for MoreStickersConverter

WARNING: This is a destructive upgrade. Sticker packs must be re-added after this upgrade.

* feat(MoreStickers): add migrate button
This commit is contained in:
leko 2024-10-28 02:05:23 +08:00 committed by GitHub
parent a019187f19
commit af40b89e65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 201 additions and 21 deletions

View file

@ -11,7 +11,8 @@ import { Button, Forms, React, TabBar, Text, TextArea, Toasts } from "@webpack/c
import { convert as convertLineEP, getIdFromUrl as getLineEmojiPackIdFromUrl, getStickerPackById as getLineEmojiPackById, isLineEmojiPackHtml, parseHtml as getLineEPFromHtml } from "../lineEmojis"; import { convert as convertLineEP, getIdFromUrl as getLineEmojiPackIdFromUrl, getStickerPackById as getLineEmojiPackById, isLineEmojiPackHtml, parseHtml as getLineEPFromHtml } from "../lineEmojis";
import { convert as convertLineSP, getIdFromUrl as getLineStickerPackIdFromUrl, getStickerPackById as getLineStickerPackById, isLineStickerPackHtml, parseHtml as getLineSPFromHtml } from "../lineStickers"; import { convert as convertLineSP, getIdFromUrl as getLineStickerPackIdFromUrl, getStickerPackById as getLineStickerPackById, isLineStickerPackHtml, parseHtml as getLineSPFromHtml } from "../lineStickers";
import { deleteStickerPack, getStickerPackMetas, saveStickerPack } from "../stickers"; import { migrate } from "../migrate-v1";
import { deleteStickerPack, getStickerPack, getStickerPackMetas, saveStickerPack } from "../stickers";
import { SettingsTabsKey, Sticker, StickerPack, StickerPackMeta } from "../types"; import { SettingsTabsKey, Sticker, StickerPack, StickerPackMeta } from "../types";
import { cl, clPicker, Mutex } from "../utils"; import { cl, clPicker, Mutex } from "../utils";
@ -21,7 +22,7 @@ const mutex = new Mutex();
export const RECENT_STICKERS_ID = "recent"; export const RECENT_STICKERS_ID = "recent";
export const RECENT_STICKERS_TITLE = "Recently Used"; export const RECENT_STICKERS_TITLE = "Recently Used";
const KEY = "Vencord-MoreStickers-RecentStickers"; const KEY = "MoreStickers:RecentStickers";
const noDrag = { const noDrag = {
onMouseDown: e => { e.preventDefault(); return false; }, onMouseDown: e => { e.preventDefault(); return false; },
@ -122,8 +123,8 @@ export const Settings = () => {
<Forms.FormTitle tag="h5">Add Sticker Pack from URL</Forms.FormTitle> <Forms.FormTitle tag="h5">Add Sticker Pack from URL</Forms.FormTitle>
<Forms.FormText> <Forms.FormText>
<p> <p>
Currently LINE stickers supported only. <br /> Currently LINE stickers/emojis supported only. <br />
Telegram stickers support is planned, but due to the lack of a public API, it is most likely to be provided by sticker pack files instead of adding by URL. Get Telegram stickers with <a href="https://github.com/lekoOwO/MoreStickersConverter">MoreStickersConverter</a>.
</p> </p>
</Forms.FormText> </Forms.FormText>
<Flex flexDirection="row" style={{ <Flex flexDirection="row" style={{
@ -357,6 +358,51 @@ export const Settings = () => {
</Button> </Button>
</div> </div>
} }
{
tab === SettingsTabsKey.MISC &&
<div className="section">
<Forms.FormTitle tag="h5">Misc tools</Forms.FormTitle>
<Flex flexDirection="row" style={{
alignItems: "center",
justifyContent: "center"
}} >
<Button
size={Button.Sizes.SMALL}
onClick={async e => {
const result: StickerPack[] = [];
const stickerPacks = await getStickerPackMetas();
for (const stickerPack of stickerPacks) {
const sp = await getStickerPack(stickerPack.id);
if (sp) {
result.push(sp);
}
}
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([JSON.stringify(result)], { type: "application/json" }));
a.download = "MoreStickers.stickerpacks";
a.click();
Toasts.show({
message: "Sticker Packs exported",
type: Toasts.Type.SUCCESS,
id: Toasts.genId(),
options: {
duration: 1000
}
});
}}
>Export Sticker Packs</Button>
<Button
size={Button.Sizes.SMALL}
onClick={async e => {
await migrate();
}}
>Migrate from v1</Button>
</Flex>
</div>
}
<Forms.FormDivider style={{ <Forms.FormDivider style={{
marginTop: "8px", marginTop: "8px",
marginBottom: "8px" marginBottom: "8px"
@ -409,14 +455,14 @@ export function Wrapper(props: { children: JSX.Element | JSX.Element[]; }) {
); );
} }
export async function getRecentStickers(): Promise<Sticker[]> { export async function getRecentStickers(key: string = KEY): Promise<Sticker[]> {
return (await DataStore.get(KEY)) ?? []; return (await DataStore.get(key)) ?? [];
} }
export async function setRecentStickers(stickers: Sticker[]): Promise<void> { export async function setRecentStickers(stickers: Sticker[], key: string = KEY): Promise<void> {
const unlock = await mutex.lock(); const unlock = await mutex.lock();
try { try {
await DataStore.set(KEY, stickers); await DataStore.set(key, stickers);
} finally { } finally {
unlock(); unlock();
} }

View file

@ -42,7 +42,7 @@ export function getIdFromUrl(url: string): string {
*/ */
function toStickerPackId(id: string): string { function toStickerPackId(id: string): string {
return "Vencord-MoreStickers-Line-Emoji-Pack-" + id; return "MoreStickers:Line:Emoji-Pack:" + id;
} }
/** /**
@ -54,7 +54,7 @@ function toStickerPackId(id: string): string {
*/ */
function toStickerId(stickerId: string, lineEmojiPackId: string): string { function toStickerId(stickerId: string, lineEmojiPackId: string): string {
return "Vencord-MoreStickers-Line-Emoji" + lineEmojiPackId + "-" + stickerId; return "MoreStickers:Line-Emoji:" + lineEmojiPackId + ":" + stickerId;
} }
/** /**

View file

@ -42,7 +42,7 @@ export function getIdFromUrl(url: string): string {
*/ */
function toStickerPackId(id: string): string { function toStickerPackId(id: string): string {
return "Vencord-MoreStickers-Line-Pack-" + id; return "MoreStickers:Line:Pack:" + id;
} }
/** /**
@ -54,7 +54,7 @@ function toStickerPackId(id: string): string {
*/ */
function toStickerId(stickerId: string, lineStickerPackId: string): string { function toStickerId(stickerId: string, lineStickerPackId: string): string {
return "Vencord-MoreStickers-Line-Sticker" + lineStickerPackId + "-" + stickerId; return "MoreStickers:Line:Sticker:" + lineStickerPackId + ":" + stickerId;
} }
/** /**

View file

@ -0,0 +1,133 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import { Toasts } from "@webpack/common";
import { getRecentStickers, setRecentStickers } from "./components/misc";
import { deleteStickerPack, getStickerPack, getStickerPackMetas, saveStickerPack } from "./stickers";
import { Sticker, StickerPack } from "./types";
const PACKS_KEY = "MoreStickers:Packs";
const PACKS_KEY_OLD = "Vencord-MoreStickers-Packs";
const RECENT_STICKERS_KEY = "MoreStickers:RecentStickers";
const RECENT_STICKERS_KEY_OLD = "Vencord-MoreStickers-RecentStickers";
function migrateStickerPackId(oldStickerPackId: string): string {
if (oldStickerPackId.startsWith("Vencord-MoreStickers-Line-Pack")) {
const id = oldStickerPackId.replace("Vencord-MoreStickers-Line-Pack-", "");
return "MoreStickers:Line:Pack:" + id;
} else if (oldStickerPackId.startsWith("Vencord-MoreStickers-Line-Emoji-Pack")) {
const id = oldStickerPackId.replace("Vencord-MoreStickers-Line-Emoji-Pack-", "");
return "MoreStickers:Line:Emoji:Pack:" + id;
} else {
return oldStickerPackId;
}
}
function migrateStickerId(oldStickerId: string): string {
if (oldStickerId.startsWith("Vencord-MoreStickers-Line-Sticker")) {
const [stickerPackId, stickerId] = oldStickerId.replace("Vencord-MoreStickers-Line-Sticker", "").split("-", 2);
return "MoreStickers:Line:Sticker:" + stickerPackId + ":" + stickerId;
} else if (oldStickerId.startsWith("Vencord-MoreStickers-Line-Emoji")) {
const [stickerPackId, stickerId] = oldStickerId.replace("Vencord-MoreStickers-Line-Emoji", "").split("-", 2);
return "MoreStickers:Line:Emoji:" + stickerPackId + ":" + stickerId;
} else {
return oldStickerId;
}
}
function migrateSticker(oldSticker: Sticker): Sticker {
return {
...oldSticker,
id: migrateStickerId(oldSticker.id),
};
}
function migrateStickerPack(oldStickerPack: StickerPack): StickerPack {
return {
...oldStickerPack,
id: migrateStickerPackId(oldStickerPack.id),
logo: migrateSticker(oldStickerPack.logo),
stickers: oldStickerPack.stickers.map(migrateSticker),
};
}
export async function migrate() {
const newPackMetas = await getStickerPackMetas(PACKS_KEY);
if (newPackMetas.length > 0) {
Toasts.show({
message: "New sticker packs already exist, migration not needed",
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {
duration: 1000
}
});
return;
}
let oldPackMetas = await getStickerPackMetas(PACKS_KEY_OLD);
if (oldPackMetas.length === 0) {
Toasts.show({
message: "Old sticker packs not found, nothing to migrate",
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {
duration: 1000
}
});
return;
}
for (const oldStickerPackMeta of oldPackMetas) {
try {
const oldStickerPack = await getStickerPack(oldStickerPackMeta.id);
if (oldStickerPack === null) continue;
const newStickerPack = migrateStickerPack(oldStickerPack);
try {
await saveStickerPack(newStickerPack, PACKS_KEY);
await deleteStickerPack(oldStickerPackMeta.id, PACKS_KEY_OLD);
} catch (e) {
await deleteStickerPack(newStickerPack.id, PACKS_KEY);
}
} catch (e) {
console.error(e);
Toasts.show({
message: `Migration failed: ${oldStickerPackMeta.title} (${oldStickerPackMeta.id})`,
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {
duration: 1000
}
});
}
}
oldPackMetas = await getStickerPackMetas(PACKS_KEY_OLD);
if (oldPackMetas.length === 0) {
await DataStore.del(PACKS_KEY_OLD);
}
const oldRecentStickers = await getRecentStickers(RECENT_STICKERS_KEY_OLD);
if (oldRecentStickers.length > 0) {
const newRecentStickers = oldRecentStickers.map(migrateSticker);
await setRecentStickers(newRecentStickers, RECENT_STICKERS_KEY);
await DataStore.del(RECENT_STICKERS_KEY_OLD);
}
console.log("Migration complete");
Toasts.show({
message: "Sticker Pack Migration Complete",
type: Toasts.Type.SUCCESS,
id: Toasts.genId(),
options: {
duration: 1000
}
});
}

View file

@ -11,7 +11,7 @@ import { StickerPack, StickerPackMeta } from "./types";
import { Mutex } from "./utils"; import { Mutex } from "./utils";
const mutex = new Mutex(); const mutex = new Mutex();
const PACKS_KEY = "Vencord-MoreStickers-Packs"; const PACKS_KEY = "MoreStickers:Packs";
/** /**
* Convert StickerPack to StickerPackMeta * Convert StickerPack to StickerPackMeta
@ -34,7 +34,7 @@ function stickerPackToMeta(sp: StickerPack): StickerPackMeta {
* @param {StickerPack} sp The StickerPack to save. * @param {StickerPack} sp The StickerPack to save.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export async function saveStickerPack(sp: StickerPack): Promise<void> { export async function saveStickerPack(sp: StickerPack, packsKey: string = PACKS_KEY): Promise<void> {
const meta = stickerPackToMeta(sp); const meta = stickerPackToMeta(sp);
await Promise.all([ await Promise.all([
@ -43,8 +43,8 @@ export async function saveStickerPack(sp: StickerPack): Promise<void> {
const unlock = await mutex.lock(); const unlock = await mutex.lock();
try { try {
const packs = (await DataStore.get(PACKS_KEY) ?? null) as (StickerPackMeta[] | null); const packs = (await DataStore.get(packsKey) ?? null) as (StickerPackMeta[] | null);
await DataStore.set(PACKS_KEY, packs === null ? [meta] : [...packs, meta]); await DataStore.set(packsKey, packs === null ? [meta] : [...packs, meta]);
} finally { } finally {
unlock(); unlock();
} }
@ -57,8 +57,8 @@ export async function saveStickerPack(sp: StickerPack): Promise<void> {
* *
* @return {Promise<StickerPackMeta[]>} * @return {Promise<StickerPackMeta[]>}
*/ */
export async function getStickerPackMetas(): Promise<StickerPackMeta[]> { export async function getStickerPackMetas(packsKey: string | undefined = PACKS_KEY): Promise<StickerPackMeta[]> {
const packs = (await DataStore.get(PACKS_KEY)) ?? null as (StickerPackMeta[] | null); const packs = (await DataStore.get(packsKey)) ?? null as (StickerPackMeta[] | null);
return packs ?? []; return packs ?? [];
} }
@ -89,7 +89,7 @@ export async function getStickerPackMeta(id: string): Promise<StickerPackMeta |
* @param {string} id The id of the sticker pack. * @param {string} id The id of the sticker pack.
* @return {Promise<void>} * @return {Promise<void>}
* */ * */
export async function deleteStickerPack(id: string): Promise<void> { export async function deleteStickerPack(id: string, packsKey: string = PACKS_KEY): Promise<void> {
await Promise.all([ await Promise.all([
DataStore.del(id), DataStore.del(id),
removeRecentStickerByPackId(id), removeRecentStickerByPackId(id),
@ -97,9 +97,9 @@ export async function deleteStickerPack(id: string): Promise<void> {
const unlock = await mutex.lock(); const unlock = await mutex.lock();
try { try {
const packs = (await DataStore.get(PACKS_KEY) ?? null) as (StickerPackMeta[] | null); const packs = (await DataStore.get(packsKey) ?? null) as (StickerPackMeta[] | null);
if (packs === null) return; if (packs === null) return;
await DataStore.set(PACKS_KEY, packs.filter(p => p.id !== id)); await DataStore.set(packsKey, packs.filter(p => p.id !== id));
} finally { } finally {
unlock(); unlock();
} }

View file

@ -100,6 +100,7 @@ export enum SettingsTabsKey {
ADD_STICKER_PACK_URL = "Add from URL", ADD_STICKER_PACK_URL = "Add from URL",
ADD_STICKER_PACK_HTML = "Add from HTML", ADD_STICKER_PACK_HTML = "Add from HTML",
ADD_STICKER_PACK_FILE = "Add from File", ADD_STICKER_PACK_FILE = "Add from File",
MISC = "Misc",
} }
export interface SidebarProps { export interface SidebarProps {