diff --git a/graphql/documents/data/config.graphql b/graphql/documents/data/config.graphql index d2c6c20b1..87d12fcaf 100644 --- a/graphql/documents/data/config.graphql +++ b/graphql/documents/data/config.graphql @@ -4,8 +4,16 @@ fragment ConfigGeneralData on ConfigGeneralResult { generatedPath } +fragment ConfigInterfaceData on ConfigInterfaceResult { + css + cssEnabled +} + fragment ConfigData on ConfigResult { general { ...ConfigGeneralData } + interface { + ...ConfigInterfaceData + } } \ No newline at end of file diff --git a/graphql/documents/mutations/config.graphql b/graphql/documents/mutations/config.graphql index 9081cb8a2..4e273f418 100644 --- a/graphql/documents/mutations/config.graphql +++ b/graphql/documents/mutations/config.graphql @@ -2,4 +2,10 @@ mutation ConfigureGeneral($input: ConfigGeneralInput!) { configureGeneral(input: $input) { ...ConfigGeneralData } +} + +mutation ConfigureInterface($input: ConfigInterfaceInput!) { + configureInterface(input: $input) { + ...ConfigInterfaceData + } } \ No newline at end of file diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index e756cd285..1bac03071 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -91,6 +91,7 @@ type Mutation { """Change general configuration options""" configureGeneral(input: ConfigGeneralInput!): ConfigGeneralResult! + configureInterface(input: ConfigInterfaceInput!): ConfigInterfaceResult! } type Subscription { diff --git a/graphql/schema/types/config.graphql b/graphql/schema/types/config.graphql index a7390c5e7..351fc844e 100644 --- a/graphql/schema/types/config.graphql +++ b/graphql/schema/types/config.graphql @@ -16,7 +16,20 @@ type ConfigGeneralResult { generatedPath: String! } +input ConfigInterfaceInput { + """Custom CSS""" + css: String + cssEnabled: Boolean +} + +type ConfigInterfaceResult { + """Custom CSS""" + css: String + cssEnabled: Boolean +} + """All configuration settings""" type ConfigResult { general: ConfigGeneralResult! + interface: ConfigInterfaceResult! } \ No newline at end of file diff --git a/pkg/api/resolver_mutation_configure.go b/pkg/api/resolver_mutation_configure.go index b7db89f05..7dff1b713 100644 --- a/pkg/api/resolver_mutation_configure.go +++ b/pkg/api/resolver_mutation_configure.go @@ -3,10 +3,11 @@ package api import ( "context" "fmt" + "path/filepath" + "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "path/filepath" ) func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.ConfigGeneralInput) (*models.ConfigGeneralResult, error) { @@ -41,3 +42,23 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co return makeConfigGeneralResult(), nil } + +func (r *mutationResolver) ConfigureInterface(ctx context.Context, input models.ConfigInterfaceInput) (*models.ConfigInterfaceResult, error) { + css := "" + + if input.CSS != nil { + css = *input.CSS + } + + config.SetCSS(css) + + if input.CSSEnabled != nil { + config.Set(config.CSSEnabled, *input.CSSEnabled) + } + + if err := config.Write(); err != nil { + return makeConfigInterfaceResult(), err + } + + return makeConfigInterfaceResult(), nil +} diff --git a/pkg/api/resolver_query_configuration.go b/pkg/api/resolver_query_configuration.go index e2ae353a0..7b96cd5f4 100644 --- a/pkg/api/resolver_query_configuration.go +++ b/pkg/api/resolver_query_configuration.go @@ -2,6 +2,7 @@ package api import ( "context" + "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" @@ -21,7 +22,8 @@ func (r *queryResolver) Directories(ctx context.Context, path *string) ([]string func makeConfigResult() *models.ConfigResult { return &models.ConfigResult{ - General: makeConfigGeneralResult(), + General: makeConfigGeneralResult(), + Interface: makeConfigInterfaceResult(), } } @@ -32,3 +34,12 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult { GeneratedPath: config.GetGeneratedPath(), } } + +func makeConfigInterfaceResult() *models.ConfigInterfaceResult { + css := config.GetCSS() + cssEnabled := config.GetCSSEnabled() + return &models.ConfigInterfaceResult{ + CSS: &css, + CSSEnabled: &cssEnabled, + } +} diff --git a/pkg/api/server.go b/pkg/api/server.go index 65454f704..bbda0a649 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -5,6 +5,15 @@ import ( "crypto/tls" "errors" "fmt" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "runtime/debug" + "strconv" + "strings" + "github.com/99designs/gqlgen/handler" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" @@ -16,14 +25,6 @@ import ( "github.com/stashapp/stash/pkg/manager/paths" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "io/ioutil" - "net/http" - "os" - "path" - "path/filepath" - "runtime/debug" - "strconv" - "strings" ) var uiBox *packr.Box @@ -72,6 +73,21 @@ func Start() { r.Mount("/scene", sceneRoutes{}.Routes()) r.Mount("/studio", studioRoutes{}.Routes()) + r.HandleFunc("/css", func(w http.ResponseWriter, r *http.Request) { + if !config.GetCSSEnabled() { + return + } + + // search for custom.css in current directory, then $HOME/.stash + fn := config.GetCSSPath() + exists, _ := utils.FileExists(fn) + if !exists { + return + } + + http.ServeFile(w, r, fn) + }) + // Serve the setup UI r.HandleFunc("/setup*", func(w http.ResponseWriter, r *http.Request) { ext := path.Ext(r.URL.Path) diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index f54caa6a9..fa3d3ffce 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -1,7 +1,10 @@ package config import ( + "io/ioutil" "github.com/spf13/viper" + + "github.com/stashapp/stash/pkg/utils" ) const Stash = "stash" @@ -15,6 +18,8 @@ const Database = "database" const Host = "host" const Port = "port" +const CSSEnabled = "cssEnabled" + func Set(key string, value interface{}) { viper.Set(key, value) } @@ -51,6 +56,46 @@ func GetPort() int { return viper.GetInt(Port) } +func GetCSSPath() string { + // search for custom.css in current directory, then $HOME/.stash + fn := "custom.css" + exists, _ := utils.FileExists(fn) + if !exists { + fn = "$HOME/.stash/" + fn + } + + return fn +} + +func GetCSS() string { + fn := GetCSSPath() + + exists, _ := utils.FileExists(fn) + if !exists { + return "" + } + + buf, err := ioutil.ReadFile(fn) + + if err != nil { + return "" + } + + return string(buf) +} + +func SetCSS(css string) { + fn := GetCSSPath() + + buf := []byte(css) + + ioutil.WriteFile(fn, buf, 0777) +} + +func GetCSSEnabled() bool { + return viper.GetBool(CSSEnabled) +} + func IsValid() bool { setPaths := viper.IsSet(Stash) && viper.IsSet(Cache) && viper.IsSet(Generated) && viper.IsSet(Metadata) // TODO: check valid paths diff --git a/pkg/models/generated_exec.go b/pkg/models/generated_exec.go index 48627edc4..a3a1aa11c 100644 --- a/pkg/models/generated_exec.go +++ b/pkg/models/generated_exec.go @@ -56,8 +56,14 @@ type ComplexityRoot struct { Stashes func(childComplexity int) int } + ConfigInterfaceResult struct { + CSS func(childComplexity int) int + CSSEnabled func(childComplexity int) int + } + ConfigResult struct { - General func(childComplexity int) int + General func(childComplexity int) int + Interface func(childComplexity int) int } FindGalleriesResultType struct { @@ -107,6 +113,7 @@ type ComplexityRoot struct { Mutation struct { ConfigureGeneral func(childComplexity int, input ConfigGeneralInput) int + ConfigureInterface func(childComplexity int, input ConfigInterfaceInput) int PerformerCreate func(childComplexity int, input PerformerCreateInput) int PerformerDestroy func(childComplexity int, input PerformerDestroyInput) int PerformerUpdate func(childComplexity int, input PerformerUpdateInput) int @@ -298,6 +305,7 @@ type MutationResolver interface { TagUpdate(ctx context.Context, input TagUpdateInput) (*Tag, error) TagDestroy(ctx context.Context, input TagDestroyInput) (bool, error) ConfigureGeneral(ctx context.Context, input ConfigGeneralInput) (*ConfigGeneralResult, error) + ConfigureInterface(ctx context.Context, input ConfigInterfaceInput) (*ConfigInterfaceResult, error) } type PerformerResolver interface { Name(ctx context.Context, obj *Performer) (*string, error) @@ -424,6 +432,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ConfigGeneralResult.Stashes(childComplexity), true + case "ConfigInterfaceResult.css": + if e.complexity.ConfigInterfaceResult.CSS == nil { + break + } + + return e.complexity.ConfigInterfaceResult.CSS(childComplexity), true + + case "ConfigInterfaceResult.cssEnabled": + if e.complexity.ConfigInterfaceResult.CSSEnabled == nil { + break + } + + return e.complexity.ConfigInterfaceResult.CSSEnabled(childComplexity), true + case "ConfigResult.general": if e.complexity.ConfigResult.General == nil { break @@ -431,6 +453,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ConfigResult.General(childComplexity), true + case "ConfigResult.interface": + if e.complexity.ConfigResult.Interface == nil { + break + } + + return e.complexity.ConfigResult.Interface(childComplexity), true + case "FindGalleriesResultType.count": if e.complexity.FindGalleriesResultType.Count == nil { break @@ -590,6 +619,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.ConfigureGeneral(childComplexity, args["input"].(ConfigGeneralInput)), true + case "Mutation.configureInterface": + if e.complexity.Mutation.ConfigureInterface == nil { + break + } + + args, err := ec.field_Mutation_configureInterface_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.ConfigureInterface(childComplexity, args["input"].(ConfigInterfaceInput)), true + case "Mutation.performerCreate": if e.complexity.Mutation.PerformerCreate == nil { break @@ -1880,6 +1921,7 @@ type Mutation { """Change general configuration options""" configureGeneral(input: ConfigGeneralInput!): ConfigGeneralResult! + configureInterface(input: ConfigInterfaceInput!): ConfigInterfaceResult! } type Subscription { @@ -1910,9 +1952,22 @@ type ConfigGeneralResult { generatedPath: String! } +input ConfigInterfaceInput { + """Custom CSS""" + css: String + cssEnabled: Boolean +} + +type ConfigInterfaceResult { + """Custom CSS""" + css: String + cssEnabled: Boolean +} + """All configuration settings""" type ConfigResult { general: ConfigGeneralResult! + interface: ConfigInterfaceResult! }`}, &ast.Source{Name: "graphql/schema/types/filters.graphql", Input: `enum SortDirectionEnum { ASC @@ -2294,6 +2349,20 @@ func (ec *executionContext) field_Mutation_configureGeneral_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Mutation_configureInterface_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 ConfigInterfaceInput + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNConfigInterfaceInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_performerCreate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2917,6 +2986,54 @@ func (ec *executionContext) _ConfigGeneralResult_generatedPath(ctx context.Conte return ec.marshalNString2string(ctx, field.Selections, res) } +func (ec *executionContext) _ConfigInterfaceResult_css(ctx context.Context, field graphql.CollectedField, obj *ConfigInterfaceResult) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "ConfigInterfaceResult", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CSS, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) _ConfigInterfaceResult_cssEnabled(ctx context.Context, field graphql.CollectedField, obj *ConfigInterfaceResult) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "ConfigInterfaceResult", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CSSEnabled, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*bool) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) +} + func (ec *executionContext) _ConfigResult_general(ctx context.Context, field graphql.CollectedField, obj *ConfigResult) graphql.Marshaler { ctx = ec.Tracer.StartFieldExecution(ctx, field) defer func() { ec.Tracer.EndFieldExecution(ctx) }() @@ -2944,6 +3061,33 @@ func (ec *executionContext) _ConfigResult_general(ctx context.Context, field gra return ec.marshalNConfigGeneralResult2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigGeneralResult(ctx, field.Selections, res) } +func (ec *executionContext) _ConfigResult_interface(ctx context.Context, field graphql.CollectedField, obj *ConfigResult) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "ConfigResult", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Interface, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*ConfigInterfaceResult) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNConfigInterfaceResult2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceResult(ctx, field.Selections, res) +} + func (ec *executionContext) _FindGalleriesResultType_count(ctx context.Context, field graphql.CollectedField, obj *FindGalleriesResultType) graphql.Marshaler { ctx = ec.Tracer.StartFieldExecution(ctx, field) defer func() { ec.Tracer.EndFieldExecution(ctx) }() @@ -3951,6 +4095,40 @@ func (ec *executionContext) _Mutation_configureGeneral(ctx context.Context, fiel return ec.marshalNConfigGeneralResult2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigGeneralResult(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_configureInterface(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_configureInterface_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().ConfigureInterface(rctx, args["input"].(ConfigInterfaceInput)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*ConfigInterfaceResult) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNConfigInterfaceResult2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceResult(ctx, field.Selections, res) +} + func (ec *executionContext) _Performer_id(ctx context.Context, field graphql.CollectedField, obj *Performer) graphql.Marshaler { ctx = ec.Tracer.StartFieldExecution(ctx, field) defer func() { ec.Tracer.EndFieldExecution(ctx) }() @@ -8049,6 +8227,30 @@ func (ec *executionContext) unmarshalInputConfigGeneralInput(ctx context.Context return it, nil } +func (ec *executionContext) unmarshalInputConfigInterfaceInput(ctx context.Context, v interface{}) (ConfigInterfaceInput, error) { + var it ConfigInterfaceInput + var asMap = v.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "css": + var err error + it.CSS, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "cssEnabled": + var err error + it.CSSEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputFindFilterType(ctx context.Context, v interface{}) (FindFilterType, error) { var it FindFilterType var asMap = v.(map[string]interface{}) @@ -8868,6 +9070,32 @@ func (ec *executionContext) _ConfigGeneralResult(ctx context.Context, sel ast.Se return out } +var configInterfaceResultImplementors = []string{"ConfigInterfaceResult"} + +func (ec *executionContext) _ConfigInterfaceResult(ctx context.Context, sel ast.SelectionSet, obj *ConfigInterfaceResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, configInterfaceResultImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ConfigInterfaceResult") + case "css": + out.Values[i] = ec._ConfigInterfaceResult_css(ctx, field, obj) + case "cssEnabled": + out.Values[i] = ec._ConfigInterfaceResult_cssEnabled(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var configResultImplementors = []string{"ConfigResult"} func (ec *executionContext) _ConfigResult(ctx context.Context, sel ast.SelectionSet, obj *ConfigResult) graphql.Marshaler { @@ -8884,6 +9112,11 @@ func (ec *executionContext) _ConfigResult(ctx context.Context, sel ast.Selection if out.Values[i] == graphql.Null { invalids++ } + case "interface": + out.Values[i] = ec._ConfigResult_interface(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -9243,6 +9476,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "configureInterface": + out.Values[i] = ec._Mutation_configureInterface(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -10843,6 +11081,24 @@ func (ec *executionContext) marshalNConfigGeneralResult2ᚖgithubᚗcomᚋstasha return ec._ConfigGeneralResult(ctx, sel, v) } +func (ec *executionContext) unmarshalNConfigInterfaceInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceInput(ctx context.Context, v interface{}) (ConfigInterfaceInput, error) { + return ec.unmarshalInputConfigInterfaceInput(ctx, v) +} + +func (ec *executionContext) marshalNConfigInterfaceResult2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceResult(ctx context.Context, sel ast.SelectionSet, v ConfigInterfaceResult) graphql.Marshaler { + return ec._ConfigInterfaceResult(ctx, sel, &v) +} + +func (ec *executionContext) marshalNConfigInterfaceResult2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigInterfaceResult(ctx context.Context, sel ast.SelectionSet, v *ConfigInterfaceResult) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._ConfigInterfaceResult(ctx, sel, v) +} + func (ec *executionContext) marshalNConfigResult2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐConfigResult(ctx context.Context, sel ast.SelectionSet, v ConfigResult) graphql.Marshaler { return ec._ConfigResult(ctx, sel, &v) } diff --git a/pkg/models/generated_models.go b/pkg/models/generated_models.go index 351270d16..6967b914a 100644 --- a/pkg/models/generated_models.go +++ b/pkg/models/generated_models.go @@ -26,9 +26,22 @@ type ConfigGeneralResult struct { GeneratedPath string `json:"generatedPath"` } +type ConfigInterfaceInput struct { + // Custom CSS + CSS *string `json:"css"` + CSSEnabled *bool `json:"cssEnabled"` +} + +type ConfigInterfaceResult struct { + // Custom CSS + CSS *string `json:"css"` + CSSEnabled *bool `json:"cssEnabled"` +} + // All configuration settings type ConfigResult struct { - General *ConfigGeneralResult `json:"general"` + General *ConfigGeneralResult `json:"general"` + Interface *ConfigInterfaceResult `json:"interface"` } type FindFilterType struct { diff --git a/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx b/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx index 4ba7cf954..39982ccf2 100644 --- a/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx +++ b/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx @@ -1,19 +1,54 @@ import { + Button, Checkbox, + Divider, FormGroup, H4, + Spinner, + TextArea } from "@blueprintjs/core"; import _ from "lodash"; -import React, { FunctionComponent } from "react"; +import React, { FunctionComponent, useEffect, useState } from "react"; import { useInterfaceLocalForage } from "../../hooks/LocalForage"; +import { StashService } from "../../core/StashService"; +import { ErrorUtils } from "../../utils/errors"; +import { ToastUtils } from "../../utils/toasts"; interface IProps {} export const SettingsInterfacePanel: FunctionComponent = () => { const {data, setData} = useInterfaceLocalForage(); + const config = StashService.useConfiguration(); + const [css, setCSS] = useState(); + const [cssEnabled, setCSSEnabled] = useState(); + + const updateInterfaceConfig = StashService.useConfigureInterface({ + css, + cssEnabled + }); + + useEffect(() => { + if (!config.data || !config.data.configuration || !!config.error) { return; } + if (!!config.data.configuration.interface) { + setCSS(config.data.configuration.interface.css || ""); + setCSSEnabled(config.data.configuration.interface.cssEnabled || false); + } + }, [config.data]); + + async function onSave() { + try { + const result = await updateInterfaceConfig(); + console.log(result); + ToastUtils.success("Updated config"); + } catch (e) { + ErrorUtils.handle(e); + } + } return ( <> + {!!config.error ?

{config.error.message}

: undefined} + {(!config.data || !config.data.configuration || config.loading) ? : undefined}

User Interface

= () => { }} /> + + + { + setCSSEnabled(!cssEnabled) + }} + /> + + + + + + ); }; diff --git a/ui/v2/src/core/StashService.ts b/ui/v2/src/core/StashService.ts index 13c83c0dd..5475f3b2b 100644 --- a/ui/v2/src/core/StashService.ts +++ b/ui/v2/src/core/StashService.ts @@ -162,6 +162,10 @@ export class StashService { return GQL.useConfigureGeneral({ variables: { input }, refetchQueries: ["Configuration"] }); } + public static useConfigureInterface(input: GQL.ConfigInterfaceInput) { + return GQL.useConfigureInterface({ variables: { input }, refetchQueries: ["Configuration"] }); + } + public static queryScrapeFreeones(performerName: string) { return StashService.client.query({ query: GQL.ScrapeFreeonesDocument, diff --git a/ui/v2/src/core/generated-graphql.tsx b/ui/v2/src/core/generated-graphql.tsx index d0239e0b2..64085ad90 100644 --- a/ui/v2/src/core/generated-graphql.tsx +++ b/ui/v2/src/core/generated-graphql.tsx @@ -1,6 +1,6 @@ /* tslint:disable */ /* eslint-disable */ -// Generated in 2019-08-14T07:29:27+10:00 +// Generated in 2019-08-23T07:28:41+10:00 export type Maybe = T | undefined; export interface SceneFilterType { @@ -237,6 +237,13 @@ export interface ConfigGeneralInput { generatedPath?: Maybe; } +export interface ConfigInterfaceInput { + /** Custom CSS */ + css?: Maybe; + + cssEnabled?: Maybe; +} + export enum CriterionModifier { Equals = "EQUALS", NotEquals = "NOT_EQUALS", @@ -277,6 +284,18 @@ export type ConfigureGeneralMutation = { export type ConfigureGeneralConfigureGeneral = ConfigGeneralDataFragment; +export type ConfigureInterfaceVariables = { + input: ConfigInterfaceInput; +}; + +export type ConfigureInterfaceMutation = { + __typename?: "Mutation"; + + configureInterface: ConfigureInterfaceConfigureInterface; +}; + +export type ConfigureInterfaceConfigureInterface = ConfigInterfaceDataFragment; + export type PerformerCreateVariables = { name?: Maybe; url?: Maybe; @@ -932,14 +951,26 @@ export type ConfigGeneralDataFragment = { generatedPath: string; }; +export type ConfigInterfaceDataFragment = { + __typename?: "ConfigInterfaceResult"; + + css: Maybe; + + cssEnabled: Maybe; +}; + export type ConfigDataFragment = { __typename?: "ConfigResult"; general: ConfigDataGeneral; + + interface: ConfigDataInterface; }; export type ConfigDataGeneral = ConfigGeneralDataFragment; +export type ConfigDataInterface = ConfigInterfaceDataFragment; + export type GalleryDataFragment = { __typename?: "Gallery"; @@ -1315,14 +1346,25 @@ export const ConfigGeneralDataFragmentDoc = gql` } `; +export const ConfigInterfaceDataFragmentDoc = gql` + fragment ConfigInterfaceData on ConfigInterfaceResult { + css + cssEnabled + } +`; + export const ConfigDataFragmentDoc = gql` fragment ConfigData on ConfigResult { general { ...ConfigGeneralData } + interface { + ...ConfigInterfaceData + } } ${ConfigGeneralDataFragmentDoc} + ${ConfigInterfaceDataFragmentDoc} `; export const SlimPerformerDataFragmentDoc = gql` @@ -1554,6 +1596,26 @@ export function useConfigureGeneral( ConfigureGeneralVariables >(ConfigureGeneralDocument, baseOptions); } +export const ConfigureInterfaceDocument = gql` + mutation ConfigureInterface($input: ConfigInterfaceInput!) { + configureInterface(input: $input) { + ...ConfigInterfaceData + } + } + + ${ConfigInterfaceDataFragmentDoc} +`; +export function useConfigureInterface( + baseOptions?: ReactApolloHooks.MutationHookOptions< + ConfigureInterfaceMutation, + ConfigureInterfaceVariables + > +) { + return ReactApolloHooks.useMutation< + ConfigureInterfaceMutation, + ConfigureInterfaceVariables + >(ConfigureInterfaceDocument, baseOptions); +} export const PerformerCreateDocument = gql` mutation PerformerCreate( $name: String diff --git a/ui/v2/src/index.tsx b/ui/v2/src/index.tsx index bde16b201..28d0db3ac 100755 --- a/ui/v2/src/index.tsx +++ b/ui/v2/src/index.tsx @@ -8,11 +8,14 @@ import "./index.scss"; import * as serviceWorker from "./serviceWorker"; ReactDOM.render(( + <> + + ), document.getElementById("root")); // If you want your app to work offline and load faster, you can change