Improve client-side graphql scalar types (#4511)

* Add types to graphql scalars
* Upgrade dependencies
* Override UI config type
* Remove all IUIConfig casts
* Add tableColumns to IUIConfig
* Add BoolMap type, set strictScalars
* Add PluginConfigMap
* Replace any with unknown
* Add SavedObjectFilter and SavedUIOptions
* Remove unused items from CriterionType
This commit is contained in:
DingDongSoLong4 2024-02-07 00:49:32 +02:00 committed by GitHub
parent 9ac6505241
commit 2d73912f15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
121 changed files with 887 additions and 1062 deletions

View File

@ -17,7 +17,7 @@
# GraphQL generated output
pkg/models/generated_*.go
ui/v2.5/src/core/generated-*.tsx
ui/v2.5/src/core/generated-graphql.ts
####
# Jetbrains

View File

@ -36,6 +36,8 @@ models:
model: github.com/stashapp/stash/internal/api.Timestamp
BoolMap:
model: github.com/stashapp/stash/internal/api.BoolMap
PluginConfigMap:
model: github.com/stashapp/stash/internal/api.PluginConfigMap
# define to force resolvers
Image:
model: github.com/stashapp/stash/pkg/models.Image

View File

@ -535,7 +535,7 @@ type ConfigResult {
scraping: ConfigScrapingResult!
defaults: ConfigDefaultSettingsResult!
ui: Map!
plugins(include: [String!]): Map!
plugins(include: [ID!]): PluginConfigMap!
}
"Directory structure of a path"

View File

@ -1,6 +1,3 @@
"Log entries"
scalar Time
enum LogLevel {
Trace
Debug

View File

@ -1,5 +1,3 @@
scalar Upload
input GenerateMetadataInput {
covers: Boolean
sprites: Boolean

View File

@ -1,3 +1,6 @@
"An RFC3339 timestamp"
scalar Time
"""
Timestamp is a point in time. It is always output as RFC3339-compatible time points.
It can be input as a RFC3339 string, or as "<4h" for "4 hours in the past" or ">5m"
@ -5,12 +8,18 @@ for "5 minutes in the future"
"""
scalar Timestamp
# generic JSON object
"A String -> Any map"
scalar Map
# string, boolean map
"A String -> Boolean map"
scalar BoolMap
"A plugin ID -> Map (String -> Any map) map"
scalar PluginConfigMap
scalar Any
scalar Int64
"A multipart file upload"
scalar Upload

View File

@ -0,0 +1,37 @@
package api
import (
"encoding/json"
"fmt"
"io"
"github.com/99designs/gqlgen/graphql"
)
func MarshalPluginConfigMap(val map[string]map[string]interface{}) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
err := json.NewEncoder(w).Encode(val)
if err != nil {
panic(err)
}
})
}
func UnmarshalPluginConfigMap(v interface{}) (map[string]map[string]interface{}, error) {
m, ok := v.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%T is not a plugin config map", v)
}
result := make(map[string]map[string]interface{})
for k, v := range m {
val, ok := v.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("key %s (%T) is not a map", k, v)
}
result[k] = val
}
return result, nil
}

View File

