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 # GraphQL generated output
pkg/models/generated_*.go pkg/models/generated_*.go
ui/v2.5/src/core/generated-*.tsx ui/v2.5/src/core/generated-graphql.ts
#### ####
# Jetbrains # Jetbrains

View file

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

View file

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

View file

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

View file

@ -1,5 +1,3 @@
scalar Upload
input GenerateMetadataInput { input GenerateMetadataInput {
covers: Boolean covers: Boolean
sprites: 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. 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" 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 scalar Timestamp
# generic JSON object "A String -> Any map"
scalar Map scalar Map
# string, boolean map "A String -> Boolean map"
scalar BoolMap scalar BoolMap
"A plugin ID -> Map (String -> Any map) map"
scalar PluginConfigMap
scalar Any scalar Any
scalar Int64 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" "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 { if len(include) == 0 {
ret := config.GetInstance().GetAllPluginConfiguration() ret := config.GetInstance().GetAllPluginConfiguration()
return ret, nil return ret, nil
} }
ret := make(map[string]interface{}) ret := make(map[string]map[string]interface{})
for _, plugin := range include { for _, plugin := range include {
c := config.GetInstance().GetPluginConfiguration(plugin) c := config.GetInstance().GetPluginConfiguration(plugin)

View file

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

View file

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

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

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

View file

@ -15,4 +15,4 @@ src/locales/**/*.json
/build /build
# generated # 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) configureUI(input: $input)
} }

View file

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

View file

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

View file

@ -11,7 +11,6 @@ import {
FrontPageContent, FrontPageContent,
generateDefaultFrontPageContent, generateDefaultFrontPageContent,
getFrontPageContent, getFrontPageContent,
IUIConfig,
} from "src/core/config"; } from "src/core/config";
import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
@ -59,7 +58,7 @@ const FrontPage: React.FC = () => {
return <FrontPageConfig onClose={(content) => onUpdateConfig(content)} />; return <FrontPageConfig onClose={(content) => onUpdateConfig(content)} />;
} }
const ui = (configuration?.ui ?? {}) as IUIConfig; const ui = configuration?.ui ?? {};
if (!ui.frontPageContent) { if (!ui.frontPageContent) {
const defaultContent = generateDefaultFrontPageContent(intl); 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 * as GQL from "src/core/generated-graphql";
import { ConfigurationContext } from "src/hooks/Config"; import { ConfigurationContext } from "src/hooks/Config";
import { import {
IUIConfig,
ISavedFilterRow, ISavedFilterRow,
ICustomFilter, ICustomFilter,
FrontPageContent, FrontPageContent,
@ -283,7 +282,7 @@ export const FrontPageConfig: React.FC<IFrontPageConfigProps> = ({
}) => { }) => {
const { configuration, loading } = React.useContext(ConfigurationContext); const { configuration, loading } = React.useContext(ConfigurationContext);
const ui = configuration?.ui as IUIConfig; const ui = configuration?.ui;
const { data: allFilters, loading: loading2 } = useFindSavedFilters(); const { data: allFilters, loading: loading2 } = useFindSavedFilters();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,7 +22,8 @@ import { useToast } from "src/hooks/Toast";
import { withoutTypename } from "src/utils/data"; import { withoutTypename } from "src/utils/data";
import { Icon } from "../Shared/Icon"; import { Icon } from "../Shared/Icon";
type PluginSettings = Record<string, Record<string, unknown>>; type PluginConfigs = Record<string, Record<string, unknown>>;
export interface ISettingsContextState { export interface ISettingsContextState {
loading: boolean; loading: boolean;
error: ApolloError | undefined; error: ApolloError | undefined;
@ -32,7 +33,7 @@ export interface ISettingsContextState {
scraping: GQL.ConfigScrapingInput; scraping: GQL.ConfigScrapingInput;
dlna: GQL.ConfigDlnaInput; dlna: GQL.ConfigDlnaInput;
ui: IUIConfig; ui: IUIConfig;
plugins: PluginSettings; plugins: PluginConfigs;
advancedMode: boolean; advancedMode: boolean;
@ -137,8 +138,8 @@ export const SettingsContext: React.FC = ({ children }) => {
const [pendingUI, setPendingUI] = useState<{}>(); const [pendingUI, setPendingUI] = useState<{}>();
const [updateUIConfig] = useConfigureUI(); const [updateUIConfig] = useConfigureUI();
const [plugins, setPlugins] = useState<PluginSettings>({}); const [plugins, setPlugins] = useState<PluginConfigs>({});
const [pendingPlugins, setPendingPlugins] = useState<PluginSettings>(); const [pendingPlugins, setPendingPlugins] = useState<PluginConfigs>();
const [updatePluginConfig] = useConfigurePlugin(); const [updatePluginConfig] = useConfigurePlugin();
const [updateSuccess, setUpdateSuccess] = useState<boolean>(); 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 // 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 { try {
setUpdateSuccess(undefined); setUpdateSuccess(undefined);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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