From 7a1e6394831fb07e303c5ed0900dfe1ea4820de5 Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Wed, 24 Apr 2024 12:12:47 +0200
Subject: [chore] Refactor settings panel routing (and other fixes) (#2864)
---
.../settings/views/admin/emoji/category-select.tsx | 134 ++++++++++++
.../settings/views/admin/emoji/local/detail.tsx | 142 +++++++++++++
.../settings/views/admin/emoji/local/new-emoji.tsx | 112 ++++++++++
.../settings/views/admin/emoji/local/overview.tsx | 173 +++++++++++++++
.../views/admin/emoji/local/use-shortcode.ts | 56 +++++
.../settings/views/admin/emoji/remote/index.tsx | 46 ++++
.../views/admin/emoji/remote/steal-this-look.tsx | 235 +++++++++++++++++++++
7 files changed, 898 insertions(+)
create mode 100644 web/source/settings/views/admin/emoji/category-select.tsx
create mode 100644 web/source/settings/views/admin/emoji/local/detail.tsx
create mode 100644 web/source/settings/views/admin/emoji/local/new-emoji.tsx
create mode 100644 web/source/settings/views/admin/emoji/local/overview.tsx
create mode 100644 web/source/settings/views/admin/emoji/local/use-shortcode.ts
create mode 100644 web/source/settings/views/admin/emoji/remote/index.tsx
create mode 100644 web/source/settings/views/admin/emoji/remote/steal-this-look.tsx
(limited to 'web/source/settings/views/admin/emoji')
diff --git a/web/source/settings/views/admin/emoji/category-select.tsx b/web/source/settings/views/admin/emoji/category-select.tsx
new file mode 100644
index 000000000..683e146d8
--- /dev/null
+++ b/web/source/settings/views/admin/emoji/category-select.tsx
@@ -0,0 +1,134 @@
+/*
+ GoToSocial
+ Copyright (C) GoToSocial Authors admin@gotosocial.org
+ SPDX-License-Identifier: AGPL-3.0-or-later
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+import React, { useMemo, useEffect, PropsWithChildren, ReactElement } from "react";
+import { matchSorter } from "match-sorter";
+import ComboBox from "../../../components/combo-box";
+import { useListEmojiQuery } from "../../../lib/query/admin/custom-emoji";
+import { CustomEmoji } from "../../../lib/types/custom-emoji";
+import { ComboboxFormInputHook } from "../../../lib/form/types";
+import Loading from "../../../components/loading";
+import { Error } from "../../../components/error";
+
+/**
+ * Sort all emoji into a map keyed by
+ * the category names (or "Unsorted").
+ */
+export function useEmojiByCategory(emojis: CustomEmoji[]) {
+ return useMemo(() => {
+ const byCategory = new Map();
+
+ emojis.forEach((emoji) => {
+ const key = emoji.category ?? "Unsorted";
+ const value = byCategory.get(key) ?? [];
+ value.push(emoji);
+ byCategory.set(key, value);
+ });
+
+ return byCategory;
+ }, [emojis]);
+}
+
+interface CategorySelectProps {
+ field: ComboboxFormInputHook;
+}
+
+/**
+ *
+ * Renders a cute lil searchable "category select" dropdown.
+ */
+export function CategorySelect({ field, children }: PropsWithChildren) {
+ // Get all local emojis.
+ const {
+ data: emoji = [],
+ isLoading,
+ isSuccess,
+ isError,
+ error,
+ } = useListEmojiQuery({ filter: "domain:local" });
+
+ const emojiByCategory = useEmojiByCategory(emoji);
+ const categories = useMemo(() => new Set(emojiByCategory.keys()), [emojiByCategory]);
+ const { value, setIsNew } = field;
+
+ // Data used by the ComboBox element
+ // to select an emoji category.
+ const categoryItems = useMemo(() => {
+ const categoriesArr = Array.from(categories);
+
+ // Sorted by complex algorithm.
+ const categoryNames = matchSorter(
+ categoriesArr,
+ value ?? "",
+ { threshold: matchSorter.rankings.NO_MATCH },
+ );
+
+ // Map each category to the static image
+ // of the first emoji it contains.
+ const categoryItems: [string, ReactElement][] = [];
+ categoryNames.forEach((categoryName) => {
+ let src: string | undefined;
+ const items = emojiByCategory.get(categoryName);
+ if (items && items.length > 0) {
+ src = items[0].static_url;
+ }
+
+ categoryItems.push([
+ categoryName,
+ <>
+
+ {categoryName}
+ >
+ ]);
+ });
+
+ return categoryItems;
+ }, [emojiByCategory, categories, value]);
+
+ // New category if something has been entered
+ // and we don't have it in categories yet.
+ useEffect(() => {
+ if (value !== undefined) {
+ const trimmed = value.trim();
+ if (trimmed.length > 0) {
+ setIsNew(!categories.has(trimmed));
+ }
+ }
+ }, [categories, value, isSuccess, setIsNew]);
+
+ if (isLoading) {
+ return ;
+ } else if (isError) {
+ return ;
+ } else {
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/web/source/settings/views/admin/emoji/local/detail.tsx b/web/source/settings/views/admin/emoji/local/detail.tsx
new file mode 100644
index 000000000..2913b6c17
--- /dev/null
+++ b/web/source/settings/views/admin/emoji/local/detail.tsx
@@ -0,0 +1,142 @@
+/*
+ GoToSocial
+ Copyright (C) GoToSocial Authors admin@gotosocial.org
+ SPDX-License-Identifier: AGPL-3.0-or-later
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+import React, { useEffect } from "react";
+import { Redirect, useParams } from "wouter";
+import { useComboBoxInput, useFileInput, useValue } from "../../../../lib/form";
+import useFormSubmit from "../../../../lib/form/submit";
+import { useBaseUrl } from "../../../../lib/navigation/util";
+import FakeToot from "../../../../components/fake-toot";
+import FormWithData from "../../../../lib/form/form-with-data";
+import Loading from "../../../../components/loading";
+import { FileInput } from "../../../../components/form/inputs";
+import MutationButton from "../../../../components/form/mutation-button";
+import { Error } from "../../../../components/error";
+import { useGetEmojiQuery, useEditEmojiMutation, useDeleteEmojiMutation } from "../../../../lib/query/admin/custom-emoji";
+import { CategorySelect } from "../category-select";
+import BackButton from "../../../../components/back-button";
+
+export default function EmojiDetail() {
+ const baseUrl = useBaseUrl();
+ const params = useParams();
+ return (
+