From 4b8d7bd952dd97091d1baddeab10213e9c38cef3 Mon Sep 17 00:00:00 2001 From: f0x52 Date: Sun, 11 Dec 2022 16:00:23 +0100 Subject: [frogend] Emoji copy "Steal this look" (#1222) * split emoji into local and remote, allow looking up remote emoji by toot url * optimize some/all filtering * fix local emoji routes * implement copy action * shortcode validation, don't wipe form on error * copy & disable PATCH * remove local toot acceptance for testing * unused import * parse emoji from account and status, get web_url from status uri * fix url parse * submit button loading info * actually send category * code cleanup, distinguish between account and status responses * use loading icons * fix loading icon on federation page * require Loading element * remove unused require * query explanation, small accessibility tweaks --- web/source/css/base.css | 10 + web/source/settings/admin/emoji/detail.js | 168 ----------- web/source/settings/admin/emoji/index.js | 40 --- web/source/settings/admin/emoji/local/detail.js | 173 +++++++++++ web/source/settings/admin/emoji/local/index.js | 40 +++ web/source/settings/admin/emoji/local/new-emoji.js | 168 +++++++++++ web/source/settings/admin/emoji/local/overview.js | 90 ++++++ web/source/settings/admin/emoji/new-emoji.js | 147 ---------- web/source/settings/admin/emoji/overview.js | 89 ------ web/source/settings/admin/emoji/remote/index.js | 54 ++++ .../settings/admin/emoji/remote/parse-from-toot.js | 319 +++++++++++++++++++++ web/source/settings/admin/federation.js | 7 +- web/source/settings/components/form/text.jsx | 16 +- web/source/settings/components/loading.jsx | 27 ++ web/source/settings/index.js | 9 +- web/source/settings/lib/query/custom-emoji.js | 97 +++++++ web/source/settings/style.css | 48 ++++ 17 files changed, 1046 insertions(+), 456 deletions(-) delete mode 100644 web/source/settings/admin/emoji/detail.js delete mode 100644 web/source/settings/admin/emoji/index.js create mode 100644 web/source/settings/admin/emoji/local/detail.js create mode 100644 web/source/settings/admin/emoji/local/index.js create mode 100644 web/source/settings/admin/emoji/local/new-emoji.js create mode 100644 web/source/settings/admin/emoji/local/overview.js delete mode 100644 web/source/settings/admin/emoji/new-emoji.js delete mode 100644 web/source/settings/admin/emoji/overview.js create mode 100644 web/source/settings/admin/emoji/remote/index.js create mode 100644 web/source/settings/admin/emoji/remote/parse-from-toot.js create mode 100644 web/source/settings/components/loading.jsx diff --git a/web/source/css/base.css b/web/source/css/base.css index d2fa95a3b..73b533733 100644 --- a/web/source/css/base.css +++ b/web/source/css/base.css @@ -394,3 +394,13 @@ footer { color: $gray1; } } + +label { + cursor: pointer; +} + +@media (prefers-reduced-motion) { + .fa-spin { + animation: none; + } +} \ No newline at end of file diff --git a/web/source/settings/admin/emoji/detail.js b/web/source/settings/admin/emoji/detail.js deleted file mode 100644 index 51e291448..000000000 --- a/web/source/settings/admin/emoji/detail.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - - 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 . -*/ - -"use strict"; - -const React = require("react"); - -const { useRoute, Link, Redirect } = require("wouter"); - -const { CategorySelect } = require("./category-select"); -const { useComboBoxInput, useFileInput } = require("../../components/form"); - -const query = require("../../lib/query"); -const FakeToot = require("../../components/fake-toot"); - -const base = "/settings/admin/custom-emoji"; - -module.exports = function EmojiDetailRoute() { - let [_match, params] = useRoute(`${base}/:emojiId`); - if (params?.emojiId == undefined) { - return ; - } else { - return ( -
- < go back - -
- ); - } -}; - -function EmojiDetailData({emojiId}) { - const {currentData: emoji, isLoading, error} = query.useGetEmojiQuery(emojiId); - - if (error) { - return ( -
- {error.status}: {error.data.error} -
- ); - } else if (isLoading) { - return "Loading..."; - } else { - return ; - } -} - -function EmojiDetail({emoji}) { - const [modifyEmoji, modifyResult] = query.useEditEmojiMutation(); - - const [isNewCategory, setIsNewCategory] = React.useState(false); - - const [categoryState, _resetCategory, { category }] = useComboBoxInput("category", {defaultValue: emoji.category}); - - const [onFileChange, _resetFile, { image, imageURL, imageInfo }] = useFileInput("image", { - withPreview: true, - maxSize: 50 * 1024 - }); - - function modifyCategory() { - modifyEmoji({id: emoji.id, category: category.trim()}); - } - - function modifyImage() { - modifyEmoji({id: emoji.id, image: image}); - } - - React.useEffect(() => { - if (category != emoji.category && !categoryState.open && !isNewCategory && category.trim().length > 0) { - console.log("updating to", category); - modifyEmoji({id: emoji.id, category: category.trim()}); - } - }, [isNewCategory, category, categoryState.open, emoji.category, emoji.id, modifyEmoji]); - - return ( - <> -
- {emoji.shortcode} -
-

{emoji.shortcode}

- -
-
- -
-

Modify this emoji {modifyResult.isLoading && "(processing..)"}

- - {modifyResult.error &&
- {modifyResult.error.status}: {modifyResult.error.data.error} -
} - -
- - - -
- -
- Image -
- - {imageInfo} - -
- - - - - Look at this new custom emoji {emoji.shortcode} isn't it cool? - -
-
- - ); -} - -function DeleteButton({id}) { - // TODO: confirmation dialog? - const [deleteEmoji, deleteResult] = query.useDeleteEmojiMutation(); - - let text = "Delete"; - if (deleteResult.isLoading) { - text = "Deleting..."; - } - - if (deleteResult.isSuccess) { - return ; - } - - return ( - - ); -} \ No newline at end of file diff --git a/web/source/settings/admin/emoji/index.js b/web/source/settings/admin/emoji/index.js deleted file mode 100644 index 0fcda8264..000000000 --- a/web/source/settings/admin/emoji/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - - 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 . -*/ - -"use strict"; - -const React = require("react"); -const {Switch, Route} = require("wouter"); - -const EmojiOverview = require("./overview"); -const EmojiDetail = require("./detail"); - -const base = "/settings/admin/custom-emoji"; - -module.exports = function CustomEmoji() { - return ( - <> - - - - - - - - ); -}; diff --git a/web/source/settings/admin/emoji/local/detail.js b/web/source/settings/admin/emoji/local/detail.js new file mode 100644 index 000000000..179ee7c7c --- /dev/null +++ b/web/source/settings/admin/emoji/local/detail.js @@ -0,0 +1,173 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const React = require("react"); + +const { useRoute, Link, Redirect } = require("wouter"); + +const { CategorySelect } = require("../category-select"); +const { useComboBoxInput, useFileInput } = require("../../../components/form"); + +const query = require("../../../lib/query"); +const FakeToot = require("../../../components/fake-toot"); +const Loading = require("../../../components/loading"); + +const base = "/settings/custom-emoji/local"; + +module.exports = function EmojiDetailRoute() { + let [_match, params] = useRoute(`${base}/:emojiId`); + if (params?.emojiId == undefined) { + return ; + } else { + return ( +
+ < go back + +
+ ); + } +}; + +function EmojiDetailData({emojiId}) { + const {currentData: emoji, isLoading, error} = query.useGetEmojiQuery(emojiId); + + if (error) { + return ( +
+ {error.status}: {error.data.error} +
+ ); + } else if (isLoading) { + return ( +
+ +
+ ); + } else { + return ; + } +} + +function EmojiDetail({emoji}) { + const [modifyEmoji, modifyResult] = query.useEditEmojiMutation(); + + const [isNewCategory, setIsNewCategory] = React.useState(false); + + const [categoryState, _resetCategory, { category }] = useComboBoxInput("category", {defaultValue: emoji.category}); + + const [onFileChange, _resetFile, { image, imageURL, imageInfo }] = useFileInput("image", { + withPreview: true, + maxSize: 50 * 1024 + }); + + function modifyCategory() { + modifyEmoji({id: emoji.id, category: category.trim()}); + } + + function modifyImage() { + modifyEmoji({id: emoji.id, image: image}); + } + + React.useEffect(() => { + if (category != emoji.category && !categoryState.open && !isNewCategory && category.trim().length > 0) { + console.log("updating to", category); + modifyEmoji({id: emoji.id, category: category.trim()}); + } + }, [isNewCategory, category, categoryState.open, emoji.category, emoji.id, modifyEmoji]); + + return ( + <> +
+ {emoji.shortcode} +
+

{emoji.shortcode}

+ +
+
+ +
+

Modify this emoji {modifyResult.isLoading && "(processing..)"}

+ + {modifyResult.error &&
+ {modifyResult.error.status}: {modifyResult.error.data.error} +
} + +
+ + + +
+ +
+ Image +
+ + {imageInfo} + +
+ + + + + Look at this new custom emoji {emoji.shortcode} isn't it cool? + +
+
+ + ); +} + +function DeleteButton({id}) { + // TODO: confirmation dialog? + const [deleteEmoji, deleteResult] = query.useDeleteEmojiMutation(); + + let text = "Delete"; + if (deleteResult.isLoading) { + text = "Deleting..."; + } + + if (deleteResult.isSuccess) { + return ; + } + + return ( + + ); +} \ No newline at end of file diff --git a/web/source/settings/admin/emoji/local/index.js b/web/source/settings/admin/emoji/local/index.js new file mode 100644 index 000000000..1ccdece72 --- /dev/null +++ b/web/source/settings/admin/emoji/local/index.js @@ -0,0 +1,40 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const React = require("react"); +const {Switch, Route} = require("wouter"); + +const EmojiOverview = require("./overview"); +const EmojiDetail = require("./detail"); + +const base = "/settings/custom-emoji/local"; + +module.exports = function CustomEmoji() { + return ( + <> + + + + + + + + ); +}; diff --git a/web/source/settings/admin/emoji/local/new-emoji.js b/web/source/settings/admin/emoji/local/new-emoji.js new file mode 100644 index 000000000..985be2d32 --- /dev/null +++ b/web/source/settings/admin/emoji/local/new-emoji.js @@ -0,0 +1,168 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const Promise = require('bluebird'); +const React = require("react"); + +const FakeToot = require("../../../components/fake-toot"); +const MutateButton = require("../../../components/mutation-button"); + +const { + useTextInput, + useFileInput, + useComboBoxInput +} = require("../../../components/form"); + +const query = require("../../../lib/query"); +const { CategorySelect } = require('../category-select'); + +const shortcodeRegex = /^[a-z0-9_]+$/; + +module.exports = function NewEmojiForm({ emoji }) { + const emojiCodes = React.useMemo(() => { + return new Set(emoji.map((e) => e.shortcode)); + }, [emoji]); + + const [addEmoji, result] = query.useAddEmojiMutation(); + + const [onFileChange, resetFile, { image, imageURL, imageInfo }] = useFileInput("image", { + withPreview: true, + maxSize: 50 * 1024 + }); + + const [onShortcodeChange, resetShortcode, { shortcode, setShortcode, shortcodeRef }] = useTextInput("shortcode", { + validator: function validateShortcode(code) { + // technically invalid, but hacky fix to prevent validation error on page load + if (shortcode == "") {return "";} + + if (emojiCodes.has(code)) { + return "Shortcode already in use"; + } + + if (code.length < 2 || code.length > 30) { + return "Shortcode must be between 2 and 30 characters"; + } + + if (code.toLowerCase() != code) { + return "Shortcode must be lowercase"; + } + + if (!shortcodeRegex.test(code)) { + return "Shortcode must only contain lowercase letters, numbers, and underscores"; + } + + return ""; + } + }); + + const [categoryState, resetCategory, { category }] = useComboBoxInput("category"); + + React.useEffect(() => { + if (shortcode.length == 0) { + if (image != undefined) { + let [name, _ext] = image.name.split("."); + setShortcode(name); + } + } + // we explicitly don't want to add 'shortcode' as a dependency here + // because we only want this to update to the filename if the field is empty + // at the moment the file is selected, not some time after when the field is emptied + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [image]); + + function uploadEmoji(e) { + if (e) { + e.preventDefault(); + } + + Promise.try(() => { + return addEmoji({ + image, + shortcode, + category + }).unwrap(); + }).then(() => { + resetFile(); + resetShortcode(); + resetCategory(); + }).catch((e) => { + console.error("Emoji upload error:", e); + }); + } + + let emojiOrShortcode = `:${shortcode}:`; + + if (imageURL != undefined) { + emojiOrShortcode = {shortcode}; + } + + return ( +
+

Add new custom emoji

+ + + Look at this new custom emoji {emojiOrShortcode} isn't it cool? + + +
+
+ + {imageInfo} + +
+ +
+ + +
+ + + + + +
+ ); +}; \ No newline at end of file diff --git a/web/source/settings/admin/emoji/local/overview.js b/web/source/settings/admin/emoji/local/overview.js new file mode 100644 index 000000000..7a5cfaad6 --- /dev/null +++ b/web/source/settings/admin/emoji/local/overview.js @@ -0,0 +1,90 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const React = require("react"); +const {Link} = require("wouter"); + +const NewEmojiForm = require("./new-emoji"); + +const query = require("../../../lib/query"); +const { useEmojiByCategory } = require("../category-select"); +const Loading = require("../../../components/loading"); + +const base = "/settings/custom-emoji/local"; + +module.exports = function EmojiOverview() { + const { + data: emoji = [], + isLoading, + error + } = query.useGetAllEmojiQuery({filter: "domain:local"}); + + return ( + <> +

Custom Emoji (local)

+ {error && +
{error}
+ } + {isLoading + ? + : <> + + + + } + + ); +}; + +function EmojiList({emoji}) { + const emojiByCategory = useEmojiByCategory(emoji); + + return ( +
+

Overview

+
+ {emoji.length == 0 && "No local emoji yet, add one below"} + {Object.entries(emojiByCategory).map(([category, entries]) => { + return ; + })} +
+
+ ); +} + +function EmojiCategory({category, entries}) { + return ( +
+ {category} +
+ {entries.map((e) => { + return ( + + {/* */} + + {e.shortcode} + + + ); + })} +
+
+ ); +} \ No newline at end of file diff --git a/web/source/settings/admin/emoji/new-emoji.js b/web/source/settings/admin/emoji/new-emoji.js deleted file mode 100644 index 8cd604c02..000000000 --- a/web/source/settings/admin/emoji/new-emoji.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - - 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 . -*/ - -"use strict"; - -const Promise = require('bluebird'); -const React = require("react"); - -const FakeToot = require("../../components/fake-toot"); -const MutateButton = require("../../components/mutation-button"); - -const { - useTextInput, - useFileInput, - useComboBoxInput -} = require("../../components/form"); - -const query = require("../../lib/query"); -const { CategorySelect } = require('./category-select'); - -module.exports = function NewEmojiForm({ emoji }) { - const emojiCodes = React.useMemo(() => { - return new Set(emoji.map((e) => e.shortcode)); - }, [emoji]); - - const [addEmoji, result] = query.useAddEmojiMutation(); - - const [onFileChange, resetFile, { image, imageURL, imageInfo }] = useFileInput("image", { - withPreview: true, - maxSize: 50 * 1024 - }); - - const [onShortcodeChange, resetShortcode, { shortcode, setShortcode, shortcodeRef }] = useTextInput("shortcode", { - validator: function validateShortcode(code) { - return emojiCodes.has(code) - ? "Shortcode already in use" - : ""; - } - }); - - const [categoryState, resetCategory, { category }] = useComboBoxInput("category"); - - React.useEffect(() => { - if (shortcode.length == 0) { - if (image != undefined) { - let [name, _ext] = image.name.split("."); - setShortcode(name); - } - } - // we explicitly don't want to add 'shortcode' as a dependency here - // because we only want this to update to the filename if the field is empty - // at the moment the file is selected, not some time after when the field is emptied - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [image]); - - function uploadEmoji(e) { - if (e) { - e.preventDefault(); - } - - Promise.try(() => { - return addEmoji({ - image, - shortcode, - category - }); - }).then(() => { - resetFile(); - resetShortcode(); - resetCategory(); - }); - } - - let emojiOrShortcode = `:${shortcode}:`; - - if (imageURL != undefined) { - emojiOrShortcode = {shortcode}; - } - - return ( -
-

Add new custom emoji

- - - Look at this new custom emoji {emojiOrShortcode} isn't it cool? - - -
-
- - {imageInfo} - -
- -
- - -
- - - - - -
- ); -}; \ No newline at end of file diff --git a/web/source/settings/admin/emoji/overview.js b/web/source/settings/admin/emoji/overview.js deleted file mode 100644 index b8ac87a0f..000000000 --- a/web/source/settings/admin/emoji/overview.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - - 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 . -*/ - -"use strict"; - -const React = require("react"); -const {Link} = require("wouter"); - -const NewEmojiForm = require("./new-emoji"); - -const query = require("../../lib/query"); -const { useEmojiByCategory } = require("./category-select"); - -const base = "/settings/admin/custom-emoji"; - -module.exports = function EmojiOverview() { - const { - data: emoji = [], - isLoading, - error - } = query.useGetAllEmojiQuery({filter: "domain:local"}); - - return ( - <> -

Custom Emoji

- {error && -
{error}
- } - {isLoading - ? "Loading..." - : <> - - - - } - - ); -}; - -function EmojiList({emoji}) { - const emojiByCategory = useEmojiByCategory(emoji); - - return ( -
-

Overview

-
- {emoji.length == 0 && "No local emoji yet, add one below"} - {Object.entries(emojiByCategory).map(([category, entries]) => { - return ; - })} -
-
- ); -} - -function EmojiCategory({category, entries}) { - return ( -
- {category} -
- {entries.map((e) => { - return ( - - {/* */} - - {e.shortcode} - - - ); - })} -
-
- ); -} \ No newline at end of file diff --git a/web/source/settings/admin/emoji/remote/index.js b/web/source/settings/admin/emoji/remote/index.js new file mode 100644 index 000000000..ae59673a5 --- /dev/null +++ b/web/source/settings/admin/emoji/remote/index.js @@ -0,0 +1,54 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const React = require("react"); + +const ParseFromToot = require("./parse-from-toot"); + +const query = require("../../../lib/query"); +const Loading = require("../../../components/loading"); + +module.exports = function RemoteEmoji() { + // local emoji are queried for shortcode collision detection + const { + data: emoji = [], + isLoading, + error + } = query.useGetAllEmojiQuery({filter: "domain:local"}); + + const emojiCodes = React.useMemo(() => { + return new Set(emoji.map((e) => e.shortcode)); + }, [emoji]); + + return ( + <> +

Custom Emoji (remote)

+ {error && +
{error}
+ } + {isLoading + ? + : <> + + + } + + ); +}; \ No newline at end of file diff --git a/web/source/settings/admin/emoji/remote/parse-from-toot.js b/web/source/settings/admin/emoji/remote/parse-from-toot.js new file mode 100644 index 000000000..75ff8bf7a --- /dev/null +++ b/web/source/settings/admin/emoji/remote/parse-from-toot.js @@ -0,0 +1,319 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + 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 . +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); +const syncpipe = require("syncpipe"); + +const { + useTextInput, + useComboBoxInput +} = require("../../../components/form"); + +const { CategorySelect } = require('../category-select'); + +const query = require("../../../lib/query"); +const Loading = require("../../../components/loading"); + +module.exports = function ParseFromToot({ emojiCodes }) { + const [searchStatus, { data, isLoading, isSuccess, error }] = query.useSearchStatusForEmojiMutation(); + const instanceDomain = Redux.useSelector((state) => (new URL(state.oauth.instance).host)); + + const [onURLChange, _resetURL, { url }] = useTextInput("url"); + + const searchResult = React.useMemo(() => { + if (!isSuccess) { + return null; + } + + if (data.type == "none") { + return "No results found"; + } + + if (data.domain == instanceDomain) { + return This is a local user/toot, all referenced emoji are already on your instance; + } + + if (data.list.length == 0) { + return This {data.type == "statuses" ? "toot" : "account"} doesn't use any custom emoji; + } + + return ( + + ); + }, [isSuccess, data, instanceDomain, emojiCodes]); + + function submitSearch(e) { + e.preventDefault(); + searchStatus(url); + } + + return ( +
+

Steal this look

+
+
+ +
+ +