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 .
-*/
-
-"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 (
- <>
-
- );
-}
-
-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 (
-
- );
-}
\ 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..abdd38020
--- /dev/null
+++ b/web/source/settings/admin/emoji/detail.js
@@ -0,0 +1,89 @@
+/*
+ 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 Redux = require("react-redux");
+
+const { useRoute, Link, Redirect } = require("wouter");
+
+const adminActions = require("../../redux/reducers/admin").actions;
+const BackButton = require("../../components/back-button");
+const { formFields } = require("../../components/form-fields");
+
+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 &&
+ );
+}
+
+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 ;
+ }
+
+ 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
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 .
+*/
+
+"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/overview.js b/web/source/settings/admin/emoji/overview.js
new file mode 100644
index 000000000..bccb00ade
--- /dev/null
+++ b/web/source/settings/admin/emoji/overview.js
@@ -0,0 +1,177 @@
+/*
+ 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 {Link} = require("wouter");
+const defaultValue = require('default-value');
+
+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 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 (
+ <>
+
+
+
+ Look at this new custom emoji {emojiOrShortcode} isn't it cool?
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/web/source/settings/index.js b/web/source/settings/index.js
index 34720e818..6a72dabe0 100644
--- a/web/source/settings/index.js
+++ b/web/source/settings/index.js
@@ -46,7 +46,7 @@ const nav = {
"Instance Settings": require("./admin/settings.js"),
"Actions": require("./admin/actions"),
"Federation": require("./admin/federation.js"),
- "Custom Emoji": require("./admin/emoji.js"),
+ "Custom Emoji": require("./admin/emoji"),
}
};
diff --git a/web/source/settings/lib/api/index.js b/web/source/settings/lib/api/index.js
index 20377824c..92c54dc0e 100644
--- a/web/source/settings/lib/api/index.js
+++ b/web/source/settings/lib/api/index.js
@@ -51,21 +51,7 @@ function apiCall(method, route, payload, type = "json") {
headers["Content-Type"] = "application/json";
body = JSON.stringify(payload);
} else if (type == "form") {
- const formData = new FormData();
- Object.entries(payload).forEach(([key, val]) => {
- if (isPlainObject(val)) {
- Object.entries(val).forEach(([key2, val2]) => {
- if (val2 != undefined) {
- formData.set(`${key}[${key2}]`, val2);
- }
- });
- } else {
- if (val != undefined) {
- formData.set(key, val);
- }
- }
- });
- body = formData;
+ body = convertToForm(payload);
}
}
@@ -100,6 +86,24 @@ function apiCall(method, route, payload, type = "json") {
};
}
+function convertToForm(payload) {
+ const formData = new FormData();
+ Object.entries(payload).forEach(([key, val]) => {
+ if (isPlainObject(val)) {
+ Object.entries(val).forEach(([key2, val2]) => {
+ if (val2 != undefined) {
+ formData.set(`${key}[${key2}]`, val2);
+ }
+ });
+ } else {
+ if (val != undefined) {
+ formData.set(key, val);
+ }
+ }
+ });
+ return formData;
+}
+
function getChanges(state, keys) {
const { formKeys = [], fileKeys = [], renamedKeys = {} } = keys;
const update = {};
@@ -182,5 +186,6 @@ module.exports = {
user: require("./user")(submoduleArgs),
admin: require("./admin")(submoduleArgs),
apiCall,
+ convertToForm,
getChanges
};
\ No newline at end of file
diff --git a/web/source/settings/lib/query/base.js b/web/source/settings/lib/query/base.js
new file mode 100644
index 000000000..4023e9fbd
--- /dev/null
+++ b/web/source/settings/lib/query/base.js
@@ -0,0 +1,55 @@
+/*
+ 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 { createApi, fetchBaseQuery } = require("@reduxjs/toolkit/query/react");
+
+const { convertToForm } = require("../api");
+
+function instanceBasedQuery(args, api, extraOptions) {
+ const state = api.getState();
+ const {instance, token} = state.oauth;
+
+ if (args.baseUrl == undefined) {
+ args.baseUrl = instance;
+ }
+
+ if (args.asForm) {
+ delete args.asForm;
+ args.body = convertToForm(args.body);
+ }
+
+ return fetchBaseQuery({
+ baseUrl: args.baseUrl,
+ prepareHeaders: (headers) => {
+ if (token != undefined) {
+ headers.set('Authorization', token);
+ }
+ headers.set("Accept", "application/json");
+ return headers;
+ },
+ })(args, api, extraOptions);
+}
+
+module.exports = createApi({
+ reducerPath: "api",
+ baseQuery: instanceBasedQuery,
+ tagTypes: ["Emojis"],
+ endpoints: () => ({})
+});
\ No newline at end of file
diff --git a/web/source/settings/lib/query/custom-emoji.js b/web/source/settings/lib/query/custom-emoji.js
new file mode 100644
index 000000000..c448c2ec7
--- /dev/null
+++ b/web/source/settings/lib/query/custom-emoji.js
@@ -0,0 +1,70 @@
+/*
+ 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 base = require("./base");
+const { getChanges } = require("../api");
+
+const endpoints = (build) => ({
+ getAllEmoji: build.query({
+ query: (params = {}) => ({
+ url: "/api/v1/admin/custom_emojis",
+ params: {
+ limit: 0,
+ ...params
+ },
+ providesTags: (res) =>
+ res
+ ? [...res.map((id) => ({type: "Emojis", id})), {type: "Emojis", id: "LIST"}]
+ : [{type: "Emojis", id: "LIST"}]
+ })
+ }),
+ getEmoji: build.query({
+ query: (id) => ({
+ url: `/api/v1/admin/custom_emojis/${id}`
+ }),
+ providesTags: (res, error, id) => [{type: "Emojis", id}]
+ }),
+ addEmoji: build.query({
+ query: (form) => {
+ const body = getChanges(form, {
+ formKeys: ["shortcode"],
+ fileKeys: ["image"]
+ });
+ return {
+ method: "POST",
+ url: `/api/v1/admin/custom_emojis`,
+ asForm: true,
+ body
+ };
+ },
+ providesTags: (res, error, id) => [{type: "Emojis", id}]
+ }),
+ deleteEmoji: build.mutation({
+ query: (id) => ({
+ method: "DELETE",
+ url: `/api/v1/admin/custom_emojis/${id}`
+ }),
+ invalidatesTags: (res, error, id) => [{type: "Emojis", id}]
+ })
+});
+
+module.exports = base.injectEndpoints({endpoints});
\ No newline at end of file
diff --git a/web/source/settings/lib/query/index.js b/web/source/settings/lib/query/index.js
new file mode 100644
index 000000000..5b15fd252
--- /dev/null
+++ b/web/source/settings/lib/query/index.js
@@ -0,0 +1,24 @@
+/*
+ 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";
+
+module.exports = {
+ ...require("./base"),
+ ...require("./custom-emoji.js")
+};
\ No newline at end of file
diff --git a/web/source/settings/redux/index.js b/web/source/settings/redux/index.js
index f18b8e452..43f5c007d 100644
--- a/web/source/settings/redux/index.js
+++ b/web/source/settings/redux/index.js
@@ -18,18 +18,20 @@
"use strict";
-const { createStore, combineReducers, applyMiddleware } = require("redux");
-const { persistStore, persistReducer } = require("redux-persist");
-const thunk = require("redux-thunk").default;
-const { composeWithDevTools } = require("redux-devtools-extension");
+const { combineReducers } = require("redux");
+const { configureStore } = require("@reduxjs/toolkit");
+const {
+ persistStore,
+ persistReducer,
+ FLUSH,
+ REHYDRATE,
+ PAUSE,
+ PERSIST,
+ PURGE,
+ REGISTER,
+} = require("redux-persist");
-const persistConfig = {
- key: "gotosocial-settings",
- storage: require("redux-persist/lib/storage").default,
- stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default,
- whitelist: ["oauth"],
- blacklist: ["temporary"]
-};
+const query = require("../lib/query/base");
const combinedReducers = combineReducers({
oauth: require("./reducers/oauth").reducer,
@@ -37,13 +39,27 @@ const combinedReducers = combineReducers({
temporary: require("./reducers/temporary").reducer,
user: require("./reducers/user").reducer,
admin: require("./reducers/admin").reducer,
+ [query.reducerPath]: query.reducer
});
-const persistedReducer = persistReducer(persistConfig, combinedReducers);
-const composedEnhancer = composeWithDevTools(applyMiddleware(thunk));
+const persistedReducer = persistReducer({
+ key: "gotosocial-settings",
+ storage: require("redux-persist/lib/storage").default,
+ stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default,
+ whitelist: ["oauth"],
+}, combinedReducers);
+
+const store = configureStore({
+ reducer: persistedReducer,
+ middleware: (getDefaultMiddleware) => {
+ return getDefaultMiddleware({
+ serializableCheck: {
+ ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, "temporary/setScrollElement"]
+ }
+ }).concat(query.middleware);
+ }
+});
-// TODO: change to configureStore
-const store = createStore(persistedReducer, composedEnhancer);
const persistor = persistStore(store);
module.exports = { store, persistor };
\ No newline at end of file