From 53940f2c00d838a9704f4fb436c78cac6a70526c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Tue, 10 Mar 2026 13:56:17 +0100 Subject: [PATCH] nicolium: remove admin reducer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/actions/admin.ts | 66 +---------- packages/nicolium/src/actions/mrf.ts | 26 ++--- .../navigation/dropdown-navigation.tsx | 4 +- .../components/registration-mode-picker.tsx | 13 +-- .../components/restricted-instance.tsx | 7 +- .../components/panels/instance-info-panel.tsx | 7 +- .../panels/instance-moderation-panel.tsx | 7 +- packages/nicolium/src/features/ui/index.tsx | 10 +- .../src/modals/edit-federation-modal.tsx | 23 ++-- .../src/pages/dashboard/frontend-config.tsx | 19 ++-- .../src/pages/dashboard/theme-editor.tsx | 7 +- .../pages/utils/federation-restrictions.tsx | 8 +- .../nicolium/src/queries/admin/use-config.ts | 57 ++++++++++ packages/nicolium/src/queries/keys.ts | 2 + .../notifications/use-notifications.ts | 2 +- packages/nicolium/src/reducers/admin.ts | 38 ------- .../nicolium/src/reducers/frontend-config.ts | 4 +- packages/nicolium/src/reducers/index.ts | 2 - packages/nicolium/src/reducers/instance.ts | 7 +- packages/nicolium/src/selectors/index.ts | 103 +++++++++--------- 20 files changed, 179 insertions(+), 233 deletions(-) create mode 100644 packages/nicolium/src/queries/admin/use-config.ts delete mode 100644 packages/nicolium/src/reducers/admin.ts diff --git a/packages/nicolium/src/actions/admin.ts b/packages/nicolium/src/actions/admin.ts index 0cb1e8ab6..253e486c4 100644 --- a/packages/nicolium/src/actions/admin.ts +++ b/packages/nicolium/src/actions/admin.ts @@ -10,52 +10,8 @@ import { filterBadges, getTagDiff } from '@/utils/badges'; import type { AppDispatch, RootState } from '@/store'; import type { PleromaConfig } from 'pl-api'; -const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS' as const; - -const ADMIN_CONFIG_UPDATE_REQUEST = 'ADMIN_CONFIG_UPDATE_REQUEST' as const; const ADMIN_CONFIG_UPDATE_SUCCESS = 'ADMIN_CONFIG_UPDATE_SUCCESS' as const; -const fetchConfig = () => (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState) - .admin.config.getPleromaConfig() - .then((data) => { - dispatch({ - type: ADMIN_CONFIG_FETCH_SUCCESS, - configs: data.configs, - needsReboot: data.need_reboot, - }); - }); - -const updateConfig = - (configs: PleromaConfig['configs']) => (dispatch: AppDispatch, getState: () => RootState) => { - dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs }); - return getClient(getState) - .admin.config.updatePleromaConfig(configs) - .then((data) => { - dispatch({ - type: ADMIN_CONFIG_UPDATE_SUCCESS, - configs: data.configs, - needsReboot: data.need_reboot, - }); - }); - }; - -const updateFrontendConfig = (data: Record) => (dispatch: AppDispatch) => { - const params = [ - { - group: ':pleroma', - key: ':frontend_configurations', - value: [ - { - tuple: [':nicolium', data], - }, - ], - }, - ]; - - return dispatch(updateConfig(params)); -}; - const deactivateUser = (accountId: string, report_id?: string) => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); @@ -150,26 +106,14 @@ const redactStatus = (statusId: string) => (dispatch: AppDispatch, getState: () }); }; -type AdminActions = - | { - type: typeof ADMIN_CONFIG_FETCH_SUCCESS; - configs: PleromaConfig['configs']; - needsReboot: boolean; - } - | { type: typeof ADMIN_CONFIG_UPDATE_REQUEST; configs: PleromaConfig['configs'] } - | { - type: typeof ADMIN_CONFIG_UPDATE_SUCCESS; - configs: PleromaConfig['configs']; - needsReboot: boolean; - }; +type AdminActions = { + type: typeof ADMIN_CONFIG_UPDATE_SUCCESS; + configs: PleromaConfig['configs']; + needsReboot: boolean; +}; export { - ADMIN_CONFIG_FETCH_SUCCESS, - ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS, - fetchConfig, - updateConfig, - updateFrontendConfig, deactivateUser, deleteUser, deleteStatus, diff --git a/packages/nicolium/src/actions/mrf.ts b/packages/nicolium/src/actions/mrf.ts index fb317b858..2ea8447b3 100644 --- a/packages/nicolium/src/actions/mrf.ts +++ b/packages/nicolium/src/actions/mrf.ts @@ -1,9 +1,7 @@ import { mrfSimpleSchema, type MRFSimple } from '@/schemas/pleroma'; import ConfigDB from '@/utils/config-db'; -import { fetchConfig, updateConfig } from './admin'; - -import type { AppDispatch, RootState } from '@/store'; +import type { PleromaConfig } from 'pl-api'; const simplePolicyMerge = ( simplePolicy: Partial, @@ -32,15 +30,15 @@ const simplePolicyMerge = ( ]); }; -const updateMrf = - (host: string, restrictions: Record) => - (dispatch: AppDispatch, getState: () => RootState) => - dispatch(fetchConfig()).then(() => { - const configs = getState().admin.configs; - const simplePolicy = ConfigDB.toSimplePolicy(configs); - const merged = simplePolicyMerge(simplePolicy, host, restrictions); - const config = ConfigDB.fromSimplePolicy(merged); - return dispatch(updateConfig(config)); - }); +const getUpdatedMrf = ( + configs: PleromaConfig['configs'], + host: string, + restrictions: Record, +) => { + const simplePolicy = ConfigDB.toSimplePolicy(configs); + const merged = simplePolicyMerge(simplePolicy, host, restrictions); + const config = ConfigDB.fromSimplePolicy(merged); + return config; +}; -export { updateMrf }; +export { getUpdatedMrf }; diff --git a/packages/nicolium/src/components/navigation/dropdown-navigation.tsx b/packages/nicolium/src/components/navigation/dropdown-navigation.tsx index 44fb4d977..00b27a13c 100644 --- a/packages/nicolium/src/components/navigation/dropdown-navigation.tsx +++ b/packages/nicolium/src/components/navigation/dropdown-navigation.tsx @@ -149,7 +149,9 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => const scheduledStatusCount = useInfiniteQuery(authenticatedScheduledStatusesCountQueryOptions).data ?? 0; const { data: draftCount = 0 } = useDraftStatusesCountQuery(); - // const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count()); + // const { data: awaitingApprovalCount = 0 } = usePendingUsersCount(); + // const { data: pendingReportsCount = 0 } = usePendingReportsCount(); + // const dashboardCount = pendingReportsCount + awaitingApprovalCount; const [sidebarVisible, setSidebarVisible] = useState(isSidebarOpen); const touchStart = useRef(0); const touchEnd = useRef(null); diff --git a/packages/nicolium/src/features/admin/components/registration-mode-picker.tsx b/packages/nicolium/src/features/admin/components/registration-mode-picker.tsx index 0bc6d7278..c40ac2ac1 100644 --- a/packages/nicolium/src/features/admin/components/registration-mode-picker.tsx +++ b/packages/nicolium/src/features/admin/components/registration-mode-picker.tsx @@ -1,10 +1,9 @@ import React from 'react'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; -import { updateConfig } from '@/actions/admin'; import { RadioGroup, RadioItem } from '@/components/ui/radio'; -import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useInstance } from '@/hooks/use-instance'; +import { useUpdateAdminConfig } from '@/queries/admin/use-config'; import toast from '@/toast'; import type { Instance } from 'pl-api'; @@ -45,18 +44,18 @@ const modeFromInstance = ({ registrations }: Instance): RegistrationMode => { /** Allows changing the registration mode of the instance, eg "open", "closed", "approval" */ const RegistrationModePicker: React.FC = () => { const intl = useIntl(); - const dispatch = useAppDispatch(); const instance = useInstance(); + const { mutate: updateConfig } = useUpdateAdminConfig(); const mode = modeFromInstance(instance); const onChange: React.ChangeEventHandler = (e) => { const config = generateConfig(e.target.value as RegistrationMode); - dispatch(updateConfig(config)) - .then(() => { + updateConfig(config, { + onSuccess: () => { toast.success(intl.formatMessage(messages.saved)); - }) - .catch(() => {}); + }, + }); }; return ( diff --git a/packages/nicolium/src/features/federation-restrictions/components/restricted-instance.tsx b/packages/nicolium/src/features/federation-restrictions/components/restricted-instance.tsx index 82756754c..b4c066ecd 100644 --- a/packages/nicolium/src/features/federation-restrictions/components/restricted-instance.tsx +++ b/packages/nicolium/src/features/federation-restrictions/components/restricted-instance.tsx @@ -2,19 +2,16 @@ import clsx from 'clsx'; import React, { useState } from 'react'; import Icon from '@/components/icon'; -import { useAppSelector } from '@/hooks/use-app-selector'; -import { makeGetRemoteInstance } from '@/selectors'; +import { useRemoteInstance } from '@/selectors'; import InstanceRestrictions from './instance-restrictions'; -const getRemoteInstance = makeGetRemoteInstance(); - interface IRestrictedInstance { host: string; } const RestrictedInstance: React.FC = ({ host }) => { - const remoteInstance = useAppSelector((state) => getRemoteInstance(state, host)); + const remoteInstance = useRemoteInstance(host); const [expanded, setExpanded] = useState(false); diff --git a/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx b/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx index c03382bb4..8afa72ab8 100644 --- a/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx +++ b/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx @@ -4,12 +4,9 @@ import { useIntl, defineMessages } from 'react-intl'; import { pinHost, unpinHost } from '@/actions/remote-timeline'; import Widget from '@/components/ui/widget'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; -import { useAppSelector } from '@/hooks/use-app-selector'; -import { makeGetRemoteInstance } from '@/selectors'; +import { useRemoteInstance } from '@/selectors'; import { useSettings } from '@/stores/settings'; -const getRemoteInstance = makeGetRemoteInstance(); - const messages = defineMessages({ pinHost: { id: 'remote_instance.pin_host', defaultMessage: 'Pin {host}' }, unpinHost: { id: 'remote_instance.unpin_host', defaultMessage: 'Unpin {host}' }, @@ -26,7 +23,7 @@ const InstanceInfoPanel: React.FC = ({ host }) => { const dispatch = useAppDispatch(); const settings = useSettings(); - const remoteInstance = useAppSelector((state) => getRemoteInstance(state, host)); + const remoteInstance = useRemoteInstance(host); const pinned = settings.remote_timeline.pinnedHosts.includes(host); const handlePinHost = () => { diff --git a/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx b/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx index 25a1774d2..2a2549c88 100644 --- a/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx +++ b/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx @@ -4,13 +4,10 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import DropdownMenu from '@/components/dropdown-menu'; import Widget from '@/components/ui/widget'; import InstanceRestrictions from '@/features/federation-restrictions/components/instance-restrictions'; -import { useAppSelector } from '@/hooks/use-app-selector'; import { useOwnAccount } from '@/hooks/use-own-account'; -import { makeGetRemoteInstance } from '@/selectors'; +import { useRemoteInstance } from '@/selectors'; import { useModalsActions } from '@/stores/modals'; -const getRemoteInstance = makeGetRemoteInstance(); - const messages = defineMessages({ editFederation: { id: 'remote_instance.edit_federation', defaultMessage: 'Edit federation' }, }); @@ -26,7 +23,7 @@ const InstanceModerationPanel: React.FC = ({ host }) = const { openModal } = useModalsActions(); const { data: account } = useOwnAccount(); - const remoteInstance = useAppSelector((state) => getRemoteInstance(state, host)); + const remoteInstance = useRemoteInstance(host); const handleEditFederation = () => { openModal('EDIT_FEDERATION', { host }); diff --git a/packages/nicolium/src/features/ui/index.tsx b/packages/nicolium/src/features/ui/index.tsx index f34b51351..19964b43b 100644 --- a/packages/nicolium/src/features/ui/index.tsx +++ b/packages/nicolium/src/features/ui/index.tsx @@ -3,7 +3,6 @@ import clsx from 'clsx'; import React, { Suspense, useEffect, useRef } from 'react'; import { Toaster } from 'react-hot-toast'; -import { fetchConfig } from '@/actions/admin'; import { register as registerPushNotifications } from '@/actions/push-notifications/registerer'; import { useUserStream } from '@/api/hooks/streaming/use-user-stream'; import SidebarNavigation from '@/components/navigation/sidebar-navigation'; @@ -17,6 +16,7 @@ import { useFeatures } from '@/hooks/use-features'; import { useInstance } from '@/hooks/use-instance'; import { useOwnAccount } from '@/hooks/use-own-account'; import { prefetchFollowRequests } from '@/queries/accounts/use-follow-requests'; +import { useAdminConfig } from '@/queries/admin/use-config'; import { queryClient } from '@/queries/client'; import { prefetchCustomEmojis } from '@/queries/instance/use-custom-emojis'; import { usePrefetchNotificationsMarker } from '@/queries/markers/use-markers'; @@ -26,10 +26,11 @@ import { scheduledStatusesQueryOptions } from '@/queries/statuses/scheduled-stat import { useShoutboxSubscription } from '@/stores/shoutbox'; import { useIsDropdownMenuOpen } from '@/stores/ui'; import { getVapidKey } from '@/utils/auth'; -import { isStandalone } from '@/utils/state'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '@/components/statuses/status'; +import { isStandalone } from '@/utils/state'; + import { ModalRoot, AccountHoverCard, @@ -53,6 +54,7 @@ const UI: React.FC = React.memo(() => { const isDropdownMenuOpen = useIsDropdownMenuOpen(); const standalone = useAppSelector(isStandalone); + useAdminConfig(); useShoutboxSubscription(); useFilters(); usePrefetchNotifications(); @@ -87,10 +89,6 @@ const UI: React.FC = React.memo(() => { prefetchCustomEmojis(client); - if (account.is_admin && features.pleromaAdminAccounts) { - dispatch(fetchConfig()); - } - if (account.locked) { requestIdleCallback(() => prefetchFollowRequests(client), { timeout: 2000 }); } diff --git a/packages/nicolium/src/modals/edit-federation-modal.tsx b/packages/nicolium/src/modals/edit-federation-modal.tsx index 902974ca1..f8b5fdc0f 100644 --- a/packages/nicolium/src/modals/edit-federation-modal.tsx +++ b/packages/nicolium/src/modals/edit-federation-modal.tsx @@ -1,13 +1,12 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { updateMrf } from '@/actions/mrf'; +import { getUpdatedMrf } from '@/actions/mrf'; import List, { ListItem } from '@/components/list'; import Modal from '@/components/ui/modal'; import Toggle from '@/components/ui/toggle'; -import { useAppDispatch } from '@/hooks/use-app-dispatch'; -import { useAppSelector } from '@/hooks/use-app-selector'; -import { makeGetRemoteInstance } from '@/selectors'; +import { useAdminConfig, useUpdateAdminConfig } from '@/queries/admin/use-config'; +import { useRemoteInstance } from '@/selectors'; import toast from '@/toast'; import type { BaseModalProps } from '@/features/ui/components/modal-root'; @@ -37,10 +36,10 @@ const EditFederationModal: React.FC = onClose, }) => { const intl = useIntl(); - const dispatch = useAppDispatch(); - const getRemoteInstance = useCallback(makeGetRemoteInstance(), []); - const remoteInstance = useAppSelector((state) => getRemoteInstance(state, host)); + const { data: config } = useAdminConfig(); + const remoteInstance = useRemoteInstance(host); + const { mutate: updateConfig } = useUpdateAdminConfig(); const [data, setData] = useState>({}); @@ -68,11 +67,11 @@ const EditFederationModal: React.FC = }; const handleSubmit = () => { - dispatch(updateMrf(host, data)) - .then(() => { + updateConfig(getUpdatedMrf(config!.configs, host, data), { + onSuccess: () => { toast.success(intl.formatMessage(messages.success, { host })); - }) - .catch(() => {}); + }, + }); onClose('EDIT_FEDERATION'); }; diff --git a/packages/nicolium/src/pages/dashboard/frontend-config.tsx b/packages/nicolium/src/pages/dashboard/frontend-config.tsx index fdd1b20cb..7b6217adb 100644 --- a/packages/nicolium/src/pages/dashboard/frontend-config.tsx +++ b/packages/nicolium/src/pages/dashboard/frontend-config.tsx @@ -3,7 +3,6 @@ import React, { useState, useEffect, useMemo } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import * as v from 'valibot'; -import { updateFrontendConfig } from '@/actions/admin'; import { uploadMedia } from '@/actions/media'; import List, { ListItem } from '@/components/list'; import Accordion from '@/components/ui/accordion'; @@ -27,6 +26,7 @@ import ThemeSelector from '@/features/ui/components/theme-selector'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useAppSelector } from '@/hooks/use-app-selector'; import { useFeatures } from '@/hooks/use-features'; +import { getUpdateFrontendConfigParams, useUpdateAdminConfig } from '@/queries/admin/use-config'; import { cryptoAddressSchema, footerItemSchema, @@ -69,8 +69,9 @@ const FrontendConfigEditor: React.FC = () => { const features = useFeatures(); const initialData = useAppSelector((state) => state.frontendConfig); + console.log(initialData); + const { mutate: updateConfig, isPending } = useUpdateAdminConfig(); - const [isLoading, setLoading] = useState(false); const [data, setData] = useState(v.parse(frontendConfigSchema, initialData)); const [jsonEditorExpanded, setJsonEditorExpanded] = useState(false); const [rawJSON, setRawJSON] = useState(JSON.stringify(initialData, null, 2)); @@ -89,15 +90,11 @@ const FrontendConfigEditor: React.FC = () => { }; const handleSubmit: React.SubmitEventHandler = (e) => { - dispatch(updateFrontendConfig(data)) - .then(() => { - setLoading(false); + updateConfig(getUpdateFrontendConfigParams(data), { + onSuccess: () => { toast.success(intl.formatMessage(messages.saved)); - }) - .catch(() => { - setLoading(false); - }); - setLoading(true); + }, + }); e.preventDefault(); }; @@ -199,7 +196,7 @@ const FrontendConfigEditor: React.FC = () => { return (
-
+
diff --git a/packages/nicolium/src/pages/dashboard/theme-editor.tsx b/packages/nicolium/src/pages/dashboard/theme-editor.tsx index 9bc17fd19..9a857def6 100644 --- a/packages/nicolium/src/pages/dashboard/theme-editor.tsx +++ b/packages/nicolium/src/pages/dashboard/theme-editor.tsx @@ -2,7 +2,6 @@ import React, { useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import * as v from 'valibot'; -import { updateFrontendConfig } from '@/actions/admin'; import { fetchFrontendConfig } from '@/actions/frontend-config'; import { getHost } from '@/actions/instance'; import DropdownMenu from '@/components/dropdown-menu'; @@ -17,6 +16,7 @@ import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useAppSelector } from '@/hooks/use-app-selector'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { normalizeColors } from '@/hooks/use-theme-css'; +import { getUpdateFrontendConfigParams, useUpdateAdminConfig } from '@/queries/admin/use-config'; import { frontendConfigSchema } from '@/schemas/frontend-config'; import toast from '@/toast'; import { download } from '@/utils/download'; @@ -56,6 +56,7 @@ const ThemeEditorPage: React.FC = () => { const frontendConfig = useFrontendConfig(); const host = useAppSelector((state) => getHost(state)); const rawConfig = useAppSelector((state) => state.frontendConfig); + const { mutate: updateConfig } = useUpdateAdminConfig(); const [colors, setColors] = useState(normalizeColors(frontendConfig)); const [isDefault, setIsDefault] = useState(false); @@ -97,14 +98,14 @@ const ThemeEditorPage: React.FC = () => { setTheme(normalizeColors(frontendConfig)); }; - const updateTheme = async () => { + const updateTheme = () => { let params; if (isDefault) { params = { ...rawConfig, colors: undefined, brandColor: undefined, accentColor: undefined }; } else { params = { ...rawConfig, colors }; } - await dispatch(updateFrontendConfig(params)); + return updateConfig(getUpdateFrontendConfigParams(params)); }; const restoreDefaultTheme = () => { diff --git a/packages/nicolium/src/pages/utils/federation-restrictions.tsx b/packages/nicolium/src/pages/utils/federation-restrictions.tsx index 6955e320e..c952ba5ac 100644 --- a/packages/nicolium/src/pages/utils/federation-restrictions.tsx +++ b/packages/nicolium/src/pages/utils/federation-restrictions.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import ScrollableList from '@/components/scrollable-list'; @@ -7,7 +7,7 @@ import Column from '@/components/ui/column'; import RestrictedInstance from '@/features/federation-restrictions/components/restricted-instance'; import { useAppSelector } from '@/hooks/use-app-selector'; import { useInstance } from '@/hooks/use-instance'; -import { makeGetHosts } from '@/selectors'; +import { useHosts } from '@/selectors'; import { federationRestrictionsDisclosed } from '@/utils/state'; const messages = defineMessages({ @@ -27,9 +27,7 @@ const FederationRestrictionsPage = () => { const intl = useIntl(); const instance = useInstance(); - const getHosts = useCallback(makeGetHosts(), []); - - const hosts = useAppSelector((state) => getHosts(state)); + const hosts = useHosts(); const disclosed = useAppSelector((state) => federationRestrictionsDisclosed(state)); const [explanationBoxExpanded, setExplanationBoxExpanded] = useState(true); diff --git a/packages/nicolium/src/queries/admin/use-config.ts b/packages/nicolium/src/queries/admin/use-config.ts new file mode 100644 index 000000000..bceb8b04b --- /dev/null +++ b/packages/nicolium/src/queries/admin/use-config.ts @@ -0,0 +1,57 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { ADMIN_CONFIG_UPDATE_SUCCESS, type AdminActions } from '@/actions/admin'; +import { useAppDispatch } from '@/hooks/use-app-dispatch'; +import { useClient } from '@/hooks/use-client'; +import { useFeatures } from '@/hooks/use-features'; +import { useOwnAccount } from '@/hooks/use-own-account'; + +import { queryKeys } from '../keys'; + +const useAdminConfig = () => { + const client = useClient(); + const features = useFeatures(); + const { data: ownAccount } = useOwnAccount(); + + return useQuery({ + queryKey: queryKeys.admin.config, + queryFn: () => client.admin.config.getPleromaConfig(), + enabled: ownAccount?.is_admin && features.pleromaAdminAccounts, + }); +}; + +const useUpdateAdminConfig = () => { + const client = useClient(); + const dispatch = useAppDispatch(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: Parameters[0]) => + client.admin.config.updatePleromaConfig(params), + retry: false, + onSuccess: (data) => { + dispatch({ + type: ADMIN_CONFIG_UPDATE_SUCCESS, + configs: data.configs, + needsReboot: data.need_reboot, + }); + queryClient.setQueryData(queryKeys.admin.config, data); + }, + }); +}; + +const getUpdateFrontendConfigParams = (data: any) => { + return [ + { + group: ':pleroma', + key: ':frontend_configurations', + value: [ + { + tuple: [':nicolium', data], + }, + ], + }, + ]; +}; + +export { useAdminConfig, useUpdateAdminConfig, getUpdateFrontendConfigParams }; diff --git a/packages/nicolium/src/queries/keys.ts b/packages/nicolium/src/queries/keys.ts index 07ed0ae86..b557369b7 100644 --- a/packages/nicolium/src/queries/keys.ts +++ b/packages/nicolium/src/queries/keys.ts @@ -53,6 +53,7 @@ import type { OauthToken, PaginatedResponse, PlApiClient, + PleromaConfig, Poll, Relationship, RssFeed, @@ -304,6 +305,7 @@ const groupRelationships = { const admin = { root: ['admin'] as const, + config: ['admin', 'config'] as TaggedKey<['admin', 'config'], PleromaConfig>, accounts: { root: ['admin', 'accounts'] as const, show: (accountId: string) => { diff --git a/packages/nicolium/src/queries/notifications/use-notifications.ts b/packages/nicolium/src/queries/notifications/use-notifications.ts index 8594c5b6e..16febab11 100644 --- a/packages/nicolium/src/queries/notifications/use-notifications.ts +++ b/packages/nicolium/src/queries/notifications/use-notifications.ts @@ -20,9 +20,9 @@ import { useLoggedIn } from '@/hooks/use-logged-in'; import { appendFollowRequest } from '@/queries/accounts/use-follow-requests'; import { queryClient } from '@/queries/client'; import { makePaginatedResponseQueryOptions } from '@/queries/utils/make-paginated-response-query-options'; -import { regexFromFilters } from '@/selectors'; import { useSettingsStore } from '@/stores/settings'; import { compareId } from '@/utils/comparators'; +import { regexFromFilters } from '@/utils/filters'; import { unescapeHTML } from '@/utils/html'; import { EXCLUDE_TYPES, NOTIFICATION_TYPES } from '@/utils/notification'; import { play, soundCache } from '@/utils/sounds'; diff --git a/packages/nicolium/src/reducers/admin.ts b/packages/nicolium/src/reducers/admin.ts deleted file mode 100644 index ca3d36607..000000000 --- a/packages/nicolium/src/reducers/admin.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { create } from 'mutative'; - -import { - ADMIN_CONFIG_FETCH_SUCCESS, - ADMIN_CONFIG_UPDATE_SUCCESS, - // ADMIN_USER_DELETE_SUCCESS, - type AdminActions, -} from '@/actions/admin'; - -import type { Config } from '@/utils/config-db'; - -interface State { - configs: Array; - needsReboot: boolean; -} - -const initialState: State = { - configs: [], - needsReboot: false, -}; - -const importConfigs = (state: State, configs: Array) => { - state.configs = configs; -}; - -const admin = (state = initialState, action: AdminActions): State => { - switch (action.type) { - case ADMIN_CONFIG_FETCH_SUCCESS: - case ADMIN_CONFIG_UPDATE_SUCCESS: - return create(state, (draft) => { - importConfigs(draft, action.configs); - }); - default: - return state; - } -}; - -export { admin as default }; diff --git a/packages/nicolium/src/reducers/frontend-config.ts b/packages/nicolium/src/reducers/frontend-config.ts index 84911b9a6..ea8ce9f91 100644 --- a/packages/nicolium/src/reducers/frontend-config.ts +++ b/packages/nicolium/src/reducers/frontend-config.ts @@ -23,7 +23,7 @@ const fallbackState: PartialFrontendConfig = { const updateFromAdmin = (state: Record, configs: PleromaConfig['configs']) => { try { return ConfigDB.find(configs, ':pleroma', ':frontend_configurations')!.value.find( - (value: Record) => value.tuple?.[0] === ':pl_fe', + (value: Record) => value.tuple?.[0] === ':nicolium', ).tuple?.[1]; } catch { return state; @@ -77,7 +77,7 @@ const frontendConfig = ( case FRONTEND_CONFIG_REQUEST_FAIL: return { ...fallbackState, ...state }; case ADMIN_CONFIG_UPDATE_SUCCESS: - return parseFrontendConfig(updateFromAdmin(state, action.configs ?? [])) || state; + return parseFrontendConfig(updateFromAdmin(state, action.configs)) || state; default: return state; } diff --git a/packages/nicolium/src/reducers/index.ts b/packages/nicolium/src/reducers/index.ts index b911627bd..80b47a9c0 100644 --- a/packages/nicolium/src/reducers/index.ts +++ b/packages/nicolium/src/reducers/index.ts @@ -3,7 +3,6 @@ import { combineReducers } from '@reduxjs/toolkit'; import { AUTH_LOGGED_OUT } from '@/actions/auth'; import * as BuildConfig from '@/build-config'; -import admin from './admin'; import auth from './auth'; import frontendConfig from './frontend-config'; import instance from './instance'; @@ -12,7 +11,6 @@ import meta from './meta'; import pushNotifications from './push-notifications'; const reducers = { - admin, auth, frontendConfig, instance, diff --git a/packages/nicolium/src/reducers/instance.ts b/packages/nicolium/src/reducers/instance.ts index dee3ff10b..df7d92543 100644 --- a/packages/nicolium/src/reducers/instance.ts +++ b/packages/nicolium/src/reducers/instance.ts @@ -2,11 +2,7 @@ import { create } from 'mutative'; import { type Instance, instanceSchema, type PleromaConfig } from 'pl-api'; import * as v from 'valibot'; -import { - ADMIN_CONFIG_UPDATE_REQUEST, - ADMIN_CONFIG_UPDATE_SUCCESS, - type AdminActions, -} from '@/actions/admin'; +import { ADMIN_CONFIG_UPDATE_SUCCESS, type AdminActions } from '@/actions/admin'; import { INSTANCE_FETCH_FAIL, INSTANCE_FETCH_SUCCESS, @@ -105,7 +101,6 @@ const instance = ( return { fetched: true, ...action.instance }; case INSTANCE_FETCH_FAIL: return handleInstanceFetchFail(state, action.error); - case ADMIN_CONFIG_UPDATE_REQUEST: case ADMIN_CONFIG_UPDATE_SUCCESS: return create(state, (draft) => importConfigs(draft, action.configs)); default: diff --git a/packages/nicolium/src/selectors/index.ts b/packages/nicolium/src/selectors/index.ts index 36d7ded2d..c6667da62 100644 --- a/packages/nicolium/src/selectors/index.ts +++ b/packages/nicolium/src/selectors/index.ts @@ -1,35 +1,36 @@ -import { createSelector } from 'reselect'; +import { useQueryClient } from '@tanstack/react-query'; -import { getAccounts } from '@/queries/accounts/selectors'; +import { useInstance } from '@/hooks/use-instance'; +import { useAdminConfig } from '@/queries/admin/use-config'; +import { queryKeys } from '@/queries/keys'; import { getDomain } from '@/utils/accounts'; import ConfigDB from '@/utils/config-db'; -import { regexFromFilters } from '@/utils/filters'; import type { MRFSimple } from '@/schemas/pleroma'; -import type { RootState } from '@/store'; - -const getSimplePolicy = createSelector( - [ - (state: RootState) => state.admin.configs, - (state: RootState) => state.instance.pleroma.metadata.federation.mrf_simple_info, - ], - (configs, instancePolicy): MRFSimple => ({ - ...instancePolicy, - ...ConfigDB.toSimplePolicy(configs), - }), -); - -const getRemoteInstanceFavicon = (_state: RootState, host: string) => { - const account = getAccounts().find((item) => item && getDomain(item) === host); - return account?.favicon ?? null; -}; +import type { Account } from 'pl-api'; type HostFederation = { [key in keyof MRFSimple]: boolean; }; -const getRemoteInstanceFederation = (state: RootState, host: string): HostFederation => { - const simplePolicy = getSimplePolicy(state); +interface RemoteInstance { + host: string; + favicon: string | null; + federation: HostFederation; +} + +const useSimplePolicy = () => { + const { data: config } = useAdminConfig(); + const simplePolicy = useInstance().pleroma.metadata.federation.mrf_simple_info; + + return { + ...simplePolicy, + ...ConfigDB.toSimplePolicy(config?.configs || []), + }; +}; + +const useRemoteInstanceFederation = (host: string) => { + const simplePolicy = useSimplePolicy(); return Object.fromEntries( Object.entries(simplePolicy).map(([key, hosts]) => [ @@ -39,33 +40,37 @@ const getRemoteInstanceFederation = (state: RootState, host: string): HostFedera ) as HostFederation; }; -const makeGetHosts = () => - createSelector([getSimplePolicy], (simplePolicy) => { - const { accept, reject_deletes, report_removal, ...rest } = simplePolicy; +const useRemoteInstanceFavicon = (host: string) => { + const queryClient = useQueryClient(); - return [ - ...new Set(Object.values(rest).reduce((acc, hosts) => (acc.push(...hosts), acc), [])), - ].toSorted(); - }); - -interface RemoteInstance { - host: string; - favicon: string | null; - federation: HostFederation; -} - -const makeGetRemoteInstance = () => - createSelector( - [ - (_state: RootState, host: string) => host, - getRemoteInstanceFavicon, - getRemoteInstanceFederation, - ], - (host, favicon, federation): RemoteInstance => ({ - host, - favicon, - federation, - }), + return ( + queryClient + .getQueriesData({ queryKey: queryKeys.accounts.root }) + .map(([, account]) => account) + .find( + (account): account is Account => + typeof account?.id === 'string' && getDomain(account) === host, + )?.favicon ?? null ); +}; -export { type RemoteInstance, regexFromFilters, makeGetHosts, makeGetRemoteInstance }; +const useRemoteInstance = (host: string) => { + const federation = useRemoteInstanceFederation(host); + const favicon = useRemoteInstanceFavicon(host); + + return { + host, + favicon, + federation, + }; +}; + +const useHosts = () => { + const simplePolicy = useSimplePolicy(); + + return [ + ...new Set(Object.values(simplePolicy).reduce((acc, hosts) => (acc.push(...hosts), acc), [])), + ].toSorted(); +}; + +export { type RemoteInstance, useHosts, useRemoteInstance };