Merge branch 'master' into delete_scene

This commit is contained in:
WithoutPants 2019-08-20 15:37:58 +10:00 committed by GitHub
commit c3e4c5702a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 458 additions and 17 deletions

View file

@ -83,3 +83,7 @@ mutation PerformerUpdate(
...PerformerData
}
}
mutation PerformerDestroy($id: ID!) {
performerDestroy(input: { id: $id })
}

View file

@ -18,3 +18,7 @@ mutation StudioUpdate(
...StudioData
}
}
mutation StudioDestroy($id: ID!) {
studioDestroy(input: { id: $id })
}

View file

@ -80,9 +80,11 @@ type Mutation {
performerCreate(input: PerformerCreateInput!): Performer
performerUpdate(input: PerformerUpdateInput!): Performer
performerDestroy(input: PerformerDestroyInput!): Boolean!
studioCreate(input: StudioCreateInput!): Studio
studioUpdate(input: StudioUpdateInput!): Studio
studioDestroy(input: StudioDestroyInput!): Boolean!
tagCreate(input: TagCreateInput!): Tag
tagUpdate(input: TagUpdateInput!): Tag

View file

@ -66,6 +66,10 @@ input PerformerUpdateInput {
image: String
}
input PerformerDestroyInput {
id: ID!
}
type FindPerformersResultType {
count: Int!
performers: [Performer!]!

View file

@ -23,6 +23,10 @@ input StudioUpdateInput {
image: String
}
input StudioDestroyInput {
id: ID!
}
type FindStudiosResultType {
count: Int!
studios: [Studio!]!

View file

@ -175,3 +175,17 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per
return performer, nil
}
func (r *mutationResolver) PerformerDestroy(ctx context.Context, input models.PerformerDestroyInput) (bool, error) {
qb := models.NewPerformerQueryBuilder()
tx := database.DB.MustBeginTx(ctx, nil)
if err := qb.Destroy(input.ID, tx); err != nil {
_ = tx.Rollback()
return false, err
}
if err := tx.Commit(); err != nil {
return false, err
}
return true, nil
}

View file

@ -3,11 +3,12 @@ package api
import (
"context"
"database/sql"
"strconv"
"time"
"github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
"strconv"
"time"
)
func (r *mutationResolver) StudioCreate(ctx context.Context, input models.StudioCreateInput) (*models.Studio, error) {
@ -85,3 +86,16 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio
return studio, nil
}
func (r *mutationResolver) StudioDestroy(ctx context.Context, input models.StudioDestroyInput) (bool, error) {
qb := models.NewStudioQueryBuilder()
tx := database.DB.MustBeginTx(ctx, nil)
if err := qb.Destroy(input.ID, tx); err != nil {
_ = tx.Rollback()
return false, err
}
if err := tx.Commit(); err != nil {
return false, err
}
return true, nil
}

View file

@ -6,6 +6,7 @@ import (
"math"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
@ -28,6 +29,8 @@ type VideoFile struct {
VideoStream *FFProbeStream
Path string
Title string
Comment string
Container string
Duration float64
StartTime float64
@ -82,6 +85,14 @@ func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
//} // TODO nil_or_unsupported.(video_stream) && nil_or_unsupported.(audio_stream)
result.Path = filePath
result.Title = probeJSON.Format.Tags.Title
if result.Title == "" {
// default title to filename
result.Title = filepath.Base(result.Path)
}
result.Comment = probeJSON.Format.Tags.Comment
result.Bitrate, _ = strconv.ParseInt(probeJSON.Format.BitRate, 10, 64)
result.Container = probeJSON.Format.FormatName

View file

@ -22,6 +22,8 @@ type FFProbeJSON struct {
Encoder string `json:"encoder"`
MajorBrand string `json:"major_brand"`
MinorVersion string `json:"minor_version"`
Title string `json:"title"`
Comment string `json:"comment"`
} `json:"tags"`
} `json:"format"`
Streams []FFProbeStream `json:"streams"`

View file

@ -1,13 +1,14 @@
package manager
import (
"path/filepath"
"sync"
"github.com/bmatcuk/doublestar"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager/config"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
"path/filepath"
"sync"
)
func (s *singleton) Scan() {
@ -34,6 +35,8 @@ func (s *singleton) Scan() {
go task.Start(&wg)
wg.Wait()
}
logger.Info("Finished scan")
}()
}

View file

@ -3,15 +3,16 @@ package manager
import (
"context"
"database/sql"
"path/filepath"
"strconv"
"sync"
"time"
"github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/ffmpeg"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
"path/filepath"
"strconv"
"sync"
"time"
)
type ScanTask struct {
@ -46,9 +47,15 @@ func (t *ScanTask) scanGallery() {
tx := database.DB.MustBeginTx(ctx, nil)
gallery, _ = qb.FindByChecksum(checksum, tx)
if gallery != nil {
exists, _ := utils.FileExists(t.FilePath)
if exists {
logger.Infof("%s already exists. Duplicate of %s ", t.FilePath, gallery.Path)
} else {
logger.Infof("%s already exists. Updating path...", t.FilePath)
gallery.Path = t.FilePath
_, err = qb.Update(*gallery, tx)
}
} else {
logger.Infof("%s doesn't exist. Creating new item...", t.FilePath)
currentTime := time.Now()
@ -95,15 +102,23 @@ func (t *ScanTask) scanScene() {
ctx := context.TODO()
tx := database.DB.MustBeginTx(ctx, nil)
if scene != nil {
exists, _ := utils.FileExists(t.FilePath)
if exists {
logger.Infof("%s already exists. Duplicate of %s ", t.FilePath, scene.Path)
} else {
logger.Infof("%s already exists. Updating path...", t.FilePath)
scene.Path = t.FilePath
_, err = qb.Update(*scene, tx)
}
} else {
logger.Infof("%s doesn't exist. Creating new item...", t.FilePath)
currentTime := time.Now()
newScene := models.Scene{
Checksum: checksum,
Path: t.FilePath,
Title: sql.NullString{String: videoFile.Title, Valid: true},
Details: sql.NullString{String: videoFile.Comment, Valid: true},
Date: models.SQLiteDate{String: videoFile.CreationTime.Format("2006-01-02")},
Duration: sql.NullFloat64{Float64: videoFile.Duration, Valid: true},
VideoCodec: sql.NullString{String: videoFile.VideoCodec, Valid: true},
AudioCodec: sql.NullString{String: videoFile.AudioCodec, Valid: true},

View file

@ -108,6 +108,7 @@ type ComplexityRoot struct {
Mutation struct {
ConfigureGeneral func(childComplexity int, input ConfigGeneralInput) int
PerformerCreate func(childComplexity int, input PerformerCreateInput) int
PerformerDestroy func(childComplexity int, input PerformerDestroyInput) int
PerformerUpdate func(childComplexity int, input PerformerUpdateInput) int
SceneDestroy func(childComplexity int, input SceneDestroyInput) int
SceneMarkerCreate func(childComplexity int, input SceneMarkerCreateInput) int
@ -115,6 +116,7 @@ type ComplexityRoot struct {
SceneMarkerUpdate func(childComplexity int, input SceneMarkerUpdateInput) int
SceneUpdate func(childComplexity int, input SceneUpdateInput) int
StudioCreate func(childComplexity int, input StudioCreateInput) int
StudioDestroy func(childComplexity int, input StudioDestroyInput) int
StudioUpdate func(childComplexity int, input StudioUpdateInput) int
TagCreate func(childComplexity int, input TagCreateInput) int
TagDestroy func(childComplexity int, input TagDestroyInput) int
@ -290,8 +292,10 @@ type MutationResolver interface {
SceneMarkerDestroy(ctx context.Context, id string) (bool, error)
PerformerCreate(ctx context.Context, input PerformerCreateInput) (*Performer, error)
PerformerUpdate(ctx context.Context, input PerformerUpdateInput) (*Performer, error)
PerformerDestroy(ctx context.Context, input PerformerDestroyInput) (bool, error)
StudioCreate(ctx context.Context, input StudioCreateInput) (*Studio, error)
StudioUpdate(ctx context.Context, input StudioUpdateInput) (*Studio, error)
StudioDestroy(ctx context.Context, input StudioDestroyInput) (bool, error)
TagCreate(ctx context.Context, input TagCreateInput) (*Tag, error)
TagUpdate(ctx context.Context, input TagUpdateInput) (*Tag, error)
TagDestroy(ctx context.Context, input TagDestroyInput) (bool, error)
@ -600,6 +604,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.PerformerCreate(childComplexity, args["input"].(PerformerCreateInput)), true
case "Mutation.performerDestroy":
if e.complexity.Mutation.PerformerDestroy == nil {
break
}
args, err := ec.field_Mutation_performerDestroy_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.PerformerDestroy(childComplexity, args["input"].(PerformerDestroyInput)), true
case "Mutation.performerUpdate":
if e.complexity.Mutation.PerformerUpdate == nil {
break
@ -684,6 +700,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.StudioCreate(childComplexity, args["input"].(StudioCreateInput)), true
case "Mutation.studioDestroy":
if e.complexity.Mutation.StudioDestroy == nil {
break
}
args, err := ec.field_Mutation_studioDestroy_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.StudioDestroy(childComplexity, args["input"].(StudioDestroyInput)), true
case "Mutation.studioUpdate":
if e.complexity.Mutation.StudioUpdate == nil {
break
@ -1855,9 +1883,11 @@ type Mutation {
performerCreate(input: PerformerCreateInput!): Performer
performerUpdate(input: PerformerUpdateInput!): Performer
performerDestroy(input: PerformerDestroyInput!): Boolean!
studioCreate(input: StudioCreateInput!): Studio
studioUpdate(input: StudioUpdateInput!): Studio
studioDestroy(input: StudioDestroyInput!): Boolean!
tagCreate(input: TagCreateInput!): Tag
tagUpdate(input: TagUpdateInput!): Tag
@ -2069,6 +2099,10 @@ input PerformerUpdateInput {
image: String
}
input PerformerDestroyInput {
id: ID!
}
type FindPerformersResultType {
count: Int!
performers: [Performer!]!
@ -2233,6 +2267,10 @@ input StudioUpdateInput {
image: String
}
input StudioDestroyInput {
id: ID!
}
type FindStudiosResultType {
count: Int!
studios: [Studio!]!
@ -2291,6 +2329,20 @@ func (ec *executionContext) field_Mutation_performerCreate_args(ctx context.Cont
return args, nil
}
func (ec *executionContext) field_Mutation_performerDestroy_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 PerformerDestroyInput
if tmp, ok := rawArgs["input"]; ok {
arg0, err = ec.unmarshalNPerformerDestroyInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐPerformerDestroyInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_performerUpdate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -2389,6 +2441,20 @@ func (ec *executionContext) field_Mutation_studioCreate_args(ctx context.Context
return args, nil
}
func (ec *executionContext) field_Mutation_studioDestroy_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 StudioDestroyInput
if tmp, ok := rawArgs["input"]; ok {
arg0, err = ec.unmarshalNStudioDestroyInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐStudioDestroyInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_studioUpdate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -3694,6 +3760,40 @@ func (ec *executionContext) _Mutation_performerUpdate(ctx context.Context, field
return ec.marshalOPerformer2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐPerformer(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_performerDestroy(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_performerDestroy_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().PerformerDestroy(rctx, args["input"].(PerformerDestroyInput))
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_studioCreate(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
@ -3756,6 +3856,40 @@ func (ec *executionContext) _Mutation_studioUpdate(ctx context.Context, field gr
return ec.marshalOStudio2ᚖgithubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐStudio(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_studioDestroy(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_studioDestroy_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().StudioDestroy(rctx, args["input"].(StudioDestroyInput))
})
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_tagCreate(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
@ -8200,6 +8334,24 @@ func (ec *executionContext) unmarshalInputPerformerCreateInput(ctx context.Conte
return it, nil
}
func (ec *executionContext) unmarshalInputPerformerDestroyInput(ctx context.Context, v interface{}) (PerformerDestroyInput, error) {
var it PerformerDestroyInput
var asMap = v.(map[string]interface{})
for k, v := range asMap {
switch k {
case "id":
var err error
it.ID, err = ec.unmarshalNID2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputPerformerFilterType(ctx context.Context, v interface{}) (PerformerFilterType, error) {
var it PerformerFilterType
var asMap = v.(map[string]interface{})
@ -8656,6 +8808,24 @@ func (ec *executionContext) unmarshalInputStudioCreateInput(ctx context.Context,
return it, nil
}
func (ec *executionContext) unmarshalInputStudioDestroyInput(ctx context.Context, v interface{}) (StudioDestroyInput, error) {
var it StudioDestroyInput
var asMap = v.(map[string]interface{})
for k, v := range asMap {
switch k {
case "id":
var err error
it.ID, err = ec.unmarshalNID2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputStudioUpdateInput(ctx context.Context, v interface{}) (StudioUpdateInput, error) {
var it StudioUpdateInput
var asMap = v.(map[string]interface{})
@ -9149,10 +9319,20 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
out.Values[i] = ec._Mutation_performerCreate(ctx, field)
case "performerUpdate":
out.Values[i] = ec._Mutation_performerUpdate(ctx, field)
case "performerDestroy":
out.Values[i] = ec._Mutation_performerDestroy(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "studioCreate":
out.Values[i] = ec._Mutation_studioCreate(ctx, field)
case "studioUpdate":
out.Values[i] = ec._Mutation_studioUpdate(ctx, field)
case "studioDestroy":
out.Values[i] = ec._Mutation_studioDestroy(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "tagCreate":
out.Values[i] = ec._Mutation_tagCreate(ctx, field)
case "tagUpdate":
@ -11114,6 +11294,10 @@ func (ec *executionContext) unmarshalNPerformerCreateInput2githubᚗcomᚋstasha
return ec.unmarshalInputPerformerCreateInput(ctx, v)
}
func (ec *executionContext) unmarshalNPerformerDestroyInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐPerformerDestroyInput(ctx context.Context, v interface{}) (PerformerDestroyInput, error) {
return ec.unmarshalInputPerformerDestroyInput(ctx, v)
}
func (ec *executionContext) unmarshalNPerformerUpdateInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐPerformerUpdateInput(ctx context.Context, v interface{}) (PerformerUpdateInput, error) {
return ec.unmarshalInputPerformerUpdateInput(ctx, v)
}
@ -11427,6 +11611,10 @@ func (ec *executionContext) unmarshalNStudioCreateInput2githubᚗcomᚋstashapp
return ec.unmarshalInputStudioCreateInput(ctx, v)
}
func (ec *executionContext) unmarshalNStudioDestroyInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐStudioDestroyInput(ctx context.Context, v interface{}) (StudioDestroyInput, error) {
return ec.unmarshalInputStudioDestroyInput(ctx, v)
}
func (ec *executionContext) unmarshalNStudioUpdateInput2githubᚗcomᚋstashappᚋstashᚋpkgᚋmodelsᚐStudioUpdateInput(ctx context.Context, v interface{}) (StudioUpdateInput, error) {
return ec.unmarshalInputStudioUpdateInput(ctx, v)
}

View file

@ -109,6 +109,10 @@ type PerformerCreateInput struct {
Image string `json:"image"`
}
type PerformerDestroyInput struct {
ID string `json:"id"`
}
type PerformerFilterType struct {
// Filter by favorite
FilterFavorites *bool `json:"filter_favorites"`
@ -260,6 +264,10 @@ type StudioCreateInput struct {
Image string `json:"image"`
}
type StudioDestroyInput struct {
ID string `json:"id"`
}
type StudioUpdateInput struct {
ID string `json:"id"`
Name *string `json:"name"`

View file

@ -2,6 +2,7 @@ package models
import (
"database/sql"
"github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/database"
)
@ -54,6 +55,15 @@ func (qb *PerformerQueryBuilder) Update(updatedPerformer Performer, tx *sqlx.Tx)
return &updatedPerformer, nil
}
func (qb *PerformerQueryBuilder) Destroy(id string, tx *sqlx.Tx) error {
_, err := tx.Exec("DELETE FROM performers_scenes WHERE performer_id = ?", id)
if err != nil {
return err
}
return executeDeleteQuery("performers", id, tx)
}
func (qb *PerformerQueryBuilder) Find(id int) (*Performer, error) {
query := "SELECT * FROM performers WHERE id = ? LIMIT 1"
args := []interface{}{id}

View file

@ -2,6 +2,7 @@ package models
import (
"database/sql"
"github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/database"
)
@ -50,6 +51,22 @@ func (qb *StudioQueryBuilder) Update(updatedStudio Studio, tx *sqlx.Tx) (*Studio
return &updatedStudio, nil
}
func (qb *StudioQueryBuilder) Destroy(id string, tx *sqlx.Tx) error {
// remove studio from scenes
_, err := tx.Exec("UPDATE scenes SET studio_id = null WHERE studio_id = ?", id)
if err != nil {
return err
}
// remove studio from scraped items
_, err = tx.Exec("UPDATE scraped_items SET studio_id = null WHERE studio_id = ?", id)
if err != nil {
return err
}
return executeDeleteQuery("studios", id, tx)
}
func (qb *StudioQueryBuilder) Find(id int, tx *sqlx.Tx) (*Studio, error) {
query := "SELECT * FROM studios WHERE id = ? LIMIT 1"
args := []interface{}{id}
@ -86,7 +103,7 @@ func (qb *StudioQueryBuilder) Query(findFilter *FindFilterType) ([]*Studio, int)
var args []interface{}
body := selectDistinctIDs("studios")
body += `
join scenes on studios.id = scenes.studio_id
left join scenes on studios.id = scenes.studio_id
`
if q := findFilter.Q; q != nil && *q != "" {

View file

@ -1,4 +1,5 @@
import {
Alert,
Button,
FileInput,
Menu,
@ -8,7 +9,7 @@ import {
Popover,
} from "@blueprintjs/core";
import _ from "lodash";
import React, { FunctionComponent } from "react";
import React, { FunctionComponent, useState } from "react";
import { Link } from "react-router-dom";
import * as GQL from "../../core/generated-graphql";
import { NavigationUtils } from "../../utils/navigation";
@ -20,6 +21,7 @@ interface IProps {
isEditing: boolean;
onToggleEdit: () => void;
onSave: () => void;
onDelete: () => void;
onImageChange: (event: React.FormEvent<HTMLInputElement>) => void;
// TODO: only for performers. make generic
@ -27,6 +29,8 @@ interface IProps {
}
export const DetailsEditNavbar: FunctionComponent<IProps> = (props: IProps) => {
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
function renderEditButton() {
if (props.isNew) { return; }
return (
@ -43,6 +47,11 @@ export const DetailsEditNavbar: FunctionComponent<IProps> = (props: IProps) => {
return <Button intent="success" text="Save" onClick={() => props.onSave()} />;
}
function renderDeleteButton() {
if (props.isNew || props.isEditing) { return; }
return <Button intent="danger" text="Delete" onClick={() => setIsDeleteAlertOpen(true)} />;
}
function renderImageInput() {
if (!props.isEditing) { return; }
return <FileInput text="Choose image..." onInputChange={props.onImageChange} inputProps={{accept: ".jpg,.jpeg"}} />;
@ -81,7 +90,37 @@ export const DetailsEditNavbar: FunctionComponent<IProps> = (props: IProps) => {
);
}
function renderDeleteAlert() {
var name;
if (props.performer) {
name = props.performer.name;
}
if (props.studio) {
name = props.studio.name;
}
return (
<Alert
cancelButtonText="Cancel"
confirmButtonText="Delete"
icon="trash"
intent="danger"
isOpen={isDeleteAlertOpen}
onCancel={() => setIsDeleteAlertOpen(false)}
onConfirm={() => props.onDelete()}
>
<p>
Are you sure you want to delete {name}?
</p>
</Alert>
);
}
return (
<>
{renderDeleteAlert()}
<Navbar>
<Navbar.Group>
{renderEditButton()}
@ -91,7 +130,9 @@ export const DetailsEditNavbar: FunctionComponent<IProps> = (props: IProps) => {
{renderSaveButton()}
{renderScenesButton()}
{renderDeleteButton()}
</Navbar.Group>
</Navbar>
</>
);
};

View file

@ -39,6 +39,7 @@ export const Studio: FunctionComponent<IProps> = (props: IProps) => {
const { data, error, loading } = StashService.useFindStudio(props.match.params.id);
const updateStudio = StashService.useStudioUpdate(getStudioInput() as GQL.StudioUpdateInput);
const createStudio = StashService.useStudioCreate(getStudioInput() as GQL.StudioCreateInput);
const deleteStudio = StashService.useStudioDestroy(getStudioInput() as GQL.StudioDestroyInput);
function updateStudioEditState(state: Partial<GQL.StudioDataFragment>) {
setName(state.name);
@ -95,6 +96,19 @@ export const Studio: FunctionComponent<IProps> = (props: IProps) => {
setIsLoading(false);
}
async function onDelete() {
setIsLoading(true);
try {
const result = await deleteStudio();
} catch (e) {
ErrorUtils.handle(e);
}
setIsLoading(false);
// redirect to studios page
props.history.push(`/studios`);
}
function onImageChange(event: React.FormEvent<HTMLInputElement>) {
const file: File = (event.target as any).files[0];
const reader: FileReader = new FileReader();
@ -120,6 +134,7 @@ export const Studio: FunctionComponent<IProps> = (props: IProps) => {
isEditing={isEditing}
onToggleEdit={() => { setIsEditing(!isEditing); updateStudioEditState(studio); }}
onSave={onSave}
onDelete={onDelete}
onImageChange={onImageChange}
/>
<h1 className="bp3-heading">

View file

@ -55,6 +55,7 @@ export const Performer: FunctionComponent<IPerformerProps> = (props: IPerformerP
const { data, error, loading } = StashService.useFindPerformer(props.match.params.id);
const updatePerformer = StashService.usePerformerUpdate(getPerformerInput() as GQL.PerformerUpdateInput);
const createPerformer = StashService.usePerformerCreate(getPerformerInput() as GQL.PerformerCreateInput);
const deletePerformer = StashService.usePerformerDestroy(getPerformerInput() as GQL.PerformerDestroyInput);
function updatePerformerEditState(state: Partial<GQL.PerformerDataFragment | GQL.ScrapeFreeonesScrapeFreeones>) {
if ((state as GQL.PerformerDataFragment).favorite !== undefined) {
@ -141,6 +142,19 @@ export const Performer: FunctionComponent<IPerformerProps> = (props: IPerformerP
setIsLoading(false);
}
async function onDelete() {
setIsLoading(true);
try {
const result = await deletePerformer();
} catch (e) {
ErrorUtils.handle(e);
}
setIsLoading(false);
// redirect to performers page
props.history.push(`/performers`);
}
function onImageChange(event: React.FormEvent<HTMLInputElement>) {
const file: File = (event.target as any).files[0];
const reader: FileReader = new FileReader();
@ -218,6 +232,7 @@ export const Performer: FunctionComponent<IPerformerProps> = (props: IPerformerP
isEditing={isEditing}
onToggleEdit={() => { setIsEditing(!isEditing); updatePerformerEditState(performer); }}
onSave={onSave}
onDelete={onDelete}
onImageChange={onImageChange}
onDisplayFreeOnesDialog={onDisplayFreeOnesDialog}
/>

View file

@ -133,6 +133,9 @@ export class StashService {
public static usePerformerUpdate(input: GQL.PerformerUpdateInput) {
return GQL.usePerformerUpdate({ variables: input });
}
public static usePerformerDestroy(input: GQL.PerformerDestroyInput) {
return GQL.usePerformerDestroy({ variables: input });
}
public static useSceneUpdate(input: GQL.SceneUpdateInput) {
return GQL.useSceneUpdate({ variables: input });
@ -148,6 +151,9 @@ export class StashService {
public static useStudioUpdate(input: GQL.StudioUpdateInput) {
return GQL.useStudioUpdate({ variables: input });
}
public static useStudioDestroy(input: GQL.StudioDestroyInput) {
return GQL.useStudioDestroy({ variables: input });
}
public static useTagCreate(input: GQL.TagCreateInput) {
return GQL.useTagCreate({ variables: input, refetchQueries: ["AllTags"] });

View file

@ -196,6 +196,10 @@ export interface PerformerUpdateInput {
image?: Maybe<string>;
}
export interface PerformerDestroyInput {
id: string;
}
export interface StudioCreateInput {
name: string;
@ -214,6 +218,10 @@ export interface StudioUpdateInput {
image?: Maybe<string>;
}
export interface StudioDestroyInput {
id: string;
}
export interface TagCreateInput {
name: string;
}
@ -334,6 +342,16 @@ export type PerformerUpdateMutation = {
export type PerformerUpdatePerformerUpdate = PerformerDataFragment;
export type PerformerDestroyVariables = {
id: string;
};
export type PerformerDestroyMutation = {
__typename?: "Mutation";
performerDestroy: boolean;
};
export type SceneMarkerCreateVariables = {
title: string;
seconds: number;
@ -439,6 +457,16 @@ export type StudioUpdateMutation = {
export type StudioUpdateStudioUpdate = StudioDataFragment;
export type StudioDestroyVariables = {
id: string;
};
export type StudioDestroyMutation = {
__typename?: "Mutation";
studioDestroy: boolean;
};
export type TagCreateVariables = {
name: string;
};
@ -1664,6 +1692,22 @@ export function usePerformerUpdate(
PerformerUpdateVariables
>(PerformerUpdateDocument, baseOptions);
}
export const PerformerDestroyDocument = gql`
mutation PerformerDestroy($id: ID!) {
performerDestroy(input: { id: $id })
}
`;
export function usePerformerDestroy(
baseOptions?: ReactApolloHooks.MutationHookOptions<
PerformerDestroyMutation,
PerformerDestroyVariables
>
) {
return ReactApolloHooks.useMutation<
PerformerDestroyMutation,
PerformerDestroyVariables
>(PerformerDestroyDocument, baseOptions);
}
export const SceneMarkerCreateDocument = gql`
mutation SceneMarkerCreate(
$title: String!
@ -1860,6 +1904,22 @@ export function useStudioUpdate(
StudioUpdateVariables
>(StudioUpdateDocument, baseOptions);
}
export const StudioDestroyDocument = gql`
mutation StudioDestroy($id: ID!) {
studioDestroy(input: { id: $id })
}
`;
export function useStudioDestroy(
baseOptions?: ReactApolloHooks.MutationHookOptions<
StudioDestroyMutation,
StudioDestroyVariables
>
) {
return ReactApolloHooks.useMutation<
StudioDestroyMutation,
StudioDestroyVariables
>(StudioDestroyDocument, baseOptions);
}
export const TagCreateDocument = gql`
mutation TagCreate($name: String!) {
tagCreate(input: { name: $name }) {

View file

@ -186,10 +186,14 @@ span.block {
& .tag-list-row {
margin: 10px;
cursor: pointer;
& .bp3-button {
margin: 0 10px;
}
}
}
& .tag-list-row:hover {
text-decoration: underline;
}
}