mirror of
https://github.com/Sonarr/Sonarr
synced 2026-05-03 18:41:35 +02:00
Use react-query for UI settings
This commit is contained in:
parent
e9011011ed
commit
74e6ce4305
32 changed files with 264 additions and 263 deletions
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
|
||||
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
|
||||
import Link from 'Components/Link/Link';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import {
|
||||
DownloadFailedHistory,
|
||||
DownloadFolderImportedHistory,
|
||||
|
|
@ -33,9 +32,7 @@ interface HistoryDetailsProps {
|
|||
function HistoryDetails(props: HistoryDetailsProps) {
|
||||
const { eventType, sourceTitle, data, downloadId } = props;
|
||||
|
||||
const { shortDateFormat, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { shortDateFormat, timeFormat } = useUiSettingsValues();
|
||||
|
||||
if (eventType === 'grabbed') {
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
||||
import { useSelect } from 'App/Select/SelectContext';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
|
|
@ -22,7 +21,7 @@ import Language from 'Language/Language';
|
|||
import { QualityModel } from 'Quality/Quality';
|
||||
import SeriesTitleLink from 'Series/SeriesTitleLink';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import CustomFormat from 'typings/CustomFormat';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import Queue, {
|
||||
|
|
@ -107,9 +106,8 @@ function QueueRow(props: QueueRowProps) {
|
|||
|
||||
const series = useSingleSeries(seriesId);
|
||||
const episodes = useEpisodesWithIds(episodeIds);
|
||||
const { showRelativeDates, shortDateFormat, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { showRelativeDates, shortDateFormat, timeFormat } =
|
||||
useUiSettingsValues();
|
||||
const { removeQueueItem, isRemoving } = useRemoveQueueItem(id);
|
||||
const { grabQueueItem, isGrabbing, grabError } = useGrabQueueItem(id);
|
||||
const { toggleSelected, useIsSelected } = useSelect<Queue>();
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import NamingConfig from 'typings/Settings/NamingConfig';
|
|||
import NamingExample from 'typings/Settings/NamingExample';
|
||||
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||
import RemotePathMapping from 'typings/Settings/RemotePathMapping';
|
||||
import UiSettings from 'typings/Settings/UiSettings';
|
||||
import MetadataAppState from './MetadataAppState';
|
||||
|
||||
type Presets<T> = T & {
|
||||
|
|
@ -156,7 +155,6 @@ export interface RemotePathMappingsAppState
|
|||
|
||||
export type IndexerFlagSettingsAppState = AppSectionState<IndexerFlag>;
|
||||
export type LanguageSettingsAppState = AppSectionState<Language>;
|
||||
export type UiSettingsAppState = AppSectionItemState<UiSettings>;
|
||||
|
||||
interface SettingsAppState {
|
||||
autoTaggings: AutoTaggingAppState;
|
||||
|
|
@ -183,7 +181,6 @@ interface SettingsAppState {
|
|||
qualityProfiles: QualityProfilesAppState;
|
||||
releaseProfiles: ReleaseProfilesAppState;
|
||||
remotePathMappings: RemotePathMappingsAppState;
|
||||
ui: UiSettingsAppState;
|
||||
}
|
||||
|
||||
export default SettingsAppState;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useQueueItemForEpisode } from 'Activity/Queue/Details/QueueDetailsProvider';
|
||||
import { useCalendarOptions } from 'Calendar/calendarOptionsStore';
|
||||
import CalendarEventQueueDetails from 'Calendar/Events/CalendarEventQueueDetails';
|
||||
|
|
@ -13,7 +12,7 @@ import getFinaleTypeName from 'Episode/getFinaleTypeName';
|
|||
import { useEpisodeFile } from 'EpisodeFile/EpisodeFileProvider';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { convertToTimezone } from 'Utilities/Date/convertToTimezone';
|
||||
import formatTime from 'Utilities/Date/formatTime';
|
||||
import padNumber from 'Utilities/Number/padNumber';
|
||||
|
|
@ -59,7 +58,7 @@ function AgendaEvent(props: AgendaEventProps) {
|
|||
const episodeFile = useEpisodeFile(episodeFileId);
|
||||
const queueItem = useQueueItemForEpisode(id);
|
||||
const { timeFormat, longDateFormat, enableColorImpairedMode, timeZone } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
|
||||
const {
|
||||
showEpisodeInformation,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import moment from 'moment';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCalendarOption } from 'Calendar/calendarOptionsStore';
|
||||
import * as calendarViews from 'Calendar/calendarViews';
|
||||
import { useCalendarDates } from 'Calendar/useCalendar';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import DayOfWeek from './DayOfWeek';
|
||||
import styles from './DaysOfWeek.css';
|
||||
|
||||
|
|
@ -12,7 +11,7 @@ function DaysOfWeek() {
|
|||
const view = useCalendarOption('view');
|
||||
const dates = useCalendarDates();
|
||||
const { calendarWeekColumnHeader, shortDateFormat, showRelativeDates } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
|
||||
const updateTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const [todaysDate, setTodaysDate] = useState(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useQueueItemForEpisode } from 'Activity/Queue/Details/QueueDetailsProvider';
|
||||
import { useCalendarOptions } from 'Calendar/calendarOptionsStore';
|
||||
import getStatusStyle from 'Calendar/getStatusStyle';
|
||||
|
|
@ -12,7 +11,7 @@ import getFinaleTypeName from 'Episode/getFinaleTypeName';
|
|||
import { useEpisodeFile } from 'EpisodeFile/EpisodeFileProvider';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { convertToTimezone } from 'Utilities/Date/convertToTimezone';
|
||||
import formatTime from 'Utilities/Date/formatTime';
|
||||
import padNumber from 'Utilities/Number/padNumber';
|
||||
|
|
@ -60,9 +59,8 @@ function CalendarEvent(props: CalendarEventProps) {
|
|||
const episodeFile = useEpisodeFile(episodeFileId);
|
||||
const queueItem = useQueueItemForEpisode(id);
|
||||
|
||||
const { timeFormat, enableColorImpairedMode, timeZone } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { timeFormat, enableColorImpairedMode, timeZone } =
|
||||
useUiSettingsValues();
|
||||
|
||||
const {
|
||||
showEpisodeInformation,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useIsDownloadingEpisodes } from 'Activity/Queue/Details/QueueDetailsProvider';
|
||||
import { useCalendarOptions } from 'Calendar/calendarOptionsStore';
|
||||
import getStatusStyle from 'Calendar/getStatusStyle';
|
||||
|
|
@ -9,7 +8,7 @@ import Link from 'Components/Link/Link';
|
|||
import getFinaleTypeName from 'Episode/getFinaleTypeName';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { CalendarItem } from 'typings/Calendar';
|
||||
import { convertToTimezone } from 'Utilities/Date/convertToTimezone';
|
||||
import formatTime from 'Utilities/Date/formatTime';
|
||||
|
|
@ -34,9 +33,8 @@ function CalendarEventGroup({
|
|||
const isDownloading = useIsDownloadingEpisodes(episodeIds);
|
||||
const series = useSingleSeries(seriesId)!;
|
||||
|
||||
const { timeFormat, enableColorImpairedMode, timeZone } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { timeFormat, enableColorImpairedMode, timeZone } =
|
||||
useUiSettingsValues();
|
||||
|
||||
const { showEpisodeInformation, showFinaleIcon, fullColorEvents } =
|
||||
useCalendarOptions();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import moment from 'moment';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useAppDimensions } from 'App/appStore';
|
||||
import {
|
||||
setCalendarOption,
|
||||
|
|
@ -22,7 +21,7 @@ import MenuButton from 'Components/Menu/MenuButton';
|
|||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import ViewMenuItem from 'Components/Menu/ViewMenuItem';
|
||||
import { align, icons } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import CalendarHeaderViewButton from './CalendarHeaderViewButton';
|
||||
import styles from './CalendarHeader.css';
|
||||
|
|
@ -35,7 +34,7 @@ function CalendarHeader() {
|
|||
|
||||
const { isSmallScreen, isLargeScreen } = useAppDimensions();
|
||||
|
||||
const { longDateFormat } = useSelector(createUISettingsSelector());
|
||||
const { longDateFormat } = useUiSettingsValues();
|
||||
|
||||
const handleViewChange = useCallback((newView: string) => {
|
||||
setCalendarOption('view', newView as CalendarView);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import {
|
||||
useCalendarOption,
|
||||
useCalendarOptions,
|
||||
} from 'Calendar/calendarOptionsStore';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import LegendIconItem from './LegendIconItem';
|
||||
import LegendItem from './LegendItem';
|
||||
|
|
@ -19,7 +18,7 @@ function Legend() {
|
|||
showCutoffUnmetIcon,
|
||||
fullColorEvents,
|
||||
} = useCalendarOptions();
|
||||
const { enableColorImpairedMode } = useSelector(createUISettingsSelector());
|
||||
const { enableColorImpairedMode } = useUiSettingsValues();
|
||||
|
||||
const iconsToShow = [];
|
||||
const isAgendaView = view === 'agenda';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
CalendarOptions,
|
||||
setCalendarOption,
|
||||
|
|
@ -22,10 +21,12 @@ import {
|
|||
timeFormatOptions,
|
||||
weekColumnOptions,
|
||||
} from 'Settings/UI/UISettings';
|
||||
import { saveUISettings } from 'Store/Actions/settingsActions';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import {
|
||||
UiSettingsModel,
|
||||
useSaveUiSettings,
|
||||
useUiSettingsValues,
|
||||
} from 'Settings/UI/useUiSettings';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import UiSettings from 'typings/Settings/UiSettings';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
interface CalendarOptionsModalContentProps {
|
||||
|
|
@ -35,8 +36,6 @@ interface CalendarOptionsModalContentProps {
|
|||
function CalendarOptionsModalContent({
|
||||
onModalClose,
|
||||
}: CalendarOptionsModalContentProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
collapseMultipleEpisodes,
|
||||
showEpisodeInformation,
|
||||
|
|
@ -46,9 +45,10 @@ function CalendarOptionsModalContent({
|
|||
fullColorEvents,
|
||||
} = useCalendarOptions();
|
||||
|
||||
const uiSettings = useSelector(createUISettingsSelector());
|
||||
const uiSettings = useUiSettingsValues();
|
||||
const saveUiSettings = useSaveUiSettings();
|
||||
|
||||
const [state, setState] = useState<Partial<UiSettings>>({
|
||||
const [state, setState] = useState<Partial<UiSettingsModel>>({
|
||||
firstDayOfWeek: uiSettings.firstDayOfWeek,
|
||||
calendarWeekColumnHeader: uiSettings.calendarWeekColumnHeader,
|
||||
timeFormat: uiSettings.timeFormat,
|
||||
|
|
@ -73,9 +73,9 @@ function CalendarOptionsModalContent({
|
|||
({ name, value }: InputChanged) => {
|
||||
setState((prevState) => ({ ...prevState, [name]: value }));
|
||||
|
||||
dispatch(saveUISettings({ [name]: value }));
|
||||
saveUiSettings({ [name]: value });
|
||||
},
|
||||
[dispatch]
|
||||
[saveUiSettings]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { keepPreviousData } from '@tanstack/react-query';
|
||||
import moment from 'moment';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { create } from 'zustand';
|
||||
import AppState from 'App/State/AppState';
|
||||
import { setEpisodeQueryKey } from 'Episode/useEpisode';
|
||||
import { Filter, FilterBuilderProp } from 'Filters/Filter';
|
||||
import { useCustomFiltersList } from 'Filters/useCustomFilters';
|
||||
import useApiQuery from 'Helpers/Hooks/useApiQuery';
|
||||
import { filterBuilderValueTypes } from 'Helpers/Props';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { CalendarItem } from 'typings/Calendar';
|
||||
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
|
@ -156,9 +155,7 @@ export const useCalendarPage = () => {
|
|||
const dayCount = useCalendarDayCount();
|
||||
const time = useCalendarTime();
|
||||
const view = useCalendarOption('view');
|
||||
const firstDayOfWeek = useSelector(
|
||||
(state: AppState) => state.settings.ui.item.firstDayOfWeek
|
||||
);
|
||||
const { firstDayOfWeek } = useUiSettingsValues();
|
||||
|
||||
useEffect(() => {
|
||||
const { dates } = getDates(time, view, firstDayOfWeek, dayCount);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ interface ErrorPageProps {
|
|||
customFiltersError: ApiError | null;
|
||||
tagsError: ApiError | null;
|
||||
qualityProfilesError?: Error;
|
||||
uiSettingsError?: Error;
|
||||
uiSettingsError: ApiError | null;
|
||||
systemStatusError: ApiError | null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { saveDimensions, useAppValue } from 'App/appStore';
|
||||
import AppUpdatedModal from 'App/AppUpdatedModal';
|
||||
import ColorImpairedContext from 'App/ColorImpairedContext';
|
||||
|
|
@ -7,7 +6,7 @@ import ConnectionLostModal from 'App/ConnectionLostModal';
|
|||
import SignalRListener from 'Components/SignalRListener';
|
||||
import AuthenticationRequiredModal from 'FirstRun/AuthenticationRequiredModal';
|
||||
import useAppPage from 'Helpers/Hooks/useAppPage';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { useSystemStatusData } from 'System/Status/useSystemStatus';
|
||||
import ErrorPage from './ErrorPage';
|
||||
import PageHeader from './Header/PageHeader';
|
||||
|
|
@ -29,7 +28,7 @@ function Page({ children }: PageProps) {
|
|||
const [isConnectionLostModalOpen, setIsConnectionLostModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const { enableColorImpairedMode } = useSelector(createUISettingsSelector());
|
||||
const { enableColorImpairedMode } = useUiSettingsValues();
|
||||
const { authentication } = useSystemStatusData();
|
||||
|
||||
const authenticationEnabled = authentication !== 'none';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import TableRowCell from './TableRowCell';
|
||||
|
|
@ -25,7 +24,7 @@ function RelativeDateCell(props: RelativeDateCellProps) {
|
|||
} = props;
|
||||
|
||||
const { showRelativeDates, shortDateFormat, longDateFormat, timeFormat } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
|
||||
if (!date) {
|
||||
return <Component className={className} {...otherProps} />;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Label from 'Components/Label';
|
||||
import { kinds, sizes } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatTime from 'Utilities/Date/formatTime';
|
||||
import isInNextWeek from 'Utilities/Date/isInNextWeek';
|
||||
import isToday from 'Utilities/Date/isToday';
|
||||
|
|
@ -18,9 +17,8 @@ interface EpisodeAiringProps {
|
|||
function EpisodeAiring(props: EpisodeAiringProps) {
|
||||
const { airDateUtc, network } = props;
|
||||
|
||||
const { shortDateFormat, showRelativeDates, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { shortDateFormat, showRelativeDates, timeFormat } =
|
||||
useUiSettingsValues();
|
||||
|
||||
const networkLabel = (
|
||||
<Label kind={kinds.INFO} size={sizes.MEDIUM}>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { useTranslations } from 'App/useTranslations';
|
|||
import useCommands from 'Commands/useCommands';
|
||||
import useCustomFilters from 'Filters/useCustomFilters';
|
||||
import useSeries from 'Series/useSeries';
|
||||
import { useUiSettings } from 'Settings/UI/useUiSettings';
|
||||
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
||||
import {
|
||||
fetchImportLists,
|
||||
fetchIndexerFlags,
|
||||
fetchLanguages,
|
||||
fetchQualityProfiles,
|
||||
fetchUISettings,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import useSystemStatus from 'System/Status/useSystemStatus';
|
||||
import useTags from 'Tags/useTags';
|
||||
|
|
@ -23,22 +23,22 @@ const createErrorsSelector = ({
|
|||
systemStatusError,
|
||||
tagsError,
|
||||
translationsError,
|
||||
uiSettingsError,
|
||||
seriesError,
|
||||
}: {
|
||||
customFiltersError: ApiError | null;
|
||||
systemStatusError: ApiError | null;
|
||||
tagsError: ApiError | null;
|
||||
translationsError: ApiError | null;
|
||||
uiSettingsError: ApiError | null;
|
||||
seriesError: ApiError | null;
|
||||
}) =>
|
||||
createSelector(
|
||||
(state: AppState) => state.settings.ui.error,
|
||||
(state: AppState) => state.settings.qualityProfiles.error,
|
||||
(state: AppState) => state.settings.languages.error,
|
||||
(state: AppState) => state.settings.importLists.error,
|
||||
(state: AppState) => state.settings.indexerFlags.error,
|
||||
(
|
||||
uiSettingsError,
|
||||
qualityProfilesError,
|
||||
languagesError,
|
||||
importListsError,
|
||||
|
|
@ -54,7 +54,8 @@ const createErrorsSelector = ({
|
|||
indexerFlagsError ||
|
||||
systemStatusError ||
|
||||
tagsError ||
|
||||
translationsError
|
||||
translationsError ||
|
||||
uiSettingsError
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -93,9 +94,11 @@ const useAppPage = () => {
|
|||
const { isFetched: isTranslationsFetched, error: translationsError } =
|
||||
useTranslations();
|
||||
|
||||
const { isFetched: isUiSettingsFetched, error: uiSettingsError } =
|
||||
useUiSettings();
|
||||
|
||||
const isAppStatePopulated = useSelector(
|
||||
(state: AppState) =>
|
||||
state.settings.ui.isPopulated &&
|
||||
state.settings.qualityProfiles.isPopulated &&
|
||||
state.settings.languages.isPopulated &&
|
||||
state.settings.importLists.isPopulated &&
|
||||
|
|
@ -108,7 +111,8 @@ const useAppPage = () => {
|
|||
isSeriesFetched &&
|
||||
isSystemStatusFetched &&
|
||||
isTagsFetched &&
|
||||
isTranslationsFetched;
|
||||
isTranslationsFetched &&
|
||||
isUiSettingsFetched;
|
||||
|
||||
const { hasError, errors } = useSelector(
|
||||
createErrorsSelector({
|
||||
|
|
@ -117,6 +121,7 @@ const useAppPage = () => {
|
|||
systemStatusError,
|
||||
tagsError,
|
||||
translationsError,
|
||||
uiSettingsError,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -139,7 +144,6 @@ const useAppPage = () => {
|
|||
dispatch(fetchLanguages());
|
||||
dispatch(fetchImportLists());
|
||||
dispatch(fetchIndexerFlags());
|
||||
dispatch(fetchUISettings());
|
||||
}, [dispatch]);
|
||||
|
||||
return useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useMemo, useRef } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { create, useStore } from 'zustand';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
|
|
@ -14,25 +14,17 @@ interface PendingChangesStore<T extends object> {
|
|||
export const usePendingChangesStore = <T extends object>(
|
||||
initialPendingChanges: Partial<T>
|
||||
) => {
|
||||
const store = useRef(
|
||||
create<PendingChangesStore<T>>()((_set) => {
|
||||
// eslint-disable-next-line react/hook-use-state
|
||||
const [store] = useState(() => {
|
||||
return create<PendingChangesStore<T>>()((_set) => {
|
||||
return {
|
||||
pendingChanges: initialPendingChanges,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const usePendingChanges = () => {
|
||||
return useStore(
|
||||
store.current,
|
||||
useShallow((state) => {
|
||||
return state.pendingChanges as Partial<T>;
|
||||
})
|
||||
);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const setPendingChange = <K extends keyof T>(key: K, value: T[K]) => {
|
||||
store.current.setState((state) => ({
|
||||
store.setState((state) => ({
|
||||
...state,
|
||||
pendingChanges: {
|
||||
...state.pendingChanges,
|
||||
|
|
@ -41,21 +33,31 @@ export const usePendingChangesStore = <T extends object>(
|
|||
}));
|
||||
};
|
||||
|
||||
const setPendingChanges = (changes: Partial<T>) => {
|
||||
store.current.setState((state) => ({
|
||||
const unsetPendingChange = <K extends keyof T>(key: K) => {
|
||||
store.setState((state) => {
|
||||
const newPendingChanges = { ...state.pendingChanges };
|
||||
delete newPendingChanges[key];
|
||||
|
||||
return {
|
||||
...state,
|
||||
pendingChanges: newPendingChanges,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const clearPendingChanges = () => {
|
||||
store.setState((state) => ({
|
||||
...state,
|
||||
pendingChanges: {
|
||||
...state.pendingChanges,
|
||||
...changes,
|
||||
},
|
||||
pendingChanges: {},
|
||||
}));
|
||||
};
|
||||
|
||||
const discardPendingChanges = () => {
|
||||
return setPendingChanges({} as Partial<T>);
|
||||
};
|
||||
|
||||
const pendingChanges = usePendingChanges();
|
||||
const pendingChanges = useStore(
|
||||
store,
|
||||
useShallow((state) => {
|
||||
return state.pendingChanges as Partial<T>;
|
||||
})
|
||||
);
|
||||
|
||||
const hasPendingChanges = useMemo(() => {
|
||||
return Object.keys(pendingChanges).length > 0;
|
||||
|
|
@ -65,7 +67,8 @@ export const usePendingChangesStore = <T extends object>(
|
|||
store,
|
||||
pendingChanges,
|
||||
setPendingChange,
|
||||
discardPendingChanges,
|
||||
unsetPendingChange,
|
||||
clearPendingChanges,
|
||||
hasPendingChanges,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import themes from 'Styles/Themes';
|
||||
|
||||
function createThemeSelector() {
|
||||
return createSelector(
|
||||
(state: AppState) => state.settings.ui.item.theme || window.Sonarr.theme,
|
||||
(theme) => theme
|
||||
);
|
||||
}
|
||||
|
||||
const useTheme = () => {
|
||||
const selectedTheme = useSelector(createThemeSelector());
|
||||
const { theme } = useUiSettingsValues();
|
||||
const selectedTheme = theme ?? window.Sonarr.theme;
|
||||
const [resolvedTheme, setResolvedTheme] = useState(selectedTheme);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
|
|
@ -14,7 +13,7 @@ import EpisodeLanguages from 'Episode/EpisodeLanguages';
|
|||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||
import IndexerFlags from 'Episode/IndexerFlags';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import formatAge from 'Utilities/Number/formatAge';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
|
|
@ -122,9 +121,7 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
|
|||
protocol,
|
||||
} = release;
|
||||
|
||||
const { longDateFormat, timeFormat, timeZone } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { longDateFormat, timeFormat, timeZone } = useUiSettingsValues();
|
||||
|
||||
const [isConfirmGrabModalOpen, setIsConfirmGrabModalOpen] = useState(false);
|
||||
const [isOverrideModalOpen, setIsOverrideModalOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { IconName } from 'Components/Icon';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import {
|
||||
UiSettingsModel,
|
||||
useUiSettingsValues,
|
||||
} from 'Settings/UI/useUiSettings';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import QualityProfile from 'typings/QualityProfile';
|
||||
import UiSettings from 'typings/Settings/UiSettings';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
|
|
@ -95,7 +96,7 @@ const rows = [
|
|||
function getInfoRowProps(
|
||||
row: RowProps,
|
||||
props: SeriesIndexOverviewInfoProps,
|
||||
uiSettings: UiSettings
|
||||
uiSettings: UiSettingsModel
|
||||
): RowInfoProps | null {
|
||||
const { name } = row;
|
||||
|
||||
|
|
@ -209,8 +210,7 @@ function getInfoRowProps(
|
|||
function SeriesIndexOverviewInfo(props: SeriesIndexOverviewInfoProps) {
|
||||
const { height, nextAiring } = props;
|
||||
|
||||
const uiSettings = useSelector(createUISettingsSelector());
|
||||
|
||||
const uiSettings = useUiSettingsValues();
|
||||
const { shortDateFormat, showRelativeDates, longDateFormat, timeFormat } =
|
||||
uiSettings;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import CommandNames from 'Commands/CommandNames';
|
||||
import { useExecuteCommand } from 'Commands/useCommands';
|
||||
import Label from 'Components/Label';
|
||||
|
|
@ -16,7 +15,7 @@ import SeriesIndexPosterSelect from 'Series/Index/Select/SeriesIndexPosterSelect
|
|||
import { Statistics } from 'Series/Series';
|
||||
import { useSeriesPosterOptions } from 'Series/seriesOptionsStore';
|
||||
import SeriesPoster from 'Series/SeriesPoster';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
|
@ -48,7 +47,7 @@ function SeriesIndexPoster(props: SeriesIndexPosterProps) {
|
|||
} = useSeriesPosterOptions();
|
||||
|
||||
const { showRelativeDates, shortDateFormat, longDateFormat, timeFormat } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
|
||||
const executeCommand = useExecuteCommand();
|
||||
const [hasPosterError, setHasPosterError] = useState(false);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
|
|
@ -12,20 +12,13 @@ import PageContent from 'Components/Page/PageContent';
|
|||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import {
|
||||
fetchUISettings,
|
||||
saveUISettings,
|
||||
setUISettingsValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import themes from 'Styles/Themes';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import timeZoneOptions from 'Utilities/Date/timeZoneOptions';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
const SECTION = 'ui';
|
||||
import { useManageUiSettings } from './useUiSettings';
|
||||
|
||||
export const firstDayOfWeekOptions: EnhancedSelectInputValue<number>[] = [
|
||||
{
|
||||
|
|
@ -69,8 +62,6 @@ export const timeFormatOptions: EnhancedSelectInputValue<string>[] = [
|
|||
];
|
||||
|
||||
function UISettings() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
items,
|
||||
isFetching: isLanguagesFetching,
|
||||
|
|
@ -86,15 +77,17 @@ function UISettings() {
|
|||
|
||||
const {
|
||||
isFetching: isSettingsFetching,
|
||||
isPopulated: isSettingsPopulated,
|
||||
isFetched: isSettingsPopulated,
|
||||
error: settingsError,
|
||||
hasPendingChanges,
|
||||
hasSettings,
|
||||
settings,
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
validationErrors,
|
||||
validationWarnings,
|
||||
} = useSelector(createSettingsSectionSelector(SECTION));
|
||||
saveSettings,
|
||||
updateSetting,
|
||||
} = useManageUiSettings();
|
||||
|
||||
const isFetching = isLanguagesFetching || isSettingsFetching;
|
||||
const isPopulated = isLanguagesPopulated && isSettingsPopulated;
|
||||
|
|
@ -116,23 +109,15 @@ function UISettings() {
|
|||
|
||||
const handleInputChange = useCallback(
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error - actions aren't typed
|
||||
dispatch(setUISettingsValue(change));
|
||||
// @ts-expect-error name needs to be keyof UiSettingsModel
|
||||
updateSetting(change.name, change.value);
|
||||
},
|
||||
[dispatch]
|
||||
[updateSetting]
|
||||
);
|
||||
|
||||
const handleSavePress = useCallback(() => {
|
||||
dispatch(saveUISettings());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchUISettings());
|
||||
|
||||
return () => {
|
||||
// @ts-expect-error - actions aren't typed
|
||||
dispatch(setUISettingsValue({ section: `settings.${SECTION}` }));
|
||||
};
|
||||
}, [dispatch]);
|
||||
saveSettings();
|
||||
}, [saveSettings]);
|
||||
|
||||
return (
|
||||
<PageContent title={translate('UiSettings')}>
|
||||
|
|
|
|||
54
frontend/src/Settings/UI/useUiSettings.ts
Normal file
54
frontend/src/Settings/UI/useUiSettings.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { useCallback } from 'react';
|
||||
import {
|
||||
useManageSettings,
|
||||
useSaveSettings,
|
||||
useSettings,
|
||||
} from 'Settings/useSettings';
|
||||
|
||||
export interface UiSettingsModel {
|
||||
theme: 'auto' | 'dark' | 'light';
|
||||
showRelativeDates: boolean;
|
||||
shortDateFormat: string;
|
||||
longDateFormat: string;
|
||||
timeFormat: string;
|
||||
timeZone: string;
|
||||
firstDayOfWeek: number;
|
||||
enableColorImpairedMode: boolean;
|
||||
calendarWeekColumnHeader: string;
|
||||
uiLanguage: number;
|
||||
}
|
||||
|
||||
const PATH = '/settings/ui';
|
||||
|
||||
export const useUiSettingsValues = () => {
|
||||
const { data } = useSettings<UiSettingsModel>(PATH);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useUiSettings = () => {
|
||||
return useSettings<UiSettingsModel>(PATH);
|
||||
};
|
||||
|
||||
export const useManageUiSettings = () => {
|
||||
return useManageSettings<UiSettingsModel>(PATH);
|
||||
};
|
||||
|
||||
export const useSaveUiSettings = () => {
|
||||
const { data } = useSettings<UiSettingsModel>(PATH);
|
||||
const { save } = useSaveSettings<UiSettingsModel>(PATH);
|
||||
|
||||
const saveSettings = useCallback(
|
||||
(changes: Partial<UiSettingsModel>) => {
|
||||
const updatedSettings = {
|
||||
...data,
|
||||
...changes,
|
||||
};
|
||||
|
||||
save(updatedSettings);
|
||||
},
|
||||
[data, save]
|
||||
);
|
||||
|
||||
return saveSettings;
|
||||
};
|
||||
91
frontend/src/Settings/useSettings.ts
Normal file
91
frontend/src/Settings/useSettings.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import useApiMutation from 'Helpers/Hooks/useApiMutation';
|
||||
import useApiQuery from 'Helpers/Hooks/useApiQuery';
|
||||
import { usePendingChangesStore } from 'Helpers/Hooks/usePendingChangesStore';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
|
||||
export const useSettings = <T extends object>(path: string) => {
|
||||
const result = useApiQuery<T>({
|
||||
path,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: result.data ?? ({} as T),
|
||||
};
|
||||
};
|
||||
|
||||
export const useSaveSettings = <T extends object>(
|
||||
path: string,
|
||||
onSuccess?: () => void
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutate, isPending, error } = useApiMutation<T, T>({
|
||||
path,
|
||||
method: 'PUT',
|
||||
mutationOptions: {
|
||||
onSuccess: (updatedSettings: T) => {
|
||||
queryClient.setQueryData<T>([path], updatedSettings);
|
||||
onSuccess?.();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
save: mutate,
|
||||
isSaving: isPending,
|
||||
saveError: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const useManageSettings = <T extends object>(path: string) => {
|
||||
const { data, isFetching, isFetched, error } = useSettings<T>(path);
|
||||
const {
|
||||
pendingChanges,
|
||||
setPendingChange,
|
||||
unsetPendingChange,
|
||||
clearPendingChanges,
|
||||
} = usePendingChangesStore<T>({});
|
||||
|
||||
const { save, isSaving, saveError } = useSaveSettings<T>(
|
||||
path,
|
||||
clearPendingChanges
|
||||
);
|
||||
|
||||
const settings = useMemo(() => {
|
||||
return selectSettings<T>(data, pendingChanges, saveError);
|
||||
}, [data, pendingChanges, saveError]);
|
||||
|
||||
const saveSettings = useCallback(() => {
|
||||
const updatedSettings = {
|
||||
...data,
|
||||
...pendingChanges,
|
||||
};
|
||||
|
||||
save(updatedSettings);
|
||||
}, [data, pendingChanges, save]);
|
||||
|
||||
const updateSetting = useCallback(
|
||||
<K extends keyof T>(key: K, value: T[K]) => {
|
||||
if (data[key] === value) {
|
||||
unsetPendingChange(key);
|
||||
} else {
|
||||
setPendingChange(key, value);
|
||||
}
|
||||
},
|
||||
[data, setPendingChange, unsetPendingChange]
|
||||
);
|
||||
|
||||
return {
|
||||
...settings,
|
||||
updateSetting,
|
||||
saveSettings,
|
||||
isFetching,
|
||||
isFetched,
|
||||
isSaving,
|
||||
error,
|
||||
saveError,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { createAction } from 'redux-actions';
|
||||
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
||||
import createSaveHandler from 'Store/Actions/Creators/createSaveHandler';
|
||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
const section = 'settings.ui';
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_UI_SETTINGS = 'settings/ui/fetchUiSettings';
|
||||
export const SET_UI_SETTINGS_VALUE = 'SET_UI_SETTINGS_VALUE';
|
||||
export const SAVE_UI_SETTINGS = 'SAVE_UI_SETTINGS';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchUISettings = createThunk(FETCH_UI_SETTINGS);
|
||||
export const saveUISettings = createThunk(SAVE_UI_SETTINGS);
|
||||
export const setUISettingsValue = createAction(SET_UI_SETTINGS_VALUE, (payload) => {
|
||||
return {
|
||||
section,
|
||||
...payload
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
// Details
|
||||
|
||||
export default {
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
defaultState: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
pendingChanges: {},
|
||||
isSaving: false,
|
||||
saveError: null,
|
||||
item: {}
|
||||
},
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
actionHandlers: {
|
||||
[FETCH_UI_SETTINGS]: createFetchHandler(section, '/config/ui'),
|
||||
[SAVE_UI_SETTINGS]: createSaveHandler(section, '/config/ui')
|
||||
},
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
reducers: {
|
||||
[SET_UI_SETTINGS_VALUE]: createSetSettingValueReducer(section)
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -24,7 +24,6 @@ import qualityDefinitions from './Settings/qualityDefinitions';
|
|||
import qualityProfiles from './Settings/qualityProfiles';
|
||||
import releaseProfiles from './Settings/releaseProfiles';
|
||||
import remotePathMappings from './Settings/remotePathMappings';
|
||||
import ui from './Settings/ui';
|
||||
|
||||
export * from './Settings/autoTaggingSpecifications';
|
||||
export * from './Settings/autoTaggings';
|
||||
|
|
@ -50,7 +49,6 @@ export * from './Settings/qualityDefinitions';
|
|||
export * from './Settings/qualityProfiles';
|
||||
export * from './Settings/releaseProfiles';
|
||||
export * from './Settings/remotePathMappings';
|
||||
export * from './Settings/ui';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
|
@ -85,8 +83,7 @@ export const defaultState = {
|
|||
qualityDefinitions: qualityDefinitions.defaultState,
|
||||
qualityProfiles: qualityProfiles.defaultState,
|
||||
releaseProfiles: releaseProfiles.defaultState,
|
||||
remotePathMappings: remotePathMappings.defaultState,
|
||||
ui: ui.defaultState
|
||||
remotePathMappings: remotePathMappings.defaultState
|
||||
};
|
||||
|
||||
export const persistState = [
|
||||
|
|
@ -120,8 +117,7 @@ export const actionHandlers = handleThunks({
|
|||
...qualityDefinitions.actionHandlers,
|
||||
...qualityProfiles.actionHandlers,
|
||||
...releaseProfiles.actionHandlers,
|
||||
...remotePathMappings.actionHandlers,
|
||||
...ui.actionHandlers
|
||||
...remotePathMappings.actionHandlers
|
||||
});
|
||||
|
||||
//
|
||||
|
|
@ -151,7 +147,6 @@ export const reducers = createHandleActions({
|
|||
...qualityDefinitions.reducers,
|
||||
...qualityProfiles.reducers,
|
||||
...releaseProfiles.reducers,
|
||||
...remotePathMappings.reducers,
|
||||
...ui.reducers
|
||||
...remotePathMappings.reducers
|
||||
|
||||
}, defaultState, section);
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
function createUISettingsSelector() {
|
||||
return createSelector(
|
||||
(state: AppState) => state.settings.ui,
|
||||
(ui) => {
|
||||
return ui.item;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createUISettingsSelector;
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import moment from 'moment';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||
|
||||
|
|
@ -11,9 +10,7 @@ interface StartTimeProps {
|
|||
|
||||
function StartTime(props: StartTimeProps) {
|
||||
const { startTime } = props;
|
||||
const { timeFormat, longDateFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { timeFormat, longDateFormat } = useUiSettingsValues();
|
||||
const [time, setTime] = useState(Date.now());
|
||||
|
||||
const { formattedStartTime, uptime } = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import moment from 'moment';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { CommandBody } from 'Commands/Command';
|
||||
import { useCancelCommand } from 'Commands/useCommands';
|
||||
import Icon, { IconProps } from 'Components/Icon';
|
||||
|
|
@ -10,7 +9,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|||
import TableRow from 'Components/Table/TableRow';
|
||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import formatDate from 'Utilities/Date/formatDate';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||
|
|
@ -120,7 +119,7 @@ export default function QueuedTaskRow(props: QueuedTaskRowProps) {
|
|||
|
||||
const { cancelCommand } = useCancelCommand(id);
|
||||
const { longDateFormat, shortDateFormat, showRelativeDates, timeFormat } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
|
||||
const updateTimeTimeoutId = useRef<ReturnType<typeof setTimeout> | null>(
|
||||
null
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import moment from 'moment';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCommand, useExecuteCommand } from 'Commands/useCommands';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { isCommandExecuting } from 'Utilities/Command';
|
||||
import formatDate from 'Utilities/Date/formatDate';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
|
|
@ -35,7 +34,7 @@ function ScheduledTaskRow({
|
|||
}: ScheduledTaskRowProps) {
|
||||
const executeCommand = useExecuteCommand();
|
||||
const { showRelativeDates, longDateFormat, shortDateFormat, timeFormat } =
|
||||
useSelector(createUISettingsSelector());
|
||||
useUiSettingsValues();
|
||||
const command = useCommand(taskName);
|
||||
|
||||
const [time, setTime] = useState(Date.now());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useAppValue } from 'App/appStore';
|
||||
import CommandNames from 'Commands/CommandNames';
|
||||
import { useCommandExecuting, useExecuteCommand } from 'Commands/useCommands';
|
||||
|
|
@ -14,8 +14,8 @@ import PageContent from 'Components/Page/PageContent';
|
|||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import useUpdateSettings from 'Settings/General/useUpdateSettings';
|
||||
import { useUiSettingsValues } from 'Settings/UI/useUiSettings';
|
||||
import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { useSystemStatusData } from 'System/Status/useSystemStatus';
|
||||
import { UpdateMechanism } from 'typings/Settings/General';
|
||||
import formatDate from 'Utilities/Date/formatDate';
|
||||
|
|
@ -31,9 +31,7 @@ function Updates() {
|
|||
const currentVersion = useAppValue('version');
|
||||
const { packageUpdateMechanismMessage } = useSystemStatusData();
|
||||
|
||||
const { shortDateFormat, longDateFormat, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const { shortDateFormat, longDateFormat, timeFormat } = useUiSettingsValues();
|
||||
const isInstallingUpdate = useCommandExecuting(
|
||||
CommandNames.ApplicationUpdate
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
export default interface UiSettings {
|
||||
theme: 'auto' | 'dark' | 'light';
|
||||
showRelativeDates: boolean;
|
||||
shortDateFormat: string;
|
||||
longDateFormat: string;
|
||||
timeFormat: string;
|
||||
timeZone: string;
|
||||
firstDayOfWeek: number;
|
||||
enableColorImpairedMode: boolean;
|
||||
calendarWeekColumnHeader: string;
|
||||
uiLanguage: number;
|
||||
}
|
||||
Loading…
Reference in a new issue