@ -6,13 +6,13 @@ import (
"github.com/stashapp/stash/internal/manager/config"
)
func (r *configResultResolver) Plugins(ctx context.Context, obj *ConfigResult, include []string) (map[string]interface{}, error) {
func (r *configResultResolver) Plugins(ctx context.Context, obj *ConfigResult, include []string) (map[string]map[string]interface{}, error) {
if len(include) == 0 {
ret := config.GetInstance().GetAllPluginConfiguration()
return ret, nil
}
ret := make(map[string]interface{})
ret := make(map[string]map[string]interface{})
for _, plugin := range include {
c := config.GetInstance().GetPluginConfiguration(plugin)

View File

@ -735,11 +735,11 @@ func (i *Config) GetPluginsPath() string {
return i.getString(PluginsPath)
}
func (i *Config) GetAllPluginConfiguration() map[string]interface{} {
func (i *Config) GetAllPluginConfiguration() map[string]map[string]interface{} {
i.RLock()
defer i.RUnlock()
ret := make(map[string]interface{})
ret := make(map[string]map[string]interface{})
sub := i.viper(PluginsSetting).GetStringMap(PluginsSetting)
if sub == nil {

View File

@ -20,7 +20,7 @@
"version": "detect"
}
},
"ignorePatterns": ["node_modules/", "src/core/generated-graphql.tsx"],
"ignorePatterns": ["node_modules/", "src/core/generated-graphql.ts"],
"rules": {
"@typescript-eslint/lines-between-class-members": "off",
"@typescript-eslint/naming-convention": [

2
ui/v2.5/.gitignore vendored
View File

@ -1,5 +1,5 @@
# generated
src/core/generated-*.tsx
src/core/generated-graphql.ts
# dependencies
/node_modules

View File

@ -15,4 +15,4 @@ src/locales/**/*.json
/build
# generated
src/core/generated-graphql.tsx
src/core/generated-graphql.ts

42
ui/v2.5/codegen.ts Normal file
View File

@ -0,0 +1,42 @@
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: [
"../../graphql/schema/**/*.graphql",
"graphql/client-schema.graphql",
],
config: {
// makes conflicting fields override rather than error
onFieldTypeConflict: (_existing: unknown, other: unknown) => other,
},
documents: "graphql/**/*.graphql",
generates: {
"src/core/generated-graphql.ts": {
plugins: [
"time",
"typescript",
"typescript-operations",
"typescript-react-apollo",
],
config: {
strictScalars: true,
scalars: {
Time: "string",
Timestamp: "string",
Map: "{ [key: string]: unknown }",
BoolMap: "{ [key: string]: boolean }",
PluginConfigMap: "{ [id: string]: { [key: string]: unknown } }",
Any: "unknown",
Int64: "number",
Upload: "File",
UIConfig: "src/core/config#IUIConfig",
SavedObjectFilter: "src/models/list-filter/types#SavedObjectFilter",
SavedUIOptions: "src/models/list-filter/types#SavedUIOptions",
},
withRefetchFn: true,
},
},
},
};
export default config;

View File

@ -1,12 +0,0 @@
overwrite: true
schema: "../../graphql/schema/**/*.graphql"
documents: "../../graphql/documents/**/*.graphql"
generates:
src/core/generated-graphql.tsx:
plugins:
- time
- typescript
- typescript-operations
- typescript-react-apollo
config:
withRefetchFn: true

View File

@ -0,0 +1,26 @@
scalar UIConfig
scalar SavedObjectFilter
scalar SavedUIOptions
extend type ConfigResult {
ui: UIConfig!
}
extend type SavedFilter {
object_filter: SavedObjectFilter
ui_options: SavedUIOptions
}
extend input SaveFilterInput {
object_filter: SavedObjectFilter
ui_options: SavedUIOptions
}
extend input SetDefaultFilterInput {
object_filter: SavedObjectFilter
ui_options: SavedUIOptions
}
extend type Mutation {
configureUI(input: UIConfig!): UIConfig!
}

View File

@ -36,7 +36,7 @@ mutation ConfigureDefaults($input: ConfigDefaultSettingsInput!) {
}
}
mutation ConfigureUI($input: Map!) {
mutation ConfigureUI($input: UIConfig!) {
configureUI(input: $input)
}

View File

@ -14,12 +14,12 @@
"check": "tsc --noEmit",
"format": "prettier --write . ../../graphql",
"format-check": "prettier --check . ../../graphql",
"gqlgen": "gql-gen --config codegen.yml",
"gqlgen": "gql-gen --config codegen.ts",
"extract": "NODE_ENV=development extract-messages -l=en,de -o src/locale -d en --flat false 'src/**/!(*.test).tsx'"
},
"dependencies": {
"@ant-design/react-slick": "^1.0.0",
"@apollo/client": "^3.7.17",
"@apollo/client": "^3.8.10",
"@formatjs/intl-getcanonicallocales": "^2.0.5",
"@formatjs/intl-locale": "^3.0.11",
"@formatjs/intl-numberformat": "^8.3.3",
@ -31,7 +31,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@silvermine/videojs-airplay": "^1.2.0",
"@silvermine/videojs-chromecast": "^1.4.1",
"apollo-upload-client": "^17.0.0",
"apollo-upload-client": "^18.0.1",
"base64-blob": "^1.4.1",
"bootstrap": "^4.6.2",
"classnames": "^2.3.2",
@ -40,7 +40,7 @@
"formik": "^2.4.5",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.3",
"graphql-ws": "^5.14.3",
"i18n-iso-countries": "^7.5.0",
"intersection-observer": "^0.12.2",
"localforage": "^1.10.0",
@ -78,12 +78,12 @@
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@graphql-codegen/cli": "^3.0.0",
"@graphql-codegen/time": "^4.0.0",
"@graphql-codegen/typescript": "^3.0.0",
"@graphql-codegen/typescript-operations": "^3.0.0",
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
"@types/apollo-upload-client": "^17.0.2",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/time": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-react-apollo": "^4.1.0",
"@types/apollo-upload-client": "^18.0.0",
"@types/lodash-es": "^4.17.6",
"@types/mousetrap": "^1.6.11",
"@types/node": "^18.13.0",

View File

@ -36,7 +36,6 @@ import { ConfigurationProvider } from "./hooks/Config";
import { ManualProvider } from "./components/Help/context";
import { InteractiveProvider } from "./hooks/Interactive/context";
import { ReleaseNotesDialog } from "./components/Dialogs/ReleaseNotesDialog";
import { IUIConfig } from "./core/config";
import { releaseNotes } from "./docs/en/ReleaseNotes";
import { getPlatformURL } from "./core/createClient";
import { lazyComponent } from "./utils/lazyComponent";
@ -324,8 +323,7 @@ export const App: React.FC = () => {
return;
}
const lastNoteSeen = (config.data?.configuration.ui as IUIConfig)
?.lastNoteSeen;
const lastNoteSeen = config.data?.configuration.ui.lastNoteSeen;
const notes = releaseNotes.filter((n) => {
return !lastNoteSeen || n.date > lastNoteSeen;
});

View File

@ -11,7 +11,6 @@ import {
FrontPageContent,
generateDefaultFrontPageContent,
getFrontPageContent,
IUIConfig,
} from "src/core/config";
import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
@ -59,7 +58,7 @@ const FrontPage: React.FC = () => {
return <FrontPageConfig onClose={(content) => onUpdateConfig(content)} />;
}
const ui = (configuration?.ui ?? {}) as IUIConfig;
const ui = configuration?.ui ?? {};
if (!ui.frontPageContent) {
const defaultContent = generateDefaultFrontPageContent(intl);

View File

@ -6,7 +6,6 @@ import { Button, Form, Modal } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { ConfigurationContext } from "src/hooks/Config";
import {
IUIConfig,
ISavedFilterRow,
ICustomFilter,
FrontPageContent,
@ -283,7 +282,7 @@ export const FrontPageConfig: React.FC<IFrontPageConfigProps> = ({
}) => {
const { configuration, loading } = React.useContext(ConfigurationContext);
const ui = configuration?.ui as IUIConfig;
const ui = configuration?.ui;
const { data: allFilters, loading: loading2 } = useFindSavedFilters();

View File

@ -161,7 +161,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({
useRatingKeybinds(
isVisible,
stashConfig?.ui?.ratingSystemOptions?.type,
stashConfig?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -111,7 +111,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
useRatingKeybinds(
true,
configuration?.ui?.ratingSystemOptions?.type,
configuration?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -32,7 +32,6 @@ import { ExportDialog } from "../Shared/ExportDialog";
import { objectTitle } from "src/core/files";
import TextUtils from "src/utils/text";
import { ConfigurationContext } from "src/hooks/Config";
import { IUIConfig } from "src/core/config";
import { useContainerDimensions } from "../Shared/GridCard";
interface IImageWallProps {
@ -45,7 +44,7 @@ interface IImageWallProps {
const ImageWall: React.FC<IImageWallProps> = ({ images, handleImageOpen }) => {
const { configuration } = useContext(ConfigurationContext);
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
let photos: {
src: string;

View File

@ -31,7 +31,6 @@ import { useCompare, usePrevious } from "src/hooks/state";
import { CriterionType } from "src/models/list-filter/types";
import { useToast } from "src/hooks/Toast";
import { useConfigureUI } from "src/core/StashService";
import { IUIConfig } from "src/core/config";
import { FilterMode } from "src/core/generated-graphql";
import { useFocusOnce } from "src/utils/focus";
import Mousetrap from "mousetrap";
@ -270,7 +269,7 @@ export const EditFilterDialog: React.FC<IEditFilterProps> = ({
[filter, criteria]
);
const ui = (configuration?.ui ?? {}) as IUIConfig;
const ui = configuration?.ui ?? {};
const [saveUI] = useConfigureUI();
const filteredOptions = useMemo(() => {

View File

@ -67,8 +67,8 @@ export const SavedFilterList: React.FC<ISavedFilterListProps> = ({
mode: filter.mode,
name,
find_filter: filterCopy.makeFindFilter(),
object_filter: filterCopy.makeSavedFindFilter(),
ui_options: filterCopy.makeUIOptions(),
object_filter: filterCopy.makeSavedFilter(),
ui_options: filterCopy.makeSavedUIOptions(),
},
},
});
@ -137,8 +137,8 @@ export const SavedFilterList: React.FC<ISavedFilterListProps> = ({
input: {
mode: filter.mode,
find_filter: filterCopy.makeFindFilter(),
object_filter: filterCopy.makeSavedFindFilter(),
ui_options: filterCopy.makeUIOptions(),
object_filter: filterCopy.makeSavedFilter(),
ui_options: filterCopy.makeSavedUIOptions(),
},
},
});

View File

@ -33,7 +33,6 @@ import TextUtils from "src/utils/text";
import { Icon } from "src/components/Shared/Icon";
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
import { ConfigurationContext } from "src/hooks/Config";
import { IUIConfig } from "src/core/config";
import { DetailImage } from "src/components/Shared/DetailImage";
import { useRatingKeybinds } from "src/hooks/keybinds";
import { useLoadStickyHeader } from "src/hooks/detailsPanel";
@ -55,7 +54,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
// Configuration settings
const { configuration } = React.useContext(ConfigurationContext);
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
const enableBackgroundImage = uiConfig?.enableMovieBackgroundImage ?? false;
const compactExpandedDetails = uiConfig?.compactExpandedDetails ?? false;
const showAllDetails = uiConfig?.showAllDetails ?? true;
@ -130,7 +129,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
useRatingKeybinds(
true,
configuration?.ui?.ratingSystemOptions?.type,
configuration?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -40,7 +40,6 @@ import {
faLink,
} from "@fortawesome/free-solid-svg-icons";
import { faInstagram, faTwitter } from "@fortawesome/free-brands-svg-icons";
import { IUIConfig } from "src/core/config";
import { useRatingKeybinds } from "src/hooks/keybinds";
import { DetailImage } from "src/components/Shared/DetailImage";
import { useLoadStickyHeader } from "src/hooks/detailsPanel";
@ -80,7 +79,7 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
// Configuration settings
const { configuration } = React.useContext(ConfigurationContext);
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
const abbreviateCounter = uiConfig?.abbreviateCounters ?? false;
const enableBackgroundImage =
uiConfig?.enablePerformerBackgroundImage ?? false;
@ -160,7 +159,7 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
useRatingKeybinds(
true,
configuration?.ui?.ratingSystemOptions?.type,
configuration?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -15,7 +15,7 @@ import {
} from "src/core/StashService";
import { ConfigurationContext } from "src/hooks/Config";
import { useIntl } from "react-intl";
import { defaultMaxOptionsShown, IUIConfig } from "src/core/config";
import { defaultMaxOptionsShown } from "src/core/config";
import { ListFilterModel } from "src/models/list-filter/filter";
import {
FilterSelectComponent,
@ -47,7 +47,7 @@ export const PerformerSelect: React.FC<
const { configuration } = React.useContext(ConfigurationContext);
const intl = useIntl();
const maxOptionsShown =
(configuration?.ui as IUIConfig).maxOptionsShown ?? defaultMaxOptionsShown;
configuration?.ui.maxOptionsShown ?? defaultMaxOptionsShown;
const defaultCreatable =
!configuration?.interface.disableDropdownCreate.performer ?? true;

View File

@ -38,7 +38,6 @@ import {
import { SceneInteractiveStatus } from "src/hooks/Interactive/status";
import { languageMap } from "src/utils/caption";
import { VIDEO_PLAYER_ID } from "./util";
import { IUIConfig } from "src/core/config";
// @ts-ignore
import airplay from "@silvermine/videojs-airplay";
@ -223,7 +222,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
}) => {
const { configuration } = useContext(ConfigurationContext);
const interfaceConfig = configuration?.interface;
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
const videoRef = useRef<HTMLDivElement>(null);
const [_player, setPlayer] = useState<VideoJsPlayer>();
const sceneId = useRef<string>();

View File

@ -224,7 +224,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
useRatingKeybinds(
isVisible,
stashConfig?.ui?.ratingSystemOptions?.type,
stashConfig?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -137,7 +137,7 @@ export const SettingsServicesPanel: React.FC = () => {
}
}
function renderDeadline(until?: string) {
function renderDeadline(until?: string | null) {
if (until) {
const deadline = new Date(until);
return `until ${intl.formatDate(deadline)}`;

View File

@ -95,6 +95,8 @@ export const ImportDialog: React.FC<IImportDialogProps> = (
}
async function onImport() {
if (!file) return;
try {
setIsRunning(true);
await mutateImportObjects({

View File

@ -22,7 +22,8 @@ import { useToast } from "src/hooks/Toast";
import { withoutTypename } from "src/utils/data";
import { Icon } from "../Shared/Icon";
type PluginSettings = Record<string, Record<string, unknown>>;
type PluginConfigs = Record<string, Record<string, unknown>>;
export interface ISettingsContextState {
loading: boolean;
error: ApolloError | undefined;
@ -32,7 +33,7 @@ export interface ISettingsContextState {
scraping: GQL.ConfigScrapingInput;
dlna: GQL.ConfigDlnaInput;
ui: IUIConfig;
plugins: PluginSettings;
plugins: PluginConfigs;
advancedMode: boolean;
@ -137,8 +138,8 @@ export const SettingsContext: React.FC = ({ children }) => {
const [pendingUI, setPendingUI] = useState<{}>();
const [updateUIConfig] = useConfigureUI();
const [plugins, setPlugins] = useState<PluginSettings>({});
const [pendingPlugins, setPendingPlugins] = useState<PluginSettings>();
const [plugins, setPlugins] = useState<PluginConfigs>({});
const [pendingPlugins, setPendingPlugins] = useState<PluginConfigs>();
const [updatePluginConfig] = useConfigurePlugin();
const [updateSuccess, setUpdateSuccess] = useState<boolean>();
@ -479,7 +480,7 @@ export const SettingsContext: React.FC = ({ children }) => {
}
// saves the configuration if no further changes are made after a half second
const savePluginConfig = useDebounce(async (input: PluginSettings) => {
const savePluginConfig = useDebounce(async (input: PluginConfigs) => {
try {
setUpdateSuccess(undefined);

View File

@ -10,7 +10,6 @@ import React, { useMemo } from "react";
import { Button, OverlayTrigger, Tooltip } from "react-bootstrap";
import { FormattedNumber, useIntl } from "react-intl";
import { Link } from "react-router-dom";
import { IUIConfig } from "src/core/config";
import { ConfigurationContext } from "src/hooks/Config";
import TextUtils from "src/utils/text";
import { Icon } from "./Icon";
@ -37,8 +36,7 @@ export const PopoverCountButton: React.FC<IProps> = ({
count,
}) => {
const { configuration } = React.useContext(ConfigurationContext);
const abbreviateCounter =
(configuration?.ui as IUIConfig)?.abbreviateCounters ?? false;
const abbreviateCounter = configuration?.ui.abbreviateCounters ?? false;
const intl = useIntl();

View File

@ -1,5 +1,4 @@
import React from "react";
import { IUIConfig } from "src/core/config";
import { ConfigurationContext } from "src/hooks/Config";
import {
defaultRatingStarPrecision,
@ -21,8 +20,7 @@ export const RatingSystem: React.FC<IRatingSystemProps> = (
) => {
const { configuration: config } = React.useContext(ConfigurationContext);
const ratingSystemOptions =
(config?.ui as IUIConfig)?.ratingSystemOptions ??
defaultRatingSystemOptions;
config?.ui.ratingSystemOptions ?? defaultRatingSystemOptions;
if (ratingSystemOptions.type === RatingSystemType.Stars) {
return (

View File

@ -7,7 +7,6 @@ import {
RatingSystemType,
} from "src/utils/rating";
import { ConfigurationContext } from "src/hooks/Config";
import { IUIConfig } from "src/core/config";
interface IProps {
rating?: number | null;
@ -16,8 +15,7 @@ interface IProps {
export const RatingBanner: React.FC<IProps> = ({ rating }) => {
const { configuration: config } = useContext(ConfigurationContext);
const ratingSystemOptions =
(config?.ui as IUIConfig)?.ratingSystemOptions ??
defaultRatingSystemOptions;
config?.ui.ratingSystemOptions ?? defaultRatingSystemOptions;
const isLegacy =
ratingSystemOptions.type === RatingSystemType.Stars &&
ratingSystemOptions.starPrecision === RatingStarPrecision.Full;

View File

@ -24,7 +24,7 @@ import { ConfigurationContext } from "src/hooks/Config";
import { useIntl } from "react-intl";
import { objectTitle } from "src/core/files";
import { galleryTitle } from "src/core/galleries";
import { defaultMaxOptionsShown, IUIConfig } from "src/core/config";
import { defaultMaxOptionsShown } from "src/core/config";
import { useDebounce } from "src/hooks/debounce";
import { Placement } from "react-bootstrap/esm/Overlay";
import { PerformerIDSelect } from "../Performers/PerformerSelect";
@ -132,7 +132,7 @@ const LimitedSelectMenu = <T extends boolean>(
) => {
const { configuration } = React.useContext(ConfigurationContext);
const maxOptionsShown =
(configuration?.ui as IUIConfig).maxOptionsShown ?? defaultMaxOptionsShown;
configuration?.ui.maxOptionsShown ?? defaultMaxOptionsShown;
const [hiddenCount, setHiddenCount] = useState<number>(0);
const hiddenCountStyle = {

View File

@ -38,7 +38,6 @@ import {
faChevronDown,
faChevronUp,
} from "@fortawesome/free-solid-svg-icons";
import { IUIConfig } from "src/core/config";
import TextUtils from "src/utils/text";
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
import { DetailImage } from "src/components/Shared/DetailImage";
@ -81,7 +80,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
// Configuration settings
const { configuration } = React.useContext(ConfigurationContext);
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
const abbreviateCounter = uiConfig?.abbreviateCounters ?? false;
const enableBackgroundImage = uiConfig?.enableStudioBackgroundImage ?? false;
const showAllDetails = uiConfig?.showAllDetails ?? true;
@ -101,8 +100,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
const [updateStudio] = useStudioUpdate();
const [deleteStudio] = useStudioDestroy({ id: studio.id });
const showAllCounts = (configuration?.ui as IUIConfig)
?.showChildStudioContent;
const showAllCounts = uiConfig?.showChildStudioContent;
const sceneCount =
(showAllCounts ? studio.scene_count_all : studio.scene_count) ?? 0;
const galleryCount =
@ -161,7 +159,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
useRatingKeybinds(
true,
configuration?.ui?.ratingSystemOptions?.type,
configuration?.ui.ratingSystemOptions?.type,
setRating
);

View File

@ -37,7 +37,6 @@ import {
faSignOutAlt,
faTrashAlt,
} from "@fortawesome/free-solid-svg-icons";
import { IUIConfig } from "src/core/config";
import { DetailImage } from "src/components/Shared/DetailImage";
import { useLoadStickyHeader } from "src/hooks/detailsPanel";
import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
@ -75,7 +74,7 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
// Configuration settings
const { configuration } = React.useContext(ConfigurationContext);
const uiConfig = configuration?.ui as IUIConfig | undefined;
const uiConfig = configuration?.ui;
const abbreviateCounter = uiConfig?.abbreviateCounters ?? false;
const enableBackgroundImage = uiConfig?.enableTagBackgroundImage ?? false;
const showAllDetails = uiConfig?.showAllDetails ?? true;
@ -96,7 +95,7 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
const [updateTag] = useTagUpdate();
const [deleteTag] = useTagDestroy({ id: tag.id });
const showAllCounts = (configuration?.ui as IUIConfig)?.showChildTagContent;
const showAllCounts = uiConfig?.showChildTagContent;
const sceneCount =
(showAllCounts ? tag.scene_count_all : tag.scene_count) ?? 0;
const imageCount =

View File

@ -5,7 +5,6 @@ import { HoverPopover } from "../Shared/HoverPopover";
import { useFindTag } from "../../core/StashService";
import { TagCard } from "./TagCard";
import { ConfigurationContext } from "../../hooks/Config";
import { IUIConfig } from "src/core/config";
import { Placement } from "react-bootstrap/esm/Overlay";
interface ITagPopoverCardProps {
@ -50,8 +49,7 @@ export const TagPopover: React.FC<ITagPopoverProps> = ({
}) => {
const { configuration: config } = React.useContext(ConfigurationContext);
const showTagCardOnHover =
(config?.ui as IUIConfig)?.showTagCardOnHover ?? true;
const showTagCardOnHover = config?.ui.showTagCardOnHover ?? true;
if (hide || !showTagCardOnHover) {
return <>{children}</>;

View File

@ -15,7 +15,7 @@ import {
} from "src/core/StashService";
import { ConfigurationContext } from "src/hooks/Config";
import { useIntl } from "react-intl";
import { defaultMaxOptionsShown, IUIConfig } from "src/core/config";
import { defaultMaxOptionsShown } from "src/core/config";
import { ListFilterModel } from "src/models/list-filter/filter";
import {
FilterSelectComponent,
@ -49,7 +49,7 @@ export const TagSelect: React.FC<
const { configuration } = React.useContext(ConfigurationContext);
const intl = useIntl();
const maxOptionsShown =
(configuration?.ui as IUIConfig).maxOptionsShown ?? defaultMaxOptionsShown;
configuration?.ui.maxOptionsShown ?? defaultMaxOptionsShown;
const defaultCreatable =
!configuration?.interface.disableDropdownCreate.tag ?? true;

Some files were not shown because too many files have changed in this diff Show More