summaryrefslogtreecommitdiff
path: root/web/source/settings/admin
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings/admin')
-rw-r--r--web/source/settings/admin/actions.js1
-rw-r--r--web/source/settings/admin/emoji.js229
-rw-r--r--web/source/settings/admin/emoji/detail.js86
-rw-r--r--web/source/settings/admin/emoji/index.js40
-rw-r--r--web/source/settings/admin/emoji/new-emoji.js133
-rw-r--r--web/source/settings/admin/emoji/overview.js99
-rw-r--r--web/source/settings/admin/federation.js4
-rw-r--r--web/source/settings/admin/settings.js3
8 files changed, 361 insertions, 234 deletions
diff --git a/web/source/settings/admin/actions.js b/web/source/settings/admin/actions.js
index d4980d021..915b8aee1 100644
--- a/web/source/settings/admin/actions.js
+++ b/web/source/settings/admin/actions.js
@@ -18,7 +18,6 @@
"use strict";
-const Promise = require("bluebird");
const React = require("react");
const Redux = require("react-redux");
diff --git a/web/source/settings/admin/emoji.js b/web/source/settings/admin/emoji.js
deleted file mode 100644
index ad7fcab06..000000000
--- a/web/source/settings/admin/emoji.js
+++ /dev/null
@@ -1,229 +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 <http://www.gnu.org/licenses/>.
-*/
-
-"use strict";
-
-const Promise = require("bluebird");
-const React = require("react");
-const Redux = require("react-redux");
-const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter");
-
-const Submit = require("../components/submit");
-const FakeToot = require("../components/fake-toot");
-const { formFields } = require("../components/form-fields");
-
-const api = require("../lib/api");
-const adminActions = require("../redux/reducers/admin").actions;
-const submit = require("../lib/submit");
-const BackButton = require("../components/back-button");
-
-const base = "/settings/admin/custom-emoji";
-
-module.exports = function CustomEmoji() {
- const dispatch = Redux.useDispatch();
- const [loaded, setLoaded] = React.useState(false);
-
- const [errorMsg, setError] = React.useState("");
-
- React.useEffect(() => {
- if (!loaded) {
- Promise.try(() => {
- return dispatch(api.admin.fetchCustomEmoji());
- }).then(() => {
- setLoaded(true);
- }).catch((e) => {
- setLoaded(true);
- setError(e.message);
- });
- }
- }, []);
-
- if (!loaded) {
- return (
- <>
- <h1>Custom Emoji</h1>
- Loading...
- </>
- );
- }
-
- return (
- <>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- <Switch>
- <Route path={`${base}/:emojiId`}>
- <EmojiDetailWrapped />
- </Route>
- <EmojiOverview />
- </Switch>
- </>
- );
-};
-
-function EmojiOverview() {
- return (
- <>
- <h1>Custom Emoji</h1>
- <EmojiList/>
- <NewEmoji/>
- </>
- );
-}
-
-const NewEmojiForm = formFields(adminActions.updateNewEmojiVal, (state) => state.admin.newEmoji);
-function NewEmoji() {
- const dispatch = Redux.useDispatch();
- const newEmojiForm = Redux.useSelector((state) => state.admin.newEmoji);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const uploadEmoji = submit(
- () => dispatch(api.admin.newEmoji()),
- {
- setStatus, setError,
- onSuccess: function() {
- URL.revokeObjectURL(newEmojiForm.image);
- return Promise.all([
- dispatch(adminActions.updateNewEmojiVal(["image", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["imageFile", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["shortcode", ""])),
- ]);
- }
- }
- );
-
- React.useEffect(() => {
- if (newEmojiForm.shortcode.length == 0) {
- if (newEmojiForm.imageFile != undefined) {
- let [name, ext] = newEmojiForm.imageFile.name.split(".");
- dispatch(adminActions.updateNewEmojiVal(["shortcode", name]));
- }
- }
- });
-
- let emojiOrShortcode = `:${newEmojiForm.shortcode}:`;
-
- if (newEmojiForm.image != undefined) {
- emojiOrShortcode = <img
- className="emoji"
- src={newEmojiForm.image}
- title={`:${newEmojiForm.shortcode}:`}
- alt={newEmojiForm.shortcode}
- />;
- }
-
- return (
- <div>
- <h2>Add new custom emoji</h2>
-
- <FakeToot>
- Look at this new custom emoji {emojiOrShortcode} isn&apos;t it cool?
- </FakeToot>
-
- <NewEmojiForm.File
- id="image"
- name="Image"
- fileType="image/png,image/gif"
- showSize={true}
- maxSize={50 * 1000}
- />
-
- <NewEmojiForm.TextInput
- id="shortcode"
- name="Shortcode (without : :), must be unique on the instance"
- placeHolder="blobcat"
- />
-
- <Submit onClick={uploadEmoji} label="Upload" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}
-
-function EmojiList() {
- const emoji = Redux.useSelector((state) => state.admin.emoji);
-
- return (
- <div>
- <h2>Overview</h2>
- <div className="list emoji-list">
- {Object.entries(emoji).map(([category, entries]) => {
- return <EmojiCategory key={category} category={category} entries={entries}/>;
- })}
- </div>
- </div>
- );
-}
-
-function EmojiCategory({category, entries}) {
- return (
- <div className="entry">
- <b>{category}</b>
- <div className="emoji-group">
- {entries.map((e) => {
- return (
- <Link key={e.id} to={`${base}/${e.id}`}>
- {/* <Link key={e.static_url} to={`${base}`}> */}
- <a>
- <img src={e.url} alt={e.shortcode} title={`:${e.shortcode}:`}/>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
- );
-}
-
-function EmojiDetailWrapped() {
- /* We wrap the component to generate formFields with a setter depending on the domain
- if formFields() is used inside the same component that is re-rendered with their state,
- inputs get re-created on every change, causing them to lose focus, and bad performance
- */
- let [_match, {emojiId}] = useRoute(`${base}/:emojiId`);
- const emojiById = Redux.useSelector((state) => state.admin.emojiById);
- const emoji = emojiById[emojiId];
- if (emoji == undefined) {
- return (
- <h1><BackButton to={base}/> Custom Emoji: </h1>
- );
- }
-
- function alterEmoji([key, val]) {
- return adminActions.updateDomainBlockVal([emojiId, key, val]);
- }
-
- const fields = formFields(alterEmoji, (state) => state.admin.blockedInstances[emojiId]);
-
- return <EmojiDetail emoji={emoji} Form={fields} />;
-}
-
-function EmojiDetail({emoji, Form}) {
- return (
- <div>
- <h1><BackButton to={base}/> Custom Emoji: {emoji.shortcode}</h1>
- <p>
- Editing custom emoji isn&apos;t implemented yet.<br/>
- <a target="_blank" rel="noreferrer" href="https://github.com/superseriousbusiness/gotosocial/issues/797">View implementation progress.</a>
- </p>
- <img src={emoji.url} alt={emoji.shortcode} title={`:${emoji.shortcode}:`}/>
- </div>
- );
-} \ No newline at end of file
diff --git a/web/source/settings/admin/emoji/detail.js b/web/source/settings/admin/emoji/detail.js
new file mode 100644
index 000000000..cc0f8e73c
--- /dev/null
+++ b/web/source/settings/admin/emoji/detail.js
@@ -0,0 +1,86 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+"use strict";
+
+const React = require("react");
+
+const { useRoute, Link, Redirect } = require("wouter");
+
+const BackButton = require("../../components/back-button");
+
+const query = require("../../lib/query");
+
+const base = "/settings/admin/custom-emoji";
+
+/* We wrap the component to generate formFields with a setter depending on the domain
+ if formFields() is used inside the same component that is re-rendered with their state,
+ inputs get re-created on every change, causing them to lose focus, and bad performance
+*/
+module.exports = function EmojiDetailWrapped() {
+ let [_match, {emojiId}] = useRoute(`${base}/:emojiId`);
+ const {currentData: emoji, isLoading, error} = query.useGetEmojiQuery(emojiId);
+
+ return (<>
+ {error && <div className="error accent">{error.status}: {error.data.error}</div>}
+ {isLoading
+ ? "Loading..."
+ : <EmojiDetail emoji={emoji}/>
+ }
+ </>);
+};
+
+function EmojiDetail({emoji}) {
+ if (emoji == undefined) {
+ return (<>
+ <Link to={base}>
+ <a className="button">go back</a>
+ </Link>
+ </>);
+ }
+
+ return (
+ <div>
+ <h1><BackButton to={base}/> Custom Emoji: {emoji.shortcode}</h1>
+ <DeleteButton id={emoji.id}/>
+ <p>
+ Editing custom emoji isn&apos;t implemented yet.<br/>
+ <a target="_blank" rel="noreferrer" href="https://github.com/superseriousbusiness/gotosocial/issues/797">View implementation progress.</a>
+ </p>
+ <img src={emoji.url} alt={emoji.shortcode} title={`:${emoji.shortcode}:`}/>
+ </div>
+ );
+}
+
+function DeleteButton({id}) {
+ // TODO: confirmation dialog?
+ const [deleteEmoji, deleteResult] = query.useDeleteEmojiMutation();
+
+ let text = "Delete this emoji";
+ if (deleteResult.isLoading) {
+ text = "processing...";
+ }
+
+ if (deleteResult.isSuccess) {
+ return <Redirect to={base}/>;
+ }
+
+ return (
+ <button onClick={() => deleteEmoji(id)} disabled={deleteResult.isLoading}>{text}</button>
+ );
+} \ No newline at end of file
diff --git a/web/source/settings/admin/emoji/index.js b/web/source/settings/admin/emoji/index.js
new file mode 100644
index 000000000..0fcda8264
--- /dev/null
+++ b/web/source/settings/admin/emoji/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 <http://www.gnu.org/licenses/>.
+*/
+
+"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 (
+ <>
+ <Switch>
+ <Route path={`${base}/:emojiId`}>
+ <EmojiDetail />
+ </Route>
+ <EmojiOverview />
+ </Switch>
+ </>
+ );
+};
diff --git a/web/source/settings/admin/emoji/new-emoji.js b/web/source/settings/admin/emoji/new-emoji.js
new file mode 100644
index 000000000..e5bc8893d
--- /dev/null
+++ b/web/source/settings/admin/emoji/new-emoji.js
@@ -0,0 +1,133 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+"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
+} = require("../../components/form");
+
+const query = require("../../lib/query");
+
+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"
+ : "";
+ }
+ });
+
+ React.useEffect(() => {
+ if (shortcode.length == 0) {
+ if (image != undefined) {
+ let [name, _ext] = image.name.split(".");
+ setShortcode(name);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [image]);
+
+ function uploadEmoji(e) {
+ if (e) {
+ e.preventDefault();
+ }
+
+ Promise.try(() => {
+ return addEmoji({
+ image,
+ shortcode
+ });
+ }).then(() => {
+ resetFile();
+ resetShortcode();
+ });
+ }
+
+ let emojiOrShortcode = `:${shortcode}:`;
+
+ if (imageURL != undefined) {
+ emojiOrShortcode = <img
+ className="emoji"
+ src={imageURL}
+ title={`:${shortcode}:`}
+ alt={shortcode}
+ />;
+ }
+
+ return (
+ <div>
+ <h2>Add new custom emoji</h2>
+
+ <FakeToot>
+ Look at this new custom emoji {emojiOrShortcode} isn&apos;t it cool?
+ </FakeToot>
+
+ <form onSubmit={uploadEmoji} className="form-flex">
+ <div className="form-field file">
+ <label className="file-input button" htmlFor="image">
+ Browse
+ </label>
+ {imageInfo}
+ <input
+ className="hidden"
+ type="file"
+ id="image"
+ name="Image"
+ accept="image/png,image/gif"
+ onChange={onFileChange}
+ />
+ </div>
+
+ <div className="form-field text">
+ <label htmlFor="shortcode">
+ Shortcode, must be unique among the instance's local emoji
+ </label>
+ <input
+ type="text"
+ id="shortcode"
+ name="Shortcode"
+ ref={shortcodeRef}
+ onChange={onShortcodeChange}
+ value={shortcode}
+ />
+ </div>
+
+ <MutateButton text="Upload emoji" result={result}/>
+ </form>
+ </div>
+ );
+}; \ No newline at end of file
diff --git a/web/source/settings/admin/emoji/overview.js b/web/source/settings/admin/emoji/overview.js
new file mode 100644
index 000000000..028276da2
--- /dev/null
+++ b/web/source/settings/admin/emoji/overview.js
@@ -0,0 +1,99 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+"use strict";
+
+const React = require("react");
+const {Link} = require("wouter");
+const defaultValue = require('default-value');
+
+const NewEmojiForm = require("./new-emoji");
+
+const query = require("../../lib/query");
+
+const base = "/settings/admin/custom-emoji";
+
+module.exports = function EmojiOverview() {
+ const {
+ data: emoji,
+ isLoading,
+ error
+ } = query.useGetAllEmojiQuery({filter: "domain:local"});
+
+ return (
+ <>
+ <h1>Custom Emoji</h1>
+ {error &&
+ <div className="error accent">{error}</div>
+ }
+ {isLoading
+ ? "Loading..."
+ : <>
+ <EmojiList emoji={emoji}/>
+ <NewEmojiForm emoji={emoji}/>
+ </>
+ }
+ </>
+ );
+};
+
+function EmojiList({emoji}) {
+ const byCategory = React.useMemo(() => {
+ const categories = {};
+
+ emoji.forEach((emoji) => {
+ let cat = defaultValue(emoji.category, "Unsorted");
+ categories[cat] = defaultValue(categories[cat], []);
+ categories[cat].push(emoji);
+ });
+
+ return categories;
+ }, [emoji]);
+
+ return (
+ <div>
+ <h2>Overview</h2>
+ <div className="list emoji-list">
+ {emoji.length == 0 && "No local emoji yet"}
+ {Object.entries(byCategory).map(([category, entries]) => {
+ return <EmojiCategory key={category} category={category} entries={entries}/>;
+ })}
+ </div>
+ </div>
+ );
+}
+
+function EmojiCategory({category, entries}) {
+ return (
+ <div className="entry">
+ <b>{category}</b>
+ <div className="emoji-group">
+ {entries.map((e) => {
+ return (
+ <Link key={e.id} to={`${base}/${e.id}`}>
+ {/* <Link key={e.static_url} to={`${base}`}> */}
+ <a>
+ <img src={e.url} alt={e.shortcode} title={`:${e.shortcode}:`}/>
+ </a>
+ </Link>
+ );
+ })}
+ </div>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/web/source/settings/admin/federation.js b/web/source/settings/admin/federation.js
index 99f10e69e..449d2156f 100644
--- a/web/source/settings/admin/federation.js
+++ b/web/source/settings/admin/federation.js
@@ -50,7 +50,7 @@ module.exports = function AdminSettings() {
return dispatch(api.admin.fetchDomainBlocks());
});
}
- }, []);
+ }, [dispatch, loadedBlockedInstances]);
if (!loadedBlockedInstances) {
return (
@@ -315,7 +315,7 @@ function InstancePage({domain, Form}) {
if (entry == undefined) {
dispatch(api.admin.getEditableDomainBlock(domain));
}
- }, []);
+ }, [dispatch, domain, entry]);
const [errorMsg, setError] = React.useState("");
const [statusMsg, setStatus] = React.useState("");
diff --git a/web/source/settings/admin/settings.js b/web/source/settings/admin/settings.js
index 845a1f924..eb6e8b267 100644
--- a/web/source/settings/admin/settings.js
+++ b/web/source/settings/admin/settings.js
@@ -18,7 +18,6 @@
"use strict";
-const Promise = require("bluebird");
const React = require("react");
const Redux = require("react-redux");
@@ -32,7 +31,7 @@ const adminActions = require("../redux/reducers/instances").actions;
const {
TextInput,
TextArea,
- File
+ _File
} = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings);
module.exports = function AdminSettings() {