mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-15 10:12:06 +03:00
Merge pull request #323 from MichaelMure/webui/typescript
Webui/typescript
This commit is contained in:
commit
0066f3d8c2
12
cache/repo_cache.go
vendored
12
cache/repo_cache.go
vendored
@ -58,6 +58,9 @@ type RepoCache struct {
|
|||||||
// the underlying repo
|
// the underlying repo
|
||||||
repo repository.ClockedRepo
|
repo repository.ClockedRepo
|
||||||
|
|
||||||
|
// the name of the repository, as defined in the MultiRepoCache
|
||||||
|
name string
|
||||||
|
|
||||||
muBug sync.RWMutex
|
muBug sync.RWMutex
|
||||||
// excerpt of bugs data for all bugs
|
// excerpt of bugs data for all bugs
|
||||||
bugExcerpts map[entity.Id]*BugExcerpt
|
bugExcerpts map[entity.Id]*BugExcerpt
|
||||||
@ -75,8 +78,13 @@ type RepoCache struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
|
func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
|
||||||
|
return NewNamedRepoCache(r, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, error) {
|
||||||
c := &RepoCache{
|
c := &RepoCache{
|
||||||
repo: r,
|
repo: r,
|
||||||
|
name: name,
|
||||||
bugs: make(map[entity.Id]*BugCache),
|
bugs: make(map[entity.Id]*BugCache),
|
||||||
identities: make(map[entity.Id]*IdentityCache),
|
identities: make(map[entity.Id]*IdentityCache),
|
||||||
}
|
}
|
||||||
@ -102,6 +110,10 @@ func NewRepoCache(r repository.ClockedRepo) (*RepoCache, error) {
|
|||||||
return c, c.write()
|
return c, c.write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RepoCache) Name() string {
|
||||||
|
return c.name
|
||||||
|
}
|
||||||
|
|
||||||
// LocalConfig give access to the repository scoped configuration
|
// LocalConfig give access to the repository scoped configuration
|
||||||
func (c *RepoCache) LocalConfig() repository.Config {
|
func (c *RepoCache) LocalConfig() repository.Config {
|
||||||
return c.repo.LocalConfig()
|
return c.repo.LocalConfig()
|
||||||
|
@ -163,16 +163,6 @@ type ComplexityRoot struct {
|
|||||||
Message func(childComplexity int) int
|
Message func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitAsNeededPayload struct {
|
|
||||||
Bug func(childComplexity int) int
|
|
||||||
ClientMutationID func(childComplexity int) int
|
|
||||||
}
|
|
||||||
|
|
||||||
CommitPayload struct {
|
|
||||||
Bug func(childComplexity int) int
|
|
||||||
ClientMutationID func(childComplexity int) int
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateOperation struct {
|
CreateOperation struct {
|
||||||
Author func(childComplexity int) int
|
Author func(childComplexity int) int
|
||||||
Date func(childComplexity int) int
|
Date func(childComplexity int) int
|
||||||
@ -264,14 +254,12 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mutation struct {
|
Mutation struct {
|
||||||
AddComment func(childComplexity int, input models.AddCommentInput) int
|
AddComment func(childComplexity int, input models.AddCommentInput) int
|
||||||
ChangeLabels func(childComplexity int, input *models.ChangeLabelInput) int
|
ChangeLabels func(childComplexity int, input *models.ChangeLabelInput) int
|
||||||
CloseBug func(childComplexity int, input models.CloseBugInput) int
|
CloseBug func(childComplexity int, input models.CloseBugInput) int
|
||||||
Commit func(childComplexity int, input models.CommitInput) int
|
NewBug func(childComplexity int, input models.NewBugInput) int
|
||||||
CommitAsNeeded func(childComplexity int, input models.CommitAsNeededInput) int
|
OpenBug func(childComplexity int, input models.OpenBugInput) int
|
||||||
NewBug func(childComplexity int, input models.NewBugInput) int
|
SetTitle func(childComplexity int, input models.SetTitleInput) int
|
||||||
OpenBug func(childComplexity int, input models.OpenBugInput) int
|
|
||||||
SetTitle func(childComplexity int, input models.SetTitleInput) int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NewBugPayload struct {
|
NewBugPayload struct {
|
||||||
@ -306,8 +294,7 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Query struct {
|
Query struct {
|
||||||
DefaultRepository func(childComplexity int) int
|
Repository func(childComplexity int, ref *string) int
|
||||||
Repository func(childComplexity int, ref string) int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repository struct {
|
Repository struct {
|
||||||
@ -315,6 +302,7 @@ type ComplexityRoot struct {
|
|||||||
AllIdentities func(childComplexity int, after *string, before *string, first *int, last *int) int
|
AllIdentities func(childComplexity int, after *string, before *string, first *int, last *int) int
|
||||||
Bug func(childComplexity int, prefix string) int
|
Bug func(childComplexity int, prefix string) int
|
||||||
Identity func(childComplexity int, prefix string) int
|
Identity func(childComplexity int, prefix string) int
|
||||||
|
Name func(childComplexity int) int
|
||||||
UserIdentity func(childComplexity int) int
|
UserIdentity func(childComplexity int) int
|
||||||
ValidLabels func(childComplexity int, after *string, before *string, first *int, last *int) int
|
ValidLabels func(childComplexity int, after *string, before *string, first *int, last *int) int
|
||||||
}
|
}
|
||||||
@ -448,14 +436,12 @@ type MutationResolver interface {
|
|||||||
OpenBug(ctx context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error)
|
OpenBug(ctx context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error)
|
||||||
CloseBug(ctx context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error)
|
CloseBug(ctx context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error)
|
||||||
SetTitle(ctx context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error)
|
SetTitle(ctx context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error)
|
||||||
Commit(ctx context.Context, input models.CommitInput) (*models.CommitPayload, error)
|
|
||||||
CommitAsNeeded(ctx context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error)
|
|
||||||
}
|
}
|
||||||
type QueryResolver interface {
|
type QueryResolver interface {
|
||||||
DefaultRepository(ctx context.Context) (*models.Repository, error)
|
Repository(ctx context.Context, ref *string) (*models.Repository, error)
|
||||||
Repository(ctx context.Context, ref string) (*models.Repository, error)
|
|
||||||
}
|
}
|
||||||
type RepositoryResolver interface {
|
type RepositoryResolver interface {
|
||||||
|
Name(ctx context.Context, obj *models.Repository) (*string, error)
|
||||||
AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BugConnection, error)
|
AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BugConnection, error)
|
||||||
Bug(ctx context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error)
|
Bug(ctx context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error)
|
||||||
AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
|
AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
|
||||||
@ -925,34 +911,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.CommentHistoryStep.Message(childComplexity), true
|
return e.complexity.CommentHistoryStep.Message(childComplexity), true
|
||||||
|
|
||||||
case "CommitAsNeededPayload.bug":
|
|
||||||
if e.complexity.CommitAsNeededPayload.Bug == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.CommitAsNeededPayload.Bug(childComplexity), true
|
|
||||||
|
|
||||||
case "CommitAsNeededPayload.clientMutationId":
|
|
||||||
if e.complexity.CommitAsNeededPayload.ClientMutationID == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.CommitAsNeededPayload.ClientMutationID(childComplexity), true
|
|
||||||
|
|
||||||
case "CommitPayload.bug":
|
|
||||||
if e.complexity.CommitPayload.Bug == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.CommitPayload.Bug(childComplexity), true
|
|
||||||
|
|
||||||
case "CommitPayload.clientMutationId":
|
|
||||||
if e.complexity.CommitPayload.ClientMutationID == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.CommitPayload.ClientMutationID(childComplexity), true
|
|
||||||
|
|
||||||
case "CreateOperation.author":
|
case "CreateOperation.author":
|
||||||
if e.complexity.CreateOperation.Author == nil {
|
if e.complexity.CreateOperation.Author == nil {
|
||||||
break
|
break
|
||||||
@ -1367,30 +1325,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.CloseBug(childComplexity, args["input"].(models.CloseBugInput)), true
|
return e.complexity.Mutation.CloseBug(childComplexity, args["input"].(models.CloseBugInput)), true
|
||||||
|
|
||||||
case "Mutation.commit":
|
|
||||||
if e.complexity.Mutation.Commit == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
args, err := ec.field_Mutation_commit_args(context.TODO(), rawArgs)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.Mutation.Commit(childComplexity, args["input"].(models.CommitInput)), true
|
|
||||||
|
|
||||||
case "Mutation.commitAsNeeded":
|
|
||||||
if e.complexity.Mutation.CommitAsNeeded == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
args, err := ec.field_Mutation_commitAsNeeded_args(context.TODO(), rawArgs)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.Mutation.CommitAsNeeded(childComplexity, args["input"].(models.CommitAsNeededInput)), true
|
|
||||||
|
|
||||||
case "Mutation.newBug":
|
case "Mutation.newBug":
|
||||||
if e.complexity.Mutation.NewBug == nil {
|
if e.complexity.Mutation.NewBug == nil {
|
||||||
break
|
break
|
||||||
@ -1539,13 +1473,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.PageInfo.StartCursor(childComplexity), true
|
return e.complexity.PageInfo.StartCursor(childComplexity), true
|
||||||
|
|
||||||
case "Query.defaultRepository":
|
|
||||||
if e.complexity.Query.DefaultRepository == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.Query.DefaultRepository(childComplexity), true
|
|
||||||
|
|
||||||
case "Query.repository":
|
case "Query.repository":
|
||||||
if e.complexity.Query.Repository == nil {
|
if e.complexity.Query.Repository == nil {
|
||||||
break
|
break
|
||||||
@ -1556,7 +1483,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.Repository(childComplexity, args["ref"].(string)), true
|
return e.complexity.Query.Repository(childComplexity, args["ref"].(*string)), true
|
||||||
|
|
||||||
case "Repository.allBugs":
|
case "Repository.allBugs":
|
||||||
if e.complexity.Repository.AllBugs == nil {
|
if e.complexity.Repository.AllBugs == nil {
|
||||||
@ -1606,6 +1533,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Repository.Identity(childComplexity, args["prefix"].(string)), true
|
return e.complexity.Repository.Identity(childComplexity, args["prefix"].(string)), true
|
||||||
|
|
||||||
|
case "Repository.name":
|
||||||
|
if e.complexity.Repository.Name == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Repository.Name(childComplexity), true
|
||||||
|
|
||||||
case "Repository.userIdentity":
|
case "Repository.userIdentity":
|
||||||
if e.complexity.Repository.UserIdentity == nil {
|
if e.complexity.Repository.UserIdentity == nil {
|
||||||
break
|
break
|
||||||
@ -2184,38 +2118,6 @@ type SetTitlePayload {
|
|||||||
"""The resulting operation"""
|
"""The resulting operation"""
|
||||||
operation: SetTitleOperation!
|
operation: SetTitleOperation!
|
||||||
}
|
}
|
||||||
|
|
||||||
input CommitInput {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
""""The name of the repository. If not set, the default repository is used."""
|
|
||||||
repoRef: String
|
|
||||||
"""The bug ID's prefix."""
|
|
||||||
prefix: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitPayload {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
"""The affected bug."""
|
|
||||||
bug: Bug!
|
|
||||||
}
|
|
||||||
|
|
||||||
input CommitAsNeededInput {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
""""The name of the repository. If not set, the default repository is used."""
|
|
||||||
repoRef: String
|
|
||||||
"""The bug ID's prefix."""
|
|
||||||
prefix: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitAsNeededPayload {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
"""The affected bug."""
|
|
||||||
bug: Bug!
|
|
||||||
}
|
|
||||||
`, BuiltIn: false},
|
`, BuiltIn: false},
|
||||||
&ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug."""
|
&ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug."""
|
||||||
interface Operation {
|
interface Operation {
|
||||||
@ -2320,6 +2222,9 @@ type LabelChangeOperation implements Operation & Authored {
|
|||||||
`, BuiltIn: false},
|
`, BuiltIn: false},
|
||||||
&ast.Source{Name: "schema/repository.graphql", Input: `
|
&ast.Source{Name: "schema/repository.graphql", Input: `
|
||||||
type Repository {
|
type Repository {
|
||||||
|
"""The name of the repository"""
|
||||||
|
name: String
|
||||||
|
|
||||||
"""All the bugs"""
|
"""All the bugs"""
|
||||||
allBugs(
|
allBugs(
|
||||||
"""Returns the elements in the list that come after the specified cursor."""
|
"""Returns the elements in the list that come after the specified cursor."""
|
||||||
@ -2330,7 +2235,7 @@ type Repository {
|
|||||||
first: Int
|
first: Int
|
||||||
"""Returns the last _n_ elements from the list."""
|
"""Returns the last _n_ elements from the list."""
|
||||||
last: Int
|
last: Int
|
||||||
"""A query to select and order bugs"""
|
"""A query to select and order bugs."""
|
||||||
query: String
|
query: String
|
||||||
): BugConnection!
|
): BugConnection!
|
||||||
|
|
||||||
@ -2366,12 +2271,8 @@ type Repository {
|
|||||||
): LabelConnection!
|
): LabelConnection!
|
||||||
}`, BuiltIn: false},
|
}`, BuiltIn: false},
|
||||||
&ast.Source{Name: "schema/root.graphql", Input: `type Query {
|
&ast.Source{Name: "schema/root.graphql", Input: `type Query {
|
||||||
"""The default unnamend repository."""
|
"""Access a repository by reference/name. If no ref is given, the default repository is returned if any."""
|
||||||
defaultRepository: Repository
|
repository(ref: String): Repository
|
||||||
"""Access a repository by reference/name."""
|
|
||||||
repository(ref: String!): Repository
|
|
||||||
|
|
||||||
#TODO: connection for all repositories
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
@ -2387,10 +2288,6 @@ type Mutation {
|
|||||||
closeBug(input: CloseBugInput!): CloseBugPayload!
|
closeBug(input: CloseBugInput!): CloseBugPayload!
|
||||||
"""Change a bug's title"""
|
"""Change a bug's title"""
|
||||||
setTitle(input: SetTitleInput!): SetTitlePayload!
|
setTitle(input: SetTitleInput!): SetTitlePayload!
|
||||||
"""Commit write the pending operations into storage. This mutation fail if nothing is pending"""
|
|
||||||
commit(input: CommitInput!): CommitPayload!
|
|
||||||
"""Commit write the pending operations into storage. This mutation succed if nothing is pending"""
|
|
||||||
commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload!
|
|
||||||
}
|
}
|
||||||
`, BuiltIn: false},
|
`, BuiltIn: false},
|
||||||
&ast.Source{Name: "schema/timeline.graphql", Input: `"""An item in the timeline of events"""
|
&ast.Source{Name: "schema/timeline.graphql", Input: `"""An item in the timeline of events"""
|
||||||
@ -2750,34 +2647,6 @@ func (ec *executionContext) field_Mutation_closeBug_args(ctx context.Context, ra
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_commitAsNeeded_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
|
||||||
var err error
|
|
||||||
args := map[string]interface{}{}
|
|
||||||
var arg0 models.CommitAsNeededInput
|
|
||||||
if tmp, ok := rawArgs["input"]; ok {
|
|
||||||
arg0, err = ec.unmarshalNCommitAsNeededInput2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitAsNeededInput(ctx, tmp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args["input"] = arg0
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_commit_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
|
||||||
var err error
|
|
||||||
args := map[string]interface{}{}
|
|
||||||
var arg0 models.CommitInput
|
|
||||||
if tmp, ok := rawArgs["input"]; ok {
|
|
||||||
arg0, err = ec.unmarshalNCommitInput2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitInput(ctx, tmp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args["input"] = arg0
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_newBug_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_newBug_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@ -2837,9 +2706,9 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
|
|||||||
func (ec *executionContext) field_Query_repository_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Query_repository_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
var arg0 string
|
var arg0 *string
|
||||||
if tmp, ok := rawArgs["ref"]; ok {
|
if tmp, ok := rawArgs["ref"]; ok {
|
||||||
arg0, err = ec.unmarshalNString2string(ctx, tmp)
|
arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -4998,136 +4867,6 @@ func (ec *executionContext) _CommentHistoryStep_date(ctx context.Context, field
|
|||||||
return ec.marshalNTime2ᚖtimeᚐTime(ctx, field.Selections, res)
|
return ec.marshalNTime2ᚖtimeᚐTime(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _CommitAsNeededPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.CommitAsNeededPayload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "CommitAsNeededPayload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.ClientMutationID, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(*string)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CommitAsNeededPayload_bug(ctx context.Context, field graphql.CollectedField, obj *models.CommitAsNeededPayload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "CommitAsNeededPayload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.Bug, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(models.BugWrapper)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CommitPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.CommitPayload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "CommitPayload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.ClientMutationID, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(*string)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CommitPayload_bug(ctx context.Context, field graphql.CollectedField, obj *models.CommitPayload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "CommitPayload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.Bug, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(models.BugWrapper)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
|
func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -7201,88 +6940,6 @@ func (ec *executionContext) _Mutation_setTitle(ctx context.Context, field graphq
|
|||||||
return ec.marshalNSetTitlePayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐSetTitlePayload(ctx, field.Selections, res)
|
return ec.marshalNSetTitlePayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐSetTitlePayload(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_commit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "Mutation",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
rawArgs := field.ArgumentMap(ec.Variables)
|
|
||||||
args, err := ec.field_Mutation_commit_args(ctx, rawArgs)
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
fc.Args = args
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return ec.resolvers.Mutation().Commit(rctx, args["input"].(models.CommitInput))
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(*models.CommitPayload)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNCommitPayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitPayload(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_commitAsNeeded(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "Mutation",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
rawArgs := field.ArgumentMap(ec.Variables)
|
|
||||||
args, err := ec.field_Mutation_commitAsNeeded_args(ctx, rawArgs)
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
fc.Args = args
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return ec.resolvers.Mutation().CommitAsNeeded(rctx, args["input"].(models.CommitAsNeededInput))
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(*models.CommitAsNeededPayload)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNCommitAsNeededPayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitAsNeededPayload(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _NewBugPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.NewBugPayload) (ret graphql.Marshaler) {
|
func (ec *executionContext) _NewBugPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.NewBugPayload) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -7821,37 +7478,6 @@ func (ec *executionContext) _PageInfo_endCursor(ctx context.Context, field graph
|
|||||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Query_defaultRepository(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "Query",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return ec.resolvers.Query().DefaultRepository(rctx)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(*models.Repository)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalORepository2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐRepository(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _Query_repository(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Query_repository(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -7876,7 +7502,7 @@ func (ec *executionContext) _Query_repository(ctx context.Context, field graphql
|
|||||||
fc.Args = args
|
fc.Args = args
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().Repository(rctx, args["ref"].(string))
|
return ec.resolvers.Query().Repository(rctx, args["ref"].(*string))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -7959,6 +7585,37 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C
|
|||||||
return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res)
|
return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Repository_name(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Repository",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Repository().Name(rctx, obj)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -10273,66 +9930,6 @@ func (ec *executionContext) unmarshalInputCloseBugInput(ctx context.Context, obj
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputCommitAsNeededInput(ctx context.Context, obj interface{}) (models.CommitAsNeededInput, error) {
|
|
||||||
var it models.CommitAsNeededInput
|
|
||||||
var asMap = obj.(map[string]interface{})
|
|
||||||
|
|
||||||
for k, v := range asMap {
|
|
||||||
switch k {
|
|
||||||
case "clientMutationId":
|
|
||||||
var err error
|
|
||||||
it.ClientMutationID, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "repoRef":
|
|
||||||
var err error
|
|
||||||
it.RepoRef, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "prefix":
|
|
||||||
var err error
|
|
||||||
it.Prefix, err = ec.unmarshalNString2string(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return it, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputCommitInput(ctx context.Context, obj interface{}) (models.CommitInput, error) {
|
|
||||||
var it models.CommitInput
|
|
||||||
var asMap = obj.(map[string]interface{})
|
|
||||||
|
|
||||||
for k, v := range asMap {
|
|
||||||
switch k {
|
|
||||||
case "clientMutationId":
|
|
||||||
var err error
|
|
||||||
it.ClientMutationID, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "repoRef":
|
|
||||||
var err error
|
|
||||||
it.RepoRef, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "prefix":
|
|
||||||
var err error
|
|
||||||
it.Prefix, err = ec.unmarshalNString2string(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return it, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputNewBugInput(ctx context.Context, obj interface{}) (models.NewBugInput, error) {
|
func (ec *executionContext) unmarshalInputNewBugInput(ctx context.Context, obj interface{}) (models.NewBugInput, error) {
|
||||||
var it models.NewBugInput
|
var it models.NewBugInput
|
||||||
var asMap = obj.(map[string]interface{})
|
var asMap = obj.(map[string]interface{})
|
||||||
@ -11346,64 +10943,6 @@ func (ec *executionContext) _CommentHistoryStep(ctx context.Context, sel ast.Sel
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
var commitAsNeededPayloadImplementors = []string{"CommitAsNeededPayload"}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CommitAsNeededPayload(ctx context.Context, sel ast.SelectionSet, obj *models.CommitAsNeededPayload) graphql.Marshaler {
|
|
||||||
fields := graphql.CollectFields(ec.OperationContext, sel, commitAsNeededPayloadImplementors)
|
|
||||||
|
|
||||||
out := graphql.NewFieldSet(fields)
|
|
||||||
var invalids uint32
|
|
||||||
for i, field := range fields {
|
|
||||||
switch field.Name {
|
|
||||||
case "__typename":
|
|
||||||
out.Values[i] = graphql.MarshalString("CommitAsNeededPayload")
|
|
||||||
case "clientMutationId":
|
|
||||||
out.Values[i] = ec._CommitAsNeededPayload_clientMutationId(ctx, field, obj)
|
|
||||||
case "bug":
|
|
||||||
out.Values[i] = ec._CommitAsNeededPayload_bug(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.Dispatch()
|
|
||||||
if invalids > 0 {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
var commitPayloadImplementors = []string{"CommitPayload"}
|
|
||||||
|
|
||||||
func (ec *executionContext) _CommitPayload(ctx context.Context, sel ast.SelectionSet, obj *models.CommitPayload) graphql.Marshaler {
|
|
||||||
fields := graphql.CollectFields(ec.OperationContext, sel, commitPayloadImplementors)
|
|
||||||
|
|
||||||
out := graphql.NewFieldSet(fields)
|
|
||||||
var invalids uint32
|
|
||||||
for i, field := range fields {
|
|
||||||
switch field.Name {
|
|
||||||
case "__typename":
|
|
||||||
out.Values[i] = graphql.MarshalString("CommitPayload")
|
|
||||||
case "clientMutationId":
|
|
||||||
out.Values[i] = ec._CommitPayload_clientMutationId(ctx, field, obj)
|
|
||||||
case "bug":
|
|
||||||
out.Values[i] = ec._CommitPayload_bug(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.Dispatch()
|
|
||||||
if invalids > 0 {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
var createOperationImplementors = []string{"CreateOperation", "Operation", "Authored"}
|
var createOperationImplementors = []string{"CreateOperation", "Operation", "Authored"}
|
||||||
|
|
||||||
func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.SelectionSet, obj *bug.CreateOperation) graphql.Marshaler {
|
func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.SelectionSet, obj *bug.CreateOperation) graphql.Marshaler {
|
||||||
@ -12172,16 +11711,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "commit":
|
|
||||||
out.Values[i] = ec._Mutation_commit(ctx, field)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
case "commitAsNeeded":
|
|
||||||
out.Values[i] = ec._Mutation_commitAsNeeded(ctx, field)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@ -12392,17 +11921,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
|||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "__typename":
|
case "__typename":
|
||||||
out.Values[i] = graphql.MarshalString("Query")
|
out.Values[i] = graphql.MarshalString("Query")
|
||||||
case "defaultRepository":
|
|
||||||
field := field
|
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
res = ec._Query_defaultRepository(ctx, field)
|
|
||||||
return res
|
|
||||||
})
|
|
||||||
case "repository":
|
case "repository":
|
||||||
field := field
|
field := field
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
@ -12440,6 +11958,17 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
|
|||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "__typename":
|
case "__typename":
|
||||||
out.Values[i] = graphql.MarshalString("Repository")
|
out.Values[i] = graphql.MarshalString("Repository")
|
||||||
|
case "name":
|
||||||
|
field := field
|
||||||
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
res = ec._Repository_name(ctx, field, obj)
|
||||||
|
return res
|
||||||
|
})
|
||||||
case "allBugs":
|
case "allBugs":
|
||||||
field := field
|
field := field
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
@ -13544,42 +13073,6 @@ func (ec *executionContext) marshalNCommentHistoryStep2ᚕgithubᚗcomᚋMichael
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNCommitAsNeededInput2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitAsNeededInput(ctx context.Context, v interface{}) (models.CommitAsNeededInput, error) {
|
|
||||||
return ec.unmarshalInputCommitAsNeededInput(ctx, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalNCommitAsNeededPayload2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitAsNeededPayload(ctx context.Context, sel ast.SelectionSet, v models.CommitAsNeededPayload) graphql.Marshaler {
|
|
||||||
return ec._CommitAsNeededPayload(ctx, sel, &v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalNCommitAsNeededPayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitAsNeededPayload(ctx context.Context, sel ast.SelectionSet, v *models.CommitAsNeededPayload) graphql.Marshaler {
|
|
||||||
if v == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
return ec._CommitAsNeededPayload(ctx, sel, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNCommitInput2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitInput(ctx context.Context, v interface{}) (models.CommitInput, error) {
|
|
||||||
return ec.unmarshalInputCommitInput(ctx, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalNCommitPayload2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitPayload(ctx context.Context, sel ast.SelectionSet, v models.CommitPayload) graphql.Marshaler {
|
|
||||||
return ec._CommitPayload(ctx, sel, &v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalNCommitPayload2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommitPayload(ctx context.Context, sel ast.SelectionSet, v *models.CommitPayload) graphql.Marshaler {
|
|
||||||
if v == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
return ec._CommitPayload(ctx, sel, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalNCreateOperation2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐCreateOperation(ctx context.Context, sel ast.SelectionSet, v bug.CreateOperation) graphql.Marshaler {
|
func (ec *executionContext) marshalNCreateOperation2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐCreateOperation(ctx context.Context, sel ast.SelectionSet, v bug.CreateOperation) graphql.Marshaler {
|
||||||
return ec._CreateOperation(ctx, sel, &v)
|
return ec._CreateOperation(ctx, sel, &v)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func TestQueries(t *testing.T) {
|
|||||||
|
|
||||||
query := `
|
query := `
|
||||||
query {
|
query {
|
||||||
defaultRepository {
|
repository {
|
||||||
allBugs(first: 2) {
|
allBugs(first: 2) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
endCursor
|
endCursor
|
||||||
@ -162,7 +162,7 @@ func TestQueries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
DefaultRepository struct {
|
Repository struct {
|
||||||
AllBugs struct {
|
AllBugs struct {
|
||||||
PageInfo models.PageInfo
|
PageInfo models.PageInfo
|
||||||
Nodes []struct {
|
Nodes []struct {
|
||||||
|
@ -111,38 +111,6 @@ type CommentEdge struct {
|
|||||||
Node *bug.Comment `json:"node"`
|
Node *bug.Comment `json:"node"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommitAsNeededInput struct {
|
|
||||||
// A unique identifier for the client performing the mutation.
|
|
||||||
ClientMutationID *string `json:"clientMutationId"`
|
|
||||||
// "The name of the repository. If not set, the default repository is used.
|
|
||||||
RepoRef *string `json:"repoRef"`
|
|
||||||
// The bug ID's prefix.
|
|
||||||
Prefix string `json:"prefix"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitAsNeededPayload struct {
|
|
||||||
// A unique identifier for the client performing the mutation.
|
|
||||||
ClientMutationID *string `json:"clientMutationId"`
|
|
||||||
// The affected bug.
|
|
||||||
Bug BugWrapper `json:"bug"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitInput struct {
|
|
||||||
// A unique identifier for the client performing the mutation.
|
|
||||||
ClientMutationID *string `json:"clientMutationId"`
|
|
||||||
// "The name of the repository. If not set, the default repository is used.
|
|
||||||
RepoRef *string `json:"repoRef"`
|
|
||||||
// The bug ID's prefix.
|
|
||||||
Prefix string `json:"prefix"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitPayload struct {
|
|
||||||
// A unique identifier for the client performing the mutation.
|
|
||||||
ClientMutationID *string `json:"clientMutationId"`
|
|
||||||
// The affected bug.
|
|
||||||
Bug BugWrapper `json:"bug"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type IdentityConnection struct {
|
type IdentityConnection struct {
|
||||||
Edges []*IdentityEdge `json:"edges"`
|
Edges []*IdentityEdge `json:"edges"`
|
||||||
Nodes []IdentityWrapper `json:"nodes"`
|
Nodes []IdentityWrapper `json:"nodes"`
|
||||||
|
@ -23,6 +23,15 @@ func (r mutationResolver) getRepo(ref *string) (*cache.RepoCache, error) {
|
|||||||
return r.cache.DefaultRepo()
|
return r.cache.DefaultRepo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r mutationResolver) getBug(repoRef *string, bugPrefix string) (*cache.BugCache, error) {
|
||||||
|
repo, err := r.getRepo(repoRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.ResolveBugPrefix(bugPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*models.NewBugPayload, error) {
|
func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*models.NewBugPayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
repo, err := r.getRepo(input.RepoRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -42,12 +51,7 @@ func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) {
|
func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
b, err := r.getBug(input.RepoRef, input.Prefix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -57,6 +61,11 @@ func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentI
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = b.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &models.AddCommentPayload{
|
return &models.AddCommentPayload{
|
||||||
ClientMutationID: input.ClientMutationID,
|
ClientMutationID: input.ClientMutationID,
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
Bug: models.NewLoadedBug(b.Snapshot()),
|
||||||
@ -65,12 +74,7 @@ func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentI
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) {
|
func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
b, err := r.getBug(input.RepoRef, input.Prefix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -80,6 +84,11 @@ func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLa
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = b.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resultsPtr := make([]*bug.LabelChangeResult, len(results))
|
resultsPtr := make([]*bug.LabelChangeResult, len(results))
|
||||||
for i, result := range results {
|
for i, result := range results {
|
||||||
resultsPtr[i] = &result
|
resultsPtr[i] = &result
|
||||||
@ -94,12 +103,7 @@ func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) {
|
func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
b, err := r.getBug(input.RepoRef, input.Prefix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -109,6 +113,11 @@ func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = b.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &models.OpenBugPayload{
|
return &models.OpenBugPayload{
|
||||||
ClientMutationID: input.ClientMutationID,
|
ClientMutationID: input.ClientMutationID,
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
Bug: models.NewLoadedBug(b.Snapshot()),
|
||||||
@ -117,12 +126,7 @@ func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) {
|
func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
b, err := r.getBug(input.RepoRef, input.Prefix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -132,6 +136,11 @@ func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = b.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &models.CloseBugPayload{
|
return &models.CloseBugPayload{
|
||||||
ClientMutationID: input.ClientMutationID,
|
ClientMutationID: input.ClientMutationID,
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
Bug: models.NewLoadedBug(b.Snapshot()),
|
||||||
@ -140,12 +149,7 @@ func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) {
|
func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) {
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
b, err := r.getBug(input.RepoRef, input.Prefix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -155,53 +159,14 @@ func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = b.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &models.SetTitlePayload{
|
return &models.SetTitlePayload{
|
||||||
ClientMutationID: input.ClientMutationID,
|
ClientMutationID: input.ClientMutationID,
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
Bug: models.NewLoadedBug(b.Snapshot()),
|
||||||
Operation: op,
|
Operation: op,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) Commit(_ context.Context, input models.CommitInput) (*models.CommitPayload, error) {
|
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = b.Commit()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.CommitPayload{
|
|
||||||
ClientMutationID: input.ClientMutationID,
|
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r mutationResolver) CommitAsNeeded(_ context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error) {
|
|
||||||
repo, err := r.getRepo(input.RepoRef)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := repo.ResolveBugPrefix(input.Prefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = b.CommitAsNeeded()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.CommitAsNeededPayload{
|
|
||||||
ClientMutationID: input.ClientMutationID,
|
|
||||||
Bug: models.NewLoadedBug(b.Snapshot()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
@ -27,8 +27,15 @@ func (r rootQueryResolver) DefaultRepository(_ context.Context) (*models.Reposit
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r rootQueryResolver) Repository(_ context.Context, ref string) (*models.Repository, error) {
|
func (r rootQueryResolver) Repository(_ context.Context, ref *string) (*models.Repository, error) {
|
||||||
repo, err := r.cache.ResolveRepo(ref)
|
var repo *cache.RepoCache
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if ref == nil {
|
||||||
|
repo, err = r.cache.DefaultRepo()
|
||||||
|
} else {
|
||||||
|
repo, err = r.cache.ResolveRepo(*ref)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -15,6 +15,11 @@ var _ graph.RepositoryResolver = &repoResolver{}
|
|||||||
|
|
||||||
type repoResolver struct{}
|
type repoResolver struct{}
|
||||||
|
|
||||||
|
func (repoResolver) Name(_ context.Context, obj *models.Repository) (*string, error) {
|
||||||
|
name := obj.Repo.Name()
|
||||||
|
return &name, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) {
|
func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) {
|
||||||
input := models.ConnectionInput{
|
input := models.ConnectionInput{
|
||||||
Before: before,
|
Before: before,
|
||||||
@ -153,7 +158,7 @@ func (repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (mod
|
|||||||
return models.NewLazyIdentity(obj.Repo, excerpt), nil
|
return models.NewLazyIdentity(obj.Repo, excerpt), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resolver repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) {
|
func (repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) {
|
||||||
input := models.ConnectionInput{
|
input := models.ConnectionInput{
|
||||||
Before: before,
|
Before: before,
|
||||||
After: after,
|
After: after,
|
||||||
|
@ -136,35 +136,3 @@ type SetTitlePayload {
|
|||||||
"""The resulting operation"""
|
"""The resulting operation"""
|
||||||
operation: SetTitleOperation!
|
operation: SetTitleOperation!
|
||||||
}
|
}
|
||||||
|
|
||||||
input CommitInput {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
""""The name of the repository. If not set, the default repository is used."""
|
|
||||||
repoRef: String
|
|
||||||
"""The bug ID's prefix."""
|
|
||||||
prefix: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitPayload {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
"""The affected bug."""
|
|
||||||
bug: Bug!
|
|
||||||
}
|
|
||||||
|
|
||||||
input CommitAsNeededInput {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
""""The name of the repository. If not set, the default repository is used."""
|
|
||||||
repoRef: String
|
|
||||||
"""The bug ID's prefix."""
|
|
||||||
prefix: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitAsNeededPayload {
|
|
||||||
"""A unique identifier for the client performing the mutation."""
|
|
||||||
clientMutationId: String
|
|
||||||
"""The affected bug."""
|
|
||||||
bug: Bug!
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
type Repository {
|
type Repository {
|
||||||
|
"""The name of the repository"""
|
||||||
|
name: String
|
||||||
|
|
||||||
"""All the bugs"""
|
"""All the bugs"""
|
||||||
allBugs(
|
allBugs(
|
||||||
"""Returns the elements in the list that come after the specified cursor."""
|
"""Returns the elements in the list that come after the specified cursor."""
|
||||||
@ -10,7 +13,7 @@ type Repository {
|
|||||||
first: Int
|
first: Int
|
||||||
"""Returns the last _n_ elements from the list."""
|
"""Returns the last _n_ elements from the list."""
|
||||||
last: Int
|
last: Int
|
||||||
"""A query to select and order bugs"""
|
"""A query to select and order bugs."""
|
||||||
query: String
|
query: String
|
||||||
): BugConnection!
|
): BugConnection!
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
type Query {
|
type Query {
|
||||||
"""The default unnamend repository."""
|
"""Access a repository by reference/name. If no ref is given, the default repository is returned if any."""
|
||||||
defaultRepository: Repository
|
repository(ref: String): Repository
|
||||||
"""Access a repository by reference/name."""
|
|
||||||
repository(ref: String!): Repository
|
|
||||||
|
|
||||||
#TODO: connection for all repositories
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
@ -20,8 +16,4 @@ type Mutation {
|
|||||||
closeBug(input: CloseBugInput!): CloseBugPayload!
|
closeBug(input: CloseBugInput!): CloseBugPayload!
|
||||||
"""Change a bug's title"""
|
"""Change a bug's title"""
|
||||||
setTitle(input: SetTitleInput!): SetTitlePayload!
|
setTitle(input: SetTitleInput!): SetTitlePayload!
|
||||||
"""Commit write the pending operations into storage. This mutation fail if nothing is pending"""
|
|
||||||
commit(input: CommitInput!): CommitPayload!
|
|
||||||
"""Commit write the pending operations into storage. This mutation succed if nothing is pending"""
|
|
||||||
commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload!
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["react-app", "plugin:prettier/recommended"],
|
|
||||||
"ignorePatterns": ["src/fragmentTypes.js"]
|
|
||||||
}
|
|
37
webui/.eslintrc.js
Normal file
37
webui/.eslintrc.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'react-app',
|
||||||
|
'prettier/@typescript-eslint',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
plugins: ['graphql'],
|
||||||
|
rules: {
|
||||||
|
'graphql/template-strings': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
schemaJson: require('./src/schema.json'),
|
||||||
|
env: 'literal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'import/order': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
alphabetize: { order: 'asc' },
|
||||||
|
pathGroups: [
|
||||||
|
{
|
||||||
|
pattern: '@material-ui/**',
|
||||||
|
group: 'external',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: '*.generated',
|
||||||
|
group: 'sibling',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
groups: [['builtin', 'external'], 'parent', ['sibling', 'index']],
|
||||||
|
'newlines-between': 'always',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
@ -5,7 +5,9 @@ install:
|
|||||||
npm install
|
npm install
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
npm run generate
|
||||||
npm run lint
|
npm run lint
|
||||||
|
CI=true npm run test
|
||||||
|
|
||||||
build:
|
build:
|
||||||
npm run build
|
npm run build
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
2. Run the GraphQL backend on the port 3001
|
2. Run the GraphQL backend on the port 3001
|
||||||
- `./git-bug webui -p 3001`
|
- `./git-bug webui -p 3001`
|
||||||
3. Run the hot-reloadable development WebUI
|
3. Run the hot-reloadable development WebUI
|
||||||
|
|
||||||
- run `npm start` in the **webui** directory
|
- run `npm start` in the **webui** directory
|
||||||
|
|
||||||
The development version of the WebUI is configured to query the backend on the port 3001. You can now live edit the js code and use the normal backend.
|
The development version of the WebUI is configured to query the backend on the port 3001. You can now live edit the js code and use the normal backend.
|
||||||
|
|
||||||
## Bundle the web UI
|
## Bundle the web UI
|
||||||
|
|
||||||
Once the webUI is good enough for a new release, run `make pack-webui` from the root directory to bundle the compiled js into the go binary.
|
Once the webUI is good enough for a new release, run `make pack-webui` from the root directory to bundle the compiled js into the go binary.
|
||||||
|
@ -1,8 +1,32 @@
|
|||||||
schema: '../graphql/schema/*.graphql'
|
schema: '../graphql/schema/*.graphql'
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
documents: src/**/*.graphql
|
||||||
generates:
|
generates:
|
||||||
./src/fragmentTypes.js:
|
./src/fragmentTypes.ts:
|
||||||
plugins:
|
plugins:
|
||||||
- fragment-matcher
|
- fragment-matcher
|
||||||
config:
|
config:
|
||||||
module: es2015
|
module: es2015
|
||||||
|
./src/gqlTypes.ts:
|
||||||
|
plugins:
|
||||||
|
- typescript
|
||||||
|
./src/schema.json:
|
||||||
|
plugins:
|
||||||
|
- introspection
|
||||||
|
./src/:
|
||||||
|
plugins:
|
||||||
|
- add: '/* eslint-disable @typescript-eslint/no-unused-vars, import/order */'
|
||||||
|
- typescript-operations
|
||||||
|
- typescript-react-apollo
|
||||||
|
preset: near-operation-file
|
||||||
|
presetConfig:
|
||||||
|
extension: .generated.tsx
|
||||||
|
baseTypesPath: gqlTypes.ts
|
||||||
|
config:
|
||||||
|
withComponent: false
|
||||||
|
withHOC: false
|
||||||
|
withHooks: true
|
||||||
|
|
||||||
|
hooks:
|
||||||
|
afterAllFileWrite:
|
||||||
|
- prettier --write
|
||||||
|
8005
webui/package-lock.json
generated
8005
webui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,28 +4,41 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.3",
|
||||||
|
"@arrows/composition": "^1.2.2",
|
||||||
"@material-ui/core": "^4.9.0",
|
"@material-ui/core": "^4.9.0",
|
||||||
"@material-ui/icons": "^4.2.1",
|
"@material-ui/icons": "^4.2.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.40",
|
"@material-ui/lab": "^4.0.0-alpha.40",
|
||||||
"@material-ui/styles": "^4.9.0",
|
"@material-ui/styles": "^4.9.0",
|
||||||
|
"@types/node": "^13.5.3",
|
||||||
|
"@types/react": "^16.9.19",
|
||||||
|
"@types/react-dom": "^16.9.5",
|
||||||
|
"@types/react-router-dom": "^5.1.3",
|
||||||
"apollo-boost": "^0.4.7",
|
"apollo-boost": "^0.4.7",
|
||||||
"graphql": "^14.3.0",
|
"clsx": "^1.1.0",
|
||||||
|
"graphql": "^14.6.0",
|
||||||
|
"graphql.macro": "^1.4.2",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-apollo": "^3.1.3",
|
"react-apollo": "^3.1.3",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-router": "^5.0.0",
|
"react-router": "^5.0.0",
|
||||||
"react-router-dom": "^5.0.0",
|
"react-router-dom": "^5.0.0",
|
||||||
"react-scripts": "^3.1.1",
|
"react-scripts": "^3.3.1",
|
||||||
"remark-html": "^10.0.0",
|
"remark-html": "^10.0.0",
|
||||||
"remark-parse": "^7.0.2",
|
"remark-parse": "^7.0.2",
|
||||||
"remark-react": "^6.0.0",
|
"remark-react": "^6.0.0",
|
||||||
|
"typescript": "^3.7.5",
|
||||||
"unified": "^8.4.2"
|
"unified": "^8.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "^1.11.2",
|
"@graphql-codegen/cli": "^1.12.1",
|
||||||
"@graphql-codegen/fragment-matcher": "^1.11.2",
|
"@graphql-codegen/fragment-matcher": "^1.12.1",
|
||||||
"eslint-config-prettier": "^6.9.0",
|
"@graphql-codegen/near-operation-file-preset": "^1.12.2-alpha-ea7264f9.15",
|
||||||
|
"@graphql-codegen/typescript-operations": "^1.12.1",
|
||||||
|
"@graphql-codegen/typescript-react-apollo": "^1.12.1",
|
||||||
|
"@graphql-codegen/introspection": "^1.12.2",
|
||||||
|
"eslint-config-prettier": "^6.10.0",
|
||||||
|
"eslint-plugin-graphql": "^3.1.1",
|
||||||
"eslint-plugin-prettier": "^3.1.2",
|
"eslint-plugin-prettier": "^3.1.2",
|
||||||
"prettier": "^1.19.1"
|
"prettier": "^1.19.1"
|
||||||
},
|
},
|
||||||
@ -35,7 +48,7 @@
|
|||||||
"test": "react-scripts test --env=jsdom",
|
"test": "react-scripts test --env=jsdom",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"generate": "graphql-codegen",
|
"generate": "graphql-codegen",
|
||||||
"lint": "eslint src/"
|
"lint": "eslint src --ext .ts --ext .tsx --ext .js --ext .jsx --ext .graphql"
|
||||||
},
|
},
|
||||||
"proxy": "http://localhost:3001",
|
"proxy": "http://localhost:3001",
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
5
webui/src/.gitignore
vendored
5
webui/src/.gitignore
vendored
@ -1 +1,4 @@
|
|||||||
fragmentTypes.js
|
fragmentTypes.ts
|
||||||
|
gqlTypes.ts
|
||||||
|
*.generated.*
|
||||||
|
schema.json
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||||
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
|
|
||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
|
import {
|
||||||
|
createMuiTheme,
|
||||||
|
ThemeProvider,
|
||||||
|
makeStyles,
|
||||||
|
} from '@material-ui/core/styles';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router';
|
import { Route, Switch } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import CurrentIdentity from './CurrentIdentity';
|
||||||
import BugQuery from './bug/BugQuery';
|
import BugQuery from './bug/BugQuery';
|
||||||
import ListQuery from './list/ListQuery';
|
import ListQuery from './list/ListQuery';
|
||||||
import CurrentIdentity from './CurrentIdentity';
|
|
||||||
|
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
@ -20,7 +23,9 @@ const theme = createMuiTheme({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
offset: theme.mixins.toolbar,
|
offset: {
|
||||||
|
...theme.mixins.toolbar,
|
||||||
|
},
|
||||||
filler: {
|
filler: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
@ -46,7 +51,7 @@ export default function App() {
|
|||||||
<AppBar position="fixed" color="primary">
|
<AppBar position="fixed" color="primary">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Link to="/" className={classes.appTitle}>
|
<Link to="/" className={classes.appTitle}>
|
||||||
<img src="logo.svg" className={classes.logo} alt="git-bug" />
|
<img src="/logo.svg" className={classes.logo} alt="git-bug" />
|
||||||
git-bug
|
git-bug
|
||||||
</Link>
|
</Link>
|
||||||
<div className={classes.filler}></div>
|
<div className={classes.filler}></div>
|
8
webui/src/Author.graphql
Normal file
8
webui/src/Author.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fragment authored on Authored {
|
||||||
|
author {
|
||||||
|
name
|
||||||
|
email
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
import gql from 'graphql-tag';
|
|
||||||
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
|
||||||
import MAvatar from '@material-ui/core/Avatar';
|
import MAvatar from '@material-ui/core/Avatar';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Author = ({ author, ...props }) => {
|
import { AuthoredFragment } from './Author.generated';
|
||||||
|
|
||||||
|
type Props = AuthoredFragment & {
|
||||||
|
className?: string;
|
||||||
|
bold?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Author = ({ author, ...props }: Props) => {
|
||||||
if (!author.email) {
|
if (!author.email) {
|
||||||
return <span {...props}>{author.displayName}</span>;
|
return <span {...props}>{author.displayName}</span>;
|
||||||
}
|
}
|
||||||
@ -15,18 +21,7 @@ const Author = ({ author, ...props }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Author.fragment = gql`
|
export const Avatar = ({ author, ...props }: Props) => {
|
||||||
fragment authored on Authored {
|
|
||||||
author {
|
|
||||||
name
|
|
||||||
email
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Avatar = ({ author, ...props }) => {
|
|
||||||
if (author.avatarUrl) {
|
if (author.avatarUrl) {
|
||||||
return <MAvatar src={author.avatarUrl} {...props} />;
|
return <MAvatar src={author.avatarUrl} {...props} />;
|
||||||
}
|
}
|
@ -1,11 +1,14 @@
|
|||||||
import unified from 'unified';
|
import React from 'react';
|
||||||
import parse from 'remark-parse';
|
|
||||||
import html from 'remark-html';
|
import html from 'remark-html';
|
||||||
|
import parse from 'remark-parse';
|
||||||
import remark2react from 'remark-react';
|
import remark2react from 'remark-react';
|
||||||
|
import unified from 'unified';
|
||||||
|
|
||||||
import ImageTag from './tag/ImageTag';
|
import ImageTag from './tag/ImageTag';
|
||||||
import PreTag from './tag/PreTag';
|
import PreTag from './tag/PreTag';
|
||||||
|
|
||||||
const Content = ({ markdown }) => {
|
type Props = { markdown: string };
|
||||||
|
const Content: React.FC<Props> = ({ markdown }: Props) => {
|
||||||
const processor = unified()
|
const processor = unified()
|
||||||
.use(parse)
|
.use(parse)
|
||||||
.use(html)
|
.use(html)
|
||||||
@ -16,7 +19,8 @@ const Content = ({ markdown }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return processor.processSync(markdown).contents;
|
const contents: React.ReactNode = processor.processSync(markdown).contents;
|
||||||
|
return <>{contents}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Content;
|
export default Content;
|
8
webui/src/CurrentIdentity.graphql
Normal file
8
webui/src/CurrentIdentity.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
query CurrentIdentity {
|
||||||
|
repository {
|
||||||
|
userIdentity {
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import { Query } from 'react-apollo';
|
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
|
||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
displayName: {
|
|
||||||
marginLeft: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const QUERY = gql`
|
|
||||||
{
|
|
||||||
defaultRepository {
|
|
||||||
userIdentity {
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CurrentIdentity = () => {
|
|
||||||
const classes = useStyles();
|
|
||||||
return (
|
|
||||||
<Query query={QUERY}>
|
|
||||||
{({ loading, error, data }) => {
|
|
||||||
if (error || loading || !data.defaultRepository.userIdentity)
|
|
||||||
return null;
|
|
||||||
const user = data.defaultRepository.userIdentity;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Avatar src={user.avatarUrl}>
|
|
||||||
{user.displayName.charAt(0).toUpperCase()}
|
|
||||||
</Avatar>
|
|
||||||
<div className={classes.displayName}>{user.displayName}</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CurrentIdentity;
|
|
30
webui/src/CurrentIdentity.tsx
Normal file
30
webui/src/CurrentIdentity.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useCurrentIdentityQuery } from './CurrentIdentity.generated';
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
displayName: {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const CurrentIdentity = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { loading, error, data } = useCurrentIdentityQuery();
|
||||||
|
|
||||||
|
if (error || loading || !data?.repository?.userIdentity) return null;
|
||||||
|
|
||||||
|
const user = data.repository.userIdentity;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Avatar src={user.avatarUrl ? user.avatarUrl : undefined}>
|
||||||
|
{user.displayName.charAt(0).toUpperCase()}
|
||||||
|
</Avatar>
|
||||||
|
<div className={classes.displayName}>{user.displayName}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CurrentIdentity;
|
@ -1,8 +1,9 @@
|
|||||||
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
||||||
import * as moment from 'moment';
|
import moment from 'moment';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Date = ({ date }) => (
|
type Props = { date: string };
|
||||||
|
const Date = ({ date }: Props) => (
|
||||||
<Tooltip title={moment(date).format('MMMM D, YYYY, h:mm a')}>
|
<Tooltip title={moment(date).format('MMMM D, YYYY, h:mm a')}>
|
||||||
<span> {moment(date).fromNow()} </span>
|
<span> {moment(date).fromNow()} </span>
|
||||||
</Tooltip>
|
</Tooltip>
|
8
webui/src/Label.graphql
Normal file
8
webui/src/Label.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fragment Label on Label {
|
||||||
|
name
|
||||||
|
color {
|
||||||
|
R
|
||||||
|
G
|
||||||
|
B
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,28 @@
|
|||||||
import React from 'react';
|
import { common } from '@material-ui/core/colors';
|
||||||
import gql from 'graphql-tag';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import {
|
import {
|
||||||
getContrastRatio,
|
getContrastRatio,
|
||||||
darken,
|
darken,
|
||||||
} from '@material-ui/core/styles/colorManipulator';
|
} from '@material-ui/core/styles/colorManipulator';
|
||||||
import { common } from '@material-ui/core/colors';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { LabelFragment } from './Label.generated';
|
||||||
|
import { Color } from './gqlTypes';
|
||||||
|
|
||||||
// Minimum contrast between the background and the text color
|
// Minimum contrast between the background and the text color
|
||||||
const contrastThreshold = 2.5;
|
const contrastThreshold = 2.5;
|
||||||
|
|
||||||
// Guess the text color based on the background color
|
// Guess the text color based on the background color
|
||||||
const getTextColor = background =>
|
const getTextColor = (background: string) =>
|
||||||
getContrastRatio(background, common.white) >= contrastThreshold
|
getContrastRatio(background, common.white) >= contrastThreshold
|
||||||
? common.white // White on dark backgrounds
|
? common.white // White on dark backgrounds
|
||||||
: common.black; // And black on light ones
|
: common.black; // And black on light ones
|
||||||
|
|
||||||
const _rgb = color => 'rgb(' + color.R + ',' + color.G + ',' + color.B + ')';
|
const _rgb = (color: Color) =>
|
||||||
|
'rgb(' + color.R + ',' + color.G + ',' + color.B + ')';
|
||||||
|
|
||||||
// Create a style object from the label RGB colors
|
// Create a style object from the label RGB colors
|
||||||
const createStyle = color => ({
|
const createStyle = (color: Color) => ({
|
||||||
backgroundColor: _rgb(color),
|
backgroundColor: _rgb(color),
|
||||||
color: getTextColor(_rgb(color)),
|
color: getTextColor(_rgb(color)),
|
||||||
borderBottomColor: darken(_rgb(color), 0.2),
|
borderBottomColor: darken(_rgb(color), 0.2),
|
||||||
@ -30,7 +33,7 @@ const useStyles = makeStyles(theme => ({
|
|||||||
...theme.typography.body1,
|
...theme.typography.body1,
|
||||||
padding: '1px 6px 0.5px',
|
padding: '1px 6px 0.5px',
|
||||||
fontSize: '0.9em',
|
fontSize: '0.9em',
|
||||||
fontWeight: '500',
|
fontWeight: 500,
|
||||||
margin: '0.05em 1px calc(-1.5px + 0.05em)',
|
margin: '0.05em 1px calc(-1.5px + 0.05em)',
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
@ -39,7 +42,8 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Label({ label }) {
|
type Props = { label: LabelFragment };
|
||||||
|
function Label({ label }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<span className={classes.label} style={createStyle(label.color)}>
|
<span className={classes.label} style={createStyle(label.color)}>
|
||||||
@ -48,15 +52,4 @@ function Label({ label }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Label.fragment = gql`
|
|
||||||
fragment Label on Label {
|
|
||||||
name
|
|
||||||
color {
|
|
||||||
R
|
|
||||||
G
|
|
||||||
B
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Label;
|
export default Label;
|
14
webui/src/bug/Bug.graphql
Normal file
14
webui/src/bug/Bug.graphql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#import "../Label.graphql"
|
||||||
|
#import "../Author.graphql"
|
||||||
|
|
||||||
|
fragment Bug on Bug {
|
||||||
|
id
|
||||||
|
humanId
|
||||||
|
status
|
||||||
|
title
|
||||||
|
labels {
|
||||||
|
...Label
|
||||||
|
}
|
||||||
|
createdAt
|
||||||
|
...authored
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import Typography from '@material-ui/core/Typography/Typography';
|
import Typography from '@material-ui/core/Typography/Typography';
|
||||||
import gql from 'graphql-tag';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import Date from '../Date';
|
import Date from '../Date';
|
||||||
import TimelineQuery from './TimelineQuery';
|
|
||||||
import Label from '../Label';
|
import Label from '../Label';
|
||||||
|
|
||||||
|
import { BugFragment } from './Bug.generated';
|
||||||
|
import TimelineQuery from './TimelineQuery';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
main: {
|
main: {
|
||||||
maxWidth: 800,
|
maxWidth: 800,
|
||||||
@ -51,7 +53,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Bug({ bug }) {
|
type Props = {
|
||||||
|
bug: BugFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function Bug({ bug }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<main className={classes.main}>
|
<main className={classes.main}>
|
||||||
@ -85,20 +91,4 @@ function Bug({ bug }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bug.fragment = gql`
|
|
||||||
fragment Bug on Bug {
|
|
||||||
id
|
|
||||||
humanId
|
|
||||||
status
|
|
||||||
title
|
|
||||||
labels {
|
|
||||||
...Label
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
...authored
|
|
||||||
}
|
|
||||||
${Label.fragment}
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Bug;
|
export default Bug;
|
9
webui/src/bug/BugQuery.graphql
Normal file
9
webui/src/bug/BugQuery.graphql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#import "./Bug.graphql"
|
||||||
|
|
||||||
|
query GetBug($id: String!) {
|
||||||
|
repository {
|
||||||
|
bug(prefix: $id) {
|
||||||
|
...Bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
|
||||||
import { Query } from 'react-apollo';
|
|
||||||
|
|
||||||
import Bug from './Bug';
|
|
||||||
|
|
||||||
const QUERY = gql`
|
|
||||||
query GetBug($id: String!) {
|
|
||||||
defaultRepository {
|
|
||||||
bug(prefix: $id) {
|
|
||||||
...Bug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Bug.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const BugQuery = ({ match }) => (
|
|
||||||
<Query query={QUERY} variables={{ id: match.params.id }}>
|
|
||||||
{({ loading, error, data }) => {
|
|
||||||
if (loading) return <CircularProgress />;
|
|
||||||
if (error) return <p>Error: {error}</p>;
|
|
||||||
return <Bug bug={data.defaultRepository.bug} />;
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default BugQuery;
|
|
22
webui/src/bug/BugQuery.tsx
Normal file
22
webui/src/bug/BugQuery.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
|
import React from 'react';
|
||||||
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Bug from './Bug';
|
||||||
|
import { useGetBugQuery } from './BugQuery.generated';
|
||||||
|
|
||||||
|
type Props = RouteComponentProps<{
|
||||||
|
id: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const BugQuery: React.FC<Props> = ({ match }: Props) => {
|
||||||
|
const { loading, error, data } = useGetBugQuery({
|
||||||
|
variables: { id: match.params.id },
|
||||||
|
});
|
||||||
|
if (loading) return <CircularProgress />;
|
||||||
|
if (error) return <p>Error: {error}</p>;
|
||||||
|
if (!data?.repository?.bug) return <p>404.</p>;
|
||||||
|
return <Bug bug={data.repository.bug} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BugQuery;
|
@ -1,10 +1,12 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import Date from '../Date';
|
import Date from '../Date';
|
||||||
import Label from '../Label';
|
import Label from '../Label';
|
||||||
|
|
||||||
|
import { LabelChangeFragment } from './LabelChangeFragment.generated';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
main: {
|
main: {
|
||||||
...theme.typography.body1,
|
...theme.typography.body1,
|
||||||
@ -15,7 +17,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function LabelChange({ op }) {
|
type Props = {
|
||||||
|
op: LabelChangeFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function LabelChange({ op }: Props) {
|
||||||
const { added, removed } = op;
|
const { added, removed } = op;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
@ -40,22 +46,4 @@ function LabelChange({ op }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelChange.fragment = gql`
|
|
||||||
fragment LabelChange on TimelineItem {
|
|
||||||
... on LabelChangeTimelineItem {
|
|
||||||
date
|
|
||||||
...authored
|
|
||||||
added {
|
|
||||||
...Label
|
|
||||||
}
|
|
||||||
removed {
|
|
||||||
...Label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Label.fragment}
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default LabelChange;
|
export default LabelChange;
|
13
webui/src/bug/LabelChangeFragment.graphql
Normal file
13
webui/src/bug/LabelChangeFragment.graphql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
#import "../Label.graphql"
|
||||||
|
|
||||||
|
fragment LabelChange on LabelChangeTimelineItem {
|
||||||
|
date
|
||||||
|
...authored
|
||||||
|
added {
|
||||||
|
...Label
|
||||||
|
}
|
||||||
|
removed {
|
||||||
|
...Label
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import gql from 'graphql-tag';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import { Avatar } from '../Author';
|
import { Avatar } from '../Author';
|
||||||
import Date from '../Date';
|
|
||||||
import Content from '../Content';
|
import Content from '../Content';
|
||||||
|
import Date from '../Date';
|
||||||
|
|
||||||
|
import { AddCommentFragment } from './MessageCommentFragment.generated';
|
||||||
|
import { CreateFragment } from './MessageCreateFragment.generated';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
author: {
|
author: {
|
||||||
@ -47,7 +50,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Message({ op }) {
|
type Props = {
|
||||||
|
op: AddCommentFragment | CreateFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function Message({ op }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<article className={classes.container}>
|
<article className={classes.container}>
|
||||||
@ -69,30 +76,4 @@ function Message({ op }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message.createFragment = gql`
|
|
||||||
fragment Create on TimelineItem {
|
|
||||||
... on CreateTimelineItem {
|
|
||||||
createdAt
|
|
||||||
...authored
|
|
||||||
edited
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
Message.commentFragment = gql`
|
|
||||||
fragment AddComment on TimelineItem {
|
|
||||||
... on AddCommentTimelineItem {
|
|
||||||
createdAt
|
|
||||||
...authored
|
|
||||||
edited
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Message;
|
export default Message;
|
8
webui/src/bug/MessageCommentFragment.graphql
Normal file
8
webui/src/bug/MessageCommentFragment.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
|
||||||
|
fragment AddComment on AddCommentTimelineItem {
|
||||||
|
createdAt
|
||||||
|
...authored
|
||||||
|
edited
|
||||||
|
message
|
||||||
|
}
|
8
webui/src/bug/MessageCreateFragment.graphql
Normal file
8
webui/src/bug/MessageCreateFragment.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
|
||||||
|
fragment Create on CreateTimelineItem {
|
||||||
|
createdAt
|
||||||
|
...authored
|
||||||
|
edited
|
||||||
|
message
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import Date from '../Date';
|
import Date from '../Date';
|
||||||
|
|
||||||
|
import { SetStatusFragment } from './SetStatusFragment.generated';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
main: {
|
main: {
|
||||||
...theme.typography.body1,
|
...theme.typography.body1,
|
||||||
@ -11,7 +13,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function SetStatus({ op }) {
|
type Props = {
|
||||||
|
op: SetStatusFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function SetStatus({ op }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<div className={classes.main}>
|
<div className={classes.main}>
|
||||||
@ -22,16 +28,4 @@ function SetStatus({ op }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetStatus.fragment = gql`
|
|
||||||
fragment SetStatus on TimelineItem {
|
|
||||||
... on SetStatusTimelineItem {
|
|
||||||
date
|
|
||||||
...authored
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default SetStatus;
|
export default SetStatus;
|
7
webui/src/bug/SetStatusFragment.graphql
Normal file
7
webui/src/bug/SetStatusFragment.graphql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
|
||||||
|
fragment SetStatus on SetStatusTimelineItem {
|
||||||
|
date
|
||||||
|
...authored
|
||||||
|
status
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import Date from '../Date';
|
import Date from '../Date';
|
||||||
|
|
||||||
|
import { SetTitleFragment } from './SetTitleFragment.generated';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
main: {
|
main: {
|
||||||
...theme.typography.body1,
|
...theme.typography.body1,
|
||||||
@ -14,7 +16,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function SetTitle({ op }) {
|
type Props = {
|
||||||
|
op: SetTitleFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function SetTitle({ op }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<div className={classes.main}>
|
<div className={classes.main}>
|
||||||
@ -28,17 +34,4 @@ function SetTitle({ op }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTitle.fragment = gql`
|
|
||||||
fragment SetTitle on TimelineItem {
|
|
||||||
... on SetTitleTimelineItem {
|
|
||||||
date
|
|
||||||
...authored
|
|
||||||
title
|
|
||||||
was
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default SetTitle;
|
export default SetTitle;
|
8
webui/src/bug/SetTitleFragment.graphql
Normal file
8
webui/src/bug/SetTitleFragment.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
|
||||||
|
fragment SetTitle on SetTitleTimelineItem {
|
||||||
|
date
|
||||||
|
...authored
|
||||||
|
title
|
||||||
|
was
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import React from 'react';
|
|
||||||
import LabelChange from './LabelChange';
|
|
||||||
import Message from './Message';
|
|
||||||
import SetStatus from './SetStatus';
|
|
||||||
import SetTitle from './SetTitle';
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
main: {
|
|
||||||
'& > *:not(:last-child)': {
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const componentMap = {
|
|
||||||
CreateTimelineItem: Message,
|
|
||||||
AddCommentTimelineItem: Message,
|
|
||||||
LabelChangeTimelineItem: LabelChange,
|
|
||||||
SetTitleTimelineItem: SetTitle,
|
|
||||||
SetStatusTimelineItem: SetStatus,
|
|
||||||
};
|
|
||||||
|
|
||||||
function Timeline({ ops }) {
|
|
||||||
const classes = useStyles();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.main}>
|
|
||||||
{ops.map((op, index) => {
|
|
||||||
const Component = componentMap[op.__typename];
|
|
||||||
|
|
||||||
if (!Component) {
|
|
||||||
console.warn('unsupported operation type ' + op.__typename);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Component key={index} op={op} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Timeline;
|
|
48
webui/src/bug/Timeline.tsx
Normal file
48
webui/src/bug/Timeline.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import LabelChange from './LabelChange';
|
||||||
|
import Message from './Message';
|
||||||
|
import SetStatus from './SetStatus';
|
||||||
|
import SetTitle from './SetTitle';
|
||||||
|
import { TimelineItemFragment } from './TimelineQuery.generated';
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
main: {
|
||||||
|
'& > *:not(:last-child)': {
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
ops: Array<TimelineItemFragment>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function Timeline({ ops }: Props) {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.main}>
|
||||||
|
{ops.map((op, index) => {
|
||||||
|
switch (op.__typename) {
|
||||||
|
case 'CreateTimelineItem':
|
||||||
|
return <Message key={index} op={op} />;
|
||||||
|
case 'AddCommentTimelineItem':
|
||||||
|
return <Message key={index} op={op} />;
|
||||||
|
case 'LabelChangeTimelineItem':
|
||||||
|
return <LabelChange key={index} op={op} />;
|
||||||
|
case 'SetTitleTimelineItem':
|
||||||
|
return <SetTitle key={index} op={op} />;
|
||||||
|
case 'SetStatusTimelineItem':
|
||||||
|
return <SetStatus key={index} op={op} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('unsupported operation type ' + op.__typename);
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Timeline;
|
39
webui/src/bug/TimelineQuery.graphql
Normal file
39
webui/src/bug/TimelineQuery.graphql
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#import "./MessageCreateFragment.graphql"
|
||||||
|
#import "./MessageCommentFragment.graphql"
|
||||||
|
#import "./LabelChangeFragment.graphql"
|
||||||
|
#import "./SetTitleFragment.graphql"
|
||||||
|
#import "./SetStatusFragment.graphql"
|
||||||
|
|
||||||
|
query Timeline($id: String!, $first: Int = 10, $after: String) {
|
||||||
|
repository {
|
||||||
|
bug(prefix: $id) {
|
||||||
|
timeline(first: $first, after: $after) {
|
||||||
|
nodes {
|
||||||
|
...TimelineItem
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment TimelineItem on TimelineItem {
|
||||||
|
... on LabelChangeTimelineItem {
|
||||||
|
...LabelChange
|
||||||
|
}
|
||||||
|
... on SetStatusTimelineItem {
|
||||||
|
...SetStatus
|
||||||
|
}
|
||||||
|
... on SetTitleTimelineItem {
|
||||||
|
...SetTitle
|
||||||
|
}
|
||||||
|
... on AddCommentTimelineItem {
|
||||||
|
...AddComment
|
||||||
|
}
|
||||||
|
... on CreateTimelineItem {
|
||||||
|
...Create
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
|
||||||
import { Query } from 'react-apollo';
|
|
||||||
import LabelChange from './LabelChange';
|
|
||||||
import SetStatus from './SetStatus';
|
|
||||||
import SetTitle from './SetTitle';
|
|
||||||
import Timeline from './Timeline';
|
|
||||||
import Message from './Message';
|
|
||||||
|
|
||||||
const QUERY = gql`
|
|
||||||
query($id: String!, $first: Int = 10, $after: String) {
|
|
||||||
defaultRepository {
|
|
||||||
bug(prefix: $id) {
|
|
||||||
timeline(first: $first, after: $after) {
|
|
||||||
nodes {
|
|
||||||
...LabelChange
|
|
||||||
...SetStatus
|
|
||||||
...SetTitle
|
|
||||||
...AddComment
|
|
||||||
...Create
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
endCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${Message.createFragment}
|
|
||||||
${Message.commentFragment}
|
|
||||||
${LabelChange.fragment}
|
|
||||||
${SetTitle.fragment}
|
|
||||||
${SetStatus.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TimelineQuery = ({ id }) => (
|
|
||||||
<Query query={QUERY} variables={{ id, first: 100 }}>
|
|
||||||
{({ loading, error, data, fetchMore }) => {
|
|
||||||
if (loading) return <CircularProgress />;
|
|
||||||
if (error) return <p>Error: {error}</p>;
|
|
||||||
return (
|
|
||||||
<Timeline
|
|
||||||
ops={data.defaultRepository.bug.timeline.nodes}
|
|
||||||
fetchMore={fetchMore}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default TimelineQuery;
|
|
30
webui/src/bug/TimelineQuery.tsx
Normal file
30
webui/src/bug/TimelineQuery.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Timeline from './Timeline';
|
||||||
|
import { useTimelineQuery } from './TimelineQuery.generated';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TimelineQuery = ({ id }: Props) => {
|
||||||
|
const { loading, error, data } = useTimelineQuery({
|
||||||
|
variables: {
|
||||||
|
id,
|
||||||
|
first: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <CircularProgress />;
|
||||||
|
if (error) return <p>Error: {error}</p>;
|
||||||
|
|
||||||
|
const nodes = data?.repository?.bug?.timeline.nodes;
|
||||||
|
if (!nodes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Timeline ops={nodes} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TimelineQuery;
|
@ -1,5 +1,5 @@
|
|||||||
import ThemeProvider from '@material-ui/styles/ThemeProvider';
|
|
||||||
import { createMuiTheme } from '@material-ui/core/styles';
|
import { createMuiTheme } from '@material-ui/core/styles';
|
||||||
|
import ThemeProvider from '@material-ui/styles/ThemeProvider';
|
||||||
import ApolloClient from 'apollo-boost';
|
import ApolloClient from 'apollo-boost';
|
||||||
import {
|
import {
|
||||||
IntrospectionFragmentMatcher,
|
IntrospectionFragmentMatcher,
|
||||||
@ -10,8 +10,8 @@ import { ApolloProvider } from 'react-apollo';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import introspectionQueryResultData from './fragmentTypes';
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
import introspectionQueryResultData from './fragmentTypes';
|
||||||
|
|
||||||
const theme = createMuiTheme();
|
const theme = createMuiTheme();
|
||||||
|
|
14
webui/src/list/BugRow.graphql
Normal file
14
webui/src/list/BugRow.graphql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#import "../Author.graphql"
|
||||||
|
#import "../Label.graphql"
|
||||||
|
|
||||||
|
fragment BugRow on Bug {
|
||||||
|
id
|
||||||
|
humanId
|
||||||
|
title
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
labels {
|
||||||
|
...Label
|
||||||
|
}
|
||||||
|
...authored
|
||||||
|
}
|
@ -1,36 +1,43 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import TableCell from '@material-ui/core/TableCell/TableCell';
|
import TableCell from '@material-ui/core/TableCell/TableCell';
|
||||||
import TableRow from '@material-ui/core/TableRow/TableRow';
|
import TableRow from '@material-ui/core/TableRow/TableRow';
|
||||||
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
||||||
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
|
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
|
||||||
import gql from 'graphql-tag';
|
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import Date from '../Date';
|
import Date from '../Date';
|
||||||
import Label from '../Label';
|
import Label from '../Label';
|
||||||
import Author from '../Author';
|
import { Status } from '../gqlTypes';
|
||||||
|
|
||||||
const Open = ({ className }) => (
|
import { BugRowFragment } from './BugRow.generated';
|
||||||
|
|
||||||
|
type OpenClosedProps = { className: string };
|
||||||
|
const Open = ({ className }: OpenClosedProps) => (
|
||||||
<Tooltip title="Open">
|
<Tooltip title="Open">
|
||||||
<ErrorOutline htmlColor="#28a745" className={className} />
|
<ErrorOutline htmlColor="#28a745" className={className} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Closed = ({ className }) => (
|
const Closed = ({ className }: OpenClosedProps) => (
|
||||||
<Tooltip title="Closed">
|
<Tooltip title="Closed">
|
||||||
<CheckCircleOutline htmlColor="#cb2431" className={className} />
|
<CheckCircleOutline htmlColor="#cb2431" className={className} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Status = ({ status, className }) => {
|
type StatusProps = { className: string; status: Status };
|
||||||
|
const BugStatus: React.FC<StatusProps> = ({
|
||||||
|
status,
|
||||||
|
className,
|
||||||
|
}: StatusProps) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'OPEN':
|
case 'OPEN':
|
||||||
return <Open className={className} />;
|
return <Open className={className} />;
|
||||||
case 'CLOSED':
|
case 'CLOSED':
|
||||||
return <Closed className={className} />;
|
return <Closed className={className} />;
|
||||||
default:
|
default:
|
||||||
return 'unknown status ' + status;
|
return <p>{'unknown status ' + status}</p>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,7 +64,6 @@ const useStyles = makeStyles(theme => ({
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
...theme.typography.textSecondary,
|
|
||||||
lineHeight: '1.5rem',
|
lineHeight: '1.5rem',
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
},
|
},
|
||||||
@ -69,12 +75,16 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function BugRow({ bug }) {
|
type Props = {
|
||||||
|
bug: BugRowFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
function BugRow({ bug }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<TableRow hover>
|
<TableRow hover>
|
||||||
<TableCell className={classes.cell}>
|
<TableCell className={classes.cell}>
|
||||||
<Status status={bug.status} className={classes.status} />
|
<BugStatus status={bug.status} className={classes.status} />
|
||||||
<div className={classes.expand}>
|
<div className={classes.expand}>
|
||||||
<Link to={'bug/' + bug.humanId}>
|
<Link to={'bug/' + bug.humanId}>
|
||||||
<div className={classes.expand}>
|
<div className={classes.expand}>
|
||||||
@ -99,21 +109,4 @@ function BugRow({ bug }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BugRow.fragment = gql`
|
|
||||||
fragment BugRow on Bug {
|
|
||||||
id
|
|
||||||
humanId
|
|
||||||
title
|
|
||||||
status
|
|
||||||
createdAt
|
|
||||||
labels {
|
|
||||||
...Label
|
|
||||||
}
|
|
||||||
...authored
|
|
||||||
}
|
|
||||||
|
|
||||||
${Label.fragment}
|
|
||||||
${Author.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default BugRow;
|
export default BugRow;
|
@ -1,13 +1,18 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { makeStyles } from '@material-ui/styles';
|
|
||||||
import Menu from '@material-ui/core/Menu';
|
import Menu from '@material-ui/core/Menu';
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
|
import { SvgIconProps } from '@material-ui/core/SvgIcon';
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
|
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { LocationDescriptor } from 'history';
|
||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
function parse(query) {
|
export type Query = { [key: string]: Array<string> };
|
||||||
|
|
||||||
|
function parse(query: string): Query {
|
||||||
// TODO: extract the rest of the query?
|
// TODO: extract the rest of the query?
|
||||||
const params = {};
|
const params: Query = {};
|
||||||
|
|
||||||
// TODO: support escaping without quotes
|
// TODO: support escaping without quotes
|
||||||
const re = /(\w+):([A-Za-z0-9-]+|(["'])(([^\3]|\\.)*)\3)+/g;
|
const re = /(\w+):([A-Za-z0-9-]+|(["'])(([^\3]|\\.)*)\3)+/g;
|
||||||
@ -29,7 +34,7 @@ function parse(query) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
function quote(value) {
|
function quote(value: string): string {
|
||||||
const hasSingle = value.includes("'");
|
const hasSingle = value.includes("'");
|
||||||
const hasDouble = value.includes('"');
|
const hasDouble = value.includes('"');
|
||||||
const hasSpaces = value.includes(' ');
|
const hasSpaces = value.includes(' ');
|
||||||
@ -49,19 +54,19 @@ function quote(value) {
|
|||||||
return `"${value}"`;
|
return `"${value}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringify(params) {
|
function stringify(params: Query): string {
|
||||||
const parts = Object.entries(params).map(([key, values]) => {
|
const parts: string[][] = Object.entries(params).map(([key, values]) => {
|
||||||
return values.map(value => `${key}:${quote(value)}`);
|
return values.map(value => `${key}:${quote(value)}`);
|
||||||
});
|
});
|
||||||
return [].concat(...parts).join(' ');
|
return new Array<string>().concat(...parts).join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
element: {
|
element: {
|
||||||
...theme.typography.body2,
|
...theme.typography.body2,
|
||||||
color: ({ active }) => (active ? '#333' : '#444'),
|
color: '#444',
|
||||||
padding: theme.spacing(0, 1),
|
padding: theme.spacing(0, 1),
|
||||||
fontWeight: ({ active }) => (active ? 600 : 400),
|
fontWeight: 400,
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
background: 'none',
|
background: 'none',
|
||||||
@ -69,21 +74,51 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
itemActive: {
|
itemActive: {
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
|
color: '#333',
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
paddingRight: theme.spacing(0.5),
|
paddingRight: theme.spacing(0.5),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Dropdown({ children, dropdown, itemActive, to, ...props }) {
|
type DropdownTuple = [string, string];
|
||||||
|
|
||||||
|
type FilterDropdownProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
dropdown: DropdownTuple[];
|
||||||
|
itemActive: (key: string) => boolean;
|
||||||
|
icon?: React.ComponentType<SvgIconProps>;
|
||||||
|
to: (key: string) => LocationDescriptor;
|
||||||
|
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||||
|
|
||||||
|
function FilterDropdown({
|
||||||
|
children,
|
||||||
|
dropdown,
|
||||||
|
itemActive,
|
||||||
|
icon: Icon,
|
||||||
|
to,
|
||||||
|
...props
|
||||||
|
}: FilterDropdownProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const buttonRef = useRef();
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const classes = useStyles();
|
const classes = useStyles({ active: false });
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<>
|
||||||
|
{Icon && <Icon fontSize="small" classes={{ root: classes.icon }} />}
|
||||||
|
<div>{children}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button ref={buttonRef} onClick={() => setOpen(!open)} {...props}>
|
<button
|
||||||
{children}
|
ref={buttonRef}
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
className={classes.element}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
<ArrowDropDown fontSize="small" />
|
<ArrowDropDown fontSize="small" />
|
||||||
</button>
|
</button>
|
||||||
<Menu
|
<Menu
|
||||||
@ -104,7 +139,7 @@ function Dropdown({ children, dropdown, itemActive, to, ...props }) {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
component={Link}
|
component={Link}
|
||||||
to={to(key)}
|
to={to(key)}
|
||||||
className={itemActive(key) ? classes.itemActive : null}
|
className={itemActive(key) ? classes.itemActive : undefined}
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
key={key}
|
key={key}
|
||||||
>
|
>
|
||||||
@ -116,8 +151,14 @@ function Dropdown({ children, dropdown, itemActive, to, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Filter({ active, to, children, icon: Icon, dropdown, ...props }) {
|
export type FilterProps = {
|
||||||
const classes = useStyles({ active });
|
active: boolean;
|
||||||
|
to: LocationDescriptor;
|
||||||
|
icon?: React.ComponentType<SvgIconProps>;
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
function Filter({ active, to, children, icon: Icon }: FilterProps) {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
@ -126,29 +167,23 @@ function Filter({ active, to, children, icon: Icon, dropdown, ...props }) {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dropdown) {
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
{...props}
|
|
||||||
to={to}
|
|
||||||
dropdown={dropdown}
|
|
||||||
className={classes.element}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
return (
|
return (
|
||||||
<Link to={to} {...props} className={classes.element}>
|
<Link
|
||||||
|
to={to}
|
||||||
|
className={clsx(classes.element, active && classes.itemActive)}
|
||||||
|
>
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={classes.element}>{content}</div>;
|
return (
|
||||||
|
<div className={clsx(classes.element, active && classes.itemActive)}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Filter;
|
export default Filter;
|
||||||
export { parse, stringify, quote };
|
export { parse, stringify, quote, FilterDropdown, Filter };
|
7
webui/src/list/FilterToolbar.graphql
Normal file
7
webui/src/list/FilterToolbar.graphql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
query BugCount($query: String) {
|
||||||
|
repository {
|
||||||
|
bugs: allBugs(query: $query) {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
import { makeStyles } from '@material-ui/styles';
|
import { pipe } from '@arrows/composition';
|
||||||
import { useQuery } from '@apollo/react-hooks';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import React from 'react';
|
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
|
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
|
||||||
import Filter, { parse, stringify } from './Filter';
|
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
||||||
|
import { LocationDescriptor } from 'history';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
// simple pipe operator
|
import {
|
||||||
// pipe(o, f, g, h) <=> h(g(f(o)))
|
FilterDropdown,
|
||||||
// TODO: move this out?
|
FilterProps,
|
||||||
const pipe = (initial, ...funcs) => funcs.reduce((acc, f) => f(acc), initial);
|
Filter,
|
||||||
|
parse,
|
||||||
|
stringify,
|
||||||
|
Query,
|
||||||
|
} from './Filter';
|
||||||
|
import { useBugCountQuery } from './FilterToolbar.generated';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
toolbar: {
|
toolbar: {
|
||||||
@ -25,27 +29,21 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const BUG_COUNT_QUERY = gql`
|
|
||||||
query($query: String) {
|
|
||||||
defaultRepository {
|
|
||||||
bugs: allBugs(query: $query) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// This prepends the filter text with a count
|
// This prepends the filter text with a count
|
||||||
function CountingFilter({ query, children, ...props }) {
|
type CountingFilterProps = {
|
||||||
const { data, loading, error } = useQuery(BUG_COUNT_QUERY, {
|
query: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
} & FilterProps;
|
||||||
|
function CountingFilter({ query, children, ...props }: CountingFilterProps) {
|
||||||
|
const { data, loading, error } = useBugCountQuery({
|
||||||
variables: { query },
|
variables: { query },
|
||||||
});
|
});
|
||||||
|
|
||||||
var prefix;
|
var prefix;
|
||||||
if (loading) prefix = '...';
|
if (loading) prefix = '...';
|
||||||
else if (error) prefix = '???';
|
else if (error || !data?.repository) prefix = '???';
|
||||||
// TODO: better prefixes & error handling
|
// TODO: better prefixes & error handling
|
||||||
else prefix = data.defaultRepository.bugs.totalCount;
|
else prefix = data.repository.bugs.totalCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Filter {...props}>
|
<Filter {...props}>
|
||||||
@ -54,18 +52,26 @@ function CountingFilter({ query, children, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FilterToolbar({ query, queryLocation }) {
|
type Props = {
|
||||||
|
query: string;
|
||||||
|
queryLocation: (query: string) => LocationDescriptor;
|
||||||
|
};
|
||||||
|
function FilterToolbar({ query, queryLocation }: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const params = parse(query);
|
const params: Query = parse(query);
|
||||||
|
|
||||||
const hasKey = key => params[key] && params[key].length > 0;
|
const hasKey = (key: string): boolean =>
|
||||||
const hasValue = (key, value) => hasKey(key) && params[key].includes(value);
|
params[key] && params[key].length > 0;
|
||||||
const loc = params => pipe(params, stringify, queryLocation);
|
const hasValue = (key: string, value: string): boolean =>
|
||||||
const replaceParam = (key, value) => params => ({
|
hasKey(key) && params[key].includes(value);
|
||||||
|
const loc = pipe(stringify, queryLocation);
|
||||||
|
const replaceParam = (key: string, value: string) => (
|
||||||
|
params: Query
|
||||||
|
): Query => ({
|
||||||
...params,
|
...params,
|
||||||
[key]: [value],
|
[key]: [value],
|
||||||
});
|
});
|
||||||
const clearParam = key => params => ({
|
const clearParam = (key: string) => (params: Query): Query => ({
|
||||||
...params,
|
...params,
|
||||||
[key]: [],
|
[key]: [],
|
||||||
});
|
});
|
||||||
@ -76,12 +82,11 @@ function FilterToolbar({ query, queryLocation }) {
|
|||||||
<CountingFilter
|
<CountingFilter
|
||||||
active={hasValue('status', 'open')}
|
active={hasValue('status', 'open')}
|
||||||
query={pipe(
|
query={pipe(
|
||||||
params,
|
|
||||||
replaceParam('status', 'open'),
|
replaceParam('status', 'open'),
|
||||||
clearParam('sort'),
|
clearParam('sort'),
|
||||||
stringify
|
stringify
|
||||||
)}
|
)(params)}
|
||||||
to={pipe(params, replaceParam('status', 'open'), loc)}
|
to={pipe(replaceParam('status', 'open'), loc)(params)}
|
||||||
icon={ErrorOutline}
|
icon={ErrorOutline}
|
||||||
>
|
>
|
||||||
open
|
open
|
||||||
@ -89,12 +94,11 @@ function FilterToolbar({ query, queryLocation }) {
|
|||||||
<CountingFilter
|
<CountingFilter
|
||||||
active={hasValue('status', 'closed')}
|
active={hasValue('status', 'closed')}
|
||||||
query={pipe(
|
query={pipe(
|
||||||
params,
|
|
||||||
replaceParam('status', 'closed'),
|
replaceParam('status', 'closed'),
|
||||||
clearParam('sort'),
|
clearParam('sort'),
|
||||||
stringify
|
stringify
|
||||||
)}
|
)(params)}
|
||||||
to={pipe(params, replaceParam('status', 'closed'), loc)}
|
to={pipe(replaceParam('status', 'closed'), loc)(params)}
|
||||||
icon={CheckCircleOutline}
|
icon={CheckCircleOutline}
|
||||||
>
|
>
|
||||||
closed
|
closed
|
||||||
@ -104,7 +108,7 @@ function FilterToolbar({ query, queryLocation }) {
|
|||||||
<Filter active={hasKey('author')}>Author</Filter>
|
<Filter active={hasKey('author')}>Author</Filter>
|
||||||
<Filter active={hasKey('label')}>Label</Filter>
|
<Filter active={hasKey('label')}>Label</Filter>
|
||||||
*/}
|
*/}
|
||||||
<Filter
|
<FilterDropdown
|
||||||
dropdown={[
|
dropdown={[
|
||||||
['id', 'ID'],
|
['id', 'ID'],
|
||||||
['creation', 'Newest'],
|
['creation', 'Newest'],
|
||||||
@ -112,12 +116,11 @@ function FilterToolbar({ query, queryLocation }) {
|
|||||||
['edit', 'Recently updated'],
|
['edit', 'Recently updated'],
|
||||||
['edit-asc', 'Least recently updated'],
|
['edit-asc', 'Least recently updated'],
|
||||||
]}
|
]}
|
||||||
active={hasKey('sort')}
|
|
||||||
itemActive={key => hasValue('sort', key)}
|
itemActive={key => hasValue('sort', key)}
|
||||||
to={key => pipe(params, replaceParam('sort', key), loc)}
|
to={key => pipe(replaceParam('sort', key), loc)(params)}
|
||||||
>
|
>
|
||||||
Sort
|
Sort
|
||||||
</Filter>
|
</FilterDropdown>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,9 +1,12 @@
|
|||||||
import Table from '@material-ui/core/Table/Table';
|
import Table from '@material-ui/core/Table/Table';
|
||||||
import TableBody from '@material-ui/core/TableBody/TableBody';
|
import TableBody from '@material-ui/core/TableBody/TableBody';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BugRow from './BugRow';
|
|
||||||
|
|
||||||
function List({ bugs }) {
|
import BugRow from './BugRow';
|
||||||
|
import { BugListFragment } from './ListQuery.generated';
|
||||||
|
|
||||||
|
type Props = { bugs: BugListFragment };
|
||||||
|
function List({ bugs }: Props) {
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
<TableBody>
|
<TableBody>
|
37
webui/src/list/ListQuery.graphql
Normal file
37
webui/src/list/ListQuery.graphql
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#import "./BugRow.graphql"
|
||||||
|
|
||||||
|
query ListBugs(
|
||||||
|
$first: Int
|
||||||
|
$last: Int
|
||||||
|
$after: String
|
||||||
|
$before: String
|
||||||
|
$query: String
|
||||||
|
) {
|
||||||
|
repository {
|
||||||
|
bugs: allBugs(
|
||||||
|
first: $first
|
||||||
|
last: $last
|
||||||
|
after: $after
|
||||||
|
before: $before
|
||||||
|
query: $query
|
||||||
|
) {
|
||||||
|
...BugList
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment BugList on BugConnection {
|
||||||
|
totalCount
|
||||||
|
edges {
|
||||||
|
cursor
|
||||||
|
node {
|
||||||
|
...BugRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,21 @@
|
|||||||
import { fade, makeStyles } from '@material-ui/core/styles';
|
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import InputBase from '@material-ui/core/InputBase';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import { fade, makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
|
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
||||||
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
|
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
|
||||||
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
|
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
|
||||||
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
|
||||||
import Paper from '@material-ui/core/Paper';
|
|
||||||
import InputBase from '@material-ui/core/InputBase';
|
|
||||||
import Skeleton from '@material-ui/lab/Skeleton';
|
import Skeleton from '@material-ui/lab/Skeleton';
|
||||||
import gql from 'graphql-tag';
|
import { ApolloError } from 'apollo-boost';
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { useQuery } from '@apollo/react-hooks';
|
|
||||||
import { useLocation, useHistory, Link } from 'react-router-dom';
|
import { useLocation, useHistory, Link } from 'react-router-dom';
|
||||||
import BugRow from './BugRow';
|
|
||||||
import List from './List';
|
|
||||||
import FilterToolbar from './FilterToolbar';
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
import FilterToolbar from './FilterToolbar';
|
||||||
|
import List from './List';
|
||||||
|
import { useListBugsQuery } from './ListQuery.generated';
|
||||||
|
|
||||||
|
type StylesProps = { searching?: boolean };
|
||||||
|
const useStyles = makeStyles<Theme, StylesProps>(theme => ({
|
||||||
main: {
|
main: {
|
||||||
maxWidth: 800,
|
maxWidth: 800,
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
@ -46,7 +47,11 @@ const useStyles = makeStyles(theme => ({
|
|||||||
backgroundColor: fade(theme.palette.primary.main, 0.05),
|
backgroundColor: fade(theme.palette.primary.main, 0.05),
|
||||||
padding: theme.spacing(0, 1),
|
padding: theme.spacing(0, 1),
|
||||||
width: ({ searching }) => (searching ? '20rem' : '15rem'),
|
width: ({ searching }) => (searching ? '20rem' : '15rem'),
|
||||||
transition: theme.transitions.create(),
|
transition: theme.transitions.create([
|
||||||
|
'width',
|
||||||
|
'borderColor',
|
||||||
|
'backgroundColor',
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
searchFocused: {
|
searchFocused: {
|
||||||
borderColor: fade(theme.palette.primary.main, 0.4),
|
borderColor: fade(theme.palette.primary.main, 0.4),
|
||||||
@ -91,51 +96,21 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const QUERY = gql`
|
function editParams(
|
||||||
query(
|
params: URLSearchParams,
|
||||||
$first: Int
|
callback: (params: URLSearchParams) => void
|
||||||
$last: Int
|
) {
|
||||||
$after: String
|
|
||||||
$before: String
|
|
||||||
$query: String
|
|
||||||
) {
|
|
||||||
defaultRepository {
|
|
||||||
bugs: allBugs(
|
|
||||||
first: $first
|
|
||||||
last: $last
|
|
||||||
after: $after
|
|
||||||
before: $before
|
|
||||||
query: $query
|
|
||||||
) {
|
|
||||||
totalCount
|
|
||||||
edges {
|
|
||||||
cursor
|
|
||||||
node {
|
|
||||||
...BugRow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
hasPreviousPage
|
|
||||||
startCursor
|
|
||||||
endCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${BugRow.fragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
function editParams(params, callback) {
|
|
||||||
const cloned = new URLSearchParams(params.toString());
|
const cloned = new URLSearchParams(params.toString());
|
||||||
callback(cloned);
|
callback(cloned);
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: factor this out
|
// TODO: factor this out
|
||||||
const Placeholder = ({ count }) => {
|
type PlaceholderProps = { count: number };
|
||||||
const classes = useStyles();
|
const Placeholder: React.FC<PlaceholderProps> = ({
|
||||||
|
count,
|
||||||
|
}: PlaceholderProps) => {
|
||||||
|
const classes = useStyles({});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{new Array(count).fill(null).map((_, i) => (
|
{new Array(count).fill(null).map((_, i) => (
|
||||||
@ -158,7 +133,7 @@ const Placeholder = ({ count }) => {
|
|||||||
|
|
||||||
// TODO: factor this out
|
// TODO: factor this out
|
||||||
const NoBug = () => {
|
const NoBug = () => {
|
||||||
const classes = useStyles();
|
const classes = useStyles({});
|
||||||
return (
|
return (
|
||||||
<div className={classes.message}>
|
<div className={classes.message}>
|
||||||
<ErrorOutline fontSize="large" />
|
<ErrorOutline fontSize="large" />
|
||||||
@ -167,8 +142,9 @@ const NoBug = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Error = ({ error }) => {
|
type ErrorProps = { error: ApolloError };
|
||||||
const classes = useStyles();
|
const Error: React.FC<ErrorProps> = ({ error }: ErrorProps) => {
|
||||||
|
const classes = useStyles({});
|
||||||
return (
|
return (
|
||||||
<div className={[classes.errorBox, classes.message].join(' ')}>
|
<div className={[classes.errorBox, classes.message].join(' ')}>
|
||||||
<ErrorOutline fontSize="large" />
|
<ErrorOutline fontSize="large" />
|
||||||
@ -194,7 +170,7 @@ function ListQuery() {
|
|||||||
const classes = useStyles({ searching: !!input });
|
const classes = useStyles({ searching: !!input });
|
||||||
|
|
||||||
// TODO is this the right way to do it?
|
// TODO is this the right way to do it?
|
||||||
const lastQuery = useRef();
|
const lastQuery = useRef<string | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query !== lastQuery.current) {
|
if (query !== lastQuery.current) {
|
||||||
setInput(query);
|
setInput(query);
|
||||||
@ -202,9 +178,10 @@ function ListQuery() {
|
|||||||
lastQuery.current = query;
|
lastQuery.current = query;
|
||||||
}, [query, input, lastQuery]);
|
}, [query, input, lastQuery]);
|
||||||
|
|
||||||
|
const num = (param: string | null) => (param ? parseInt(param) : null);
|
||||||
const page = {
|
const page = {
|
||||||
first: params.get('first'),
|
first: num(params.get('first')),
|
||||||
last: params.get('last'),
|
last: num(params.get('last')),
|
||||||
after: params.get('after'),
|
after: params.get('after'),
|
||||||
before: params.get('before'),
|
before: params.get('before'),
|
||||||
};
|
};
|
||||||
@ -214,9 +191,9 @@ function ListQuery() {
|
|||||||
page.first = 10;
|
page.first = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
const perPage = page.first || page.last;
|
const perPage = (page.first || page.last || 10).toString();
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(QUERY, {
|
const { loading, error, data } = useListBugsQuery({
|
||||||
variables: {
|
variables: {
|
||||||
...page,
|
...page,
|
||||||
query,
|
query,
|
||||||
@ -225,34 +202,34 @@ function ListQuery() {
|
|||||||
|
|
||||||
let nextPage = null;
|
let nextPage = null;
|
||||||
let previousPage = null;
|
let previousPage = null;
|
||||||
let hasNextPage = false;
|
|
||||||
let hasPreviousPage = false;
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
if (!loading && !error && data.defaultRepository.bugs) {
|
if (!loading && !error && data?.repository?.bugs) {
|
||||||
const bugs = data.defaultRepository.bugs;
|
const bugs = data.repository.bugs;
|
||||||
hasNextPage = bugs.pageInfo.hasNextPage;
|
|
||||||
hasPreviousPage = bugs.pageInfo.hasPreviousPage;
|
|
||||||
count = bugs.totalCount;
|
count = bugs.totalCount;
|
||||||
// This computes the URL for the next page
|
// This computes the URL for the next page
|
||||||
nextPage = {
|
if (bugs.pageInfo.hasNextPage) {
|
||||||
...location,
|
nextPage = {
|
||||||
search: editParams(params, p => {
|
...location,
|
||||||
p.delete('last');
|
search: editParams(params, p => {
|
||||||
p.delete('before');
|
p.delete('last');
|
||||||
p.set('first', perPage);
|
p.delete('before');
|
||||||
p.set('after', bugs.pageInfo.endCursor);
|
p.set('first', perPage);
|
||||||
}).toString(),
|
p.set('after', bugs.pageInfo.endCursor);
|
||||||
};
|
}).toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
// and this for the previous page
|
// and this for the previous page
|
||||||
previousPage = {
|
if (bugs.pageInfo.hasPreviousPage) {
|
||||||
...location,
|
previousPage = {
|
||||||
search: editParams(params, p => {
|
...location,
|
||||||
p.delete('first');
|
search: editParams(params, p => {
|
||||||
p.delete('after');
|
p.delete('first');
|
||||||
p.set('last', perPage);
|
p.delete('after');
|
||||||
p.set('before', bugs.pageInfo.startCursor);
|
p.set('last', perPage);
|
||||||
}).toString(),
|
p.set('before', bugs.pageInfo.startCursor);
|
||||||
};
|
}).toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare params without paging for editing filters
|
// Prepare params without paging for editing filters
|
||||||
@ -263,7 +240,7 @@ function ListQuery() {
|
|||||||
p.delete('after');
|
p.delete('after');
|
||||||
});
|
});
|
||||||
// Returns a new location with the `q` param edited
|
// Returns a new location with the `q` param edited
|
||||||
const queryLocation = query => ({
|
const queryLocation = (query: string) => ({
|
||||||
...location,
|
...location,
|
||||||
search: editParams(paramsWithoutPaging, p => p.set('q', query)).toString(),
|
search: editParams(paramsWithoutPaging, p => p.set('q', query)).toString(),
|
||||||
});
|
});
|
||||||
@ -273,8 +250,8 @@ function ListQuery() {
|
|||||||
content = <Placeholder count={10} />;
|
content = <Placeholder count={10} />;
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
content = <Error error={error} />;
|
content = <Error error={error} />;
|
||||||
} else {
|
} else if (data?.repository) {
|
||||||
const bugs = data.defaultRepository.bugs;
|
const bugs = data.repository.bugs;
|
||||||
|
|
||||||
if (bugs.totalCount === 0) {
|
if (bugs.totalCount === 0) {
|
||||||
content = <NoBug />;
|
content = <NoBug />;
|
||||||
@ -283,7 +260,7 @@ function ListQuery() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const formSubmit = e => {
|
const formSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.push(queryLocation(input));
|
history.push(queryLocation(input));
|
||||||
};
|
};
|
||||||
@ -296,7 +273,7 @@ function ListQuery() {
|
|||||||
<InputBase
|
<InputBase
|
||||||
placeholder="Filter"
|
placeholder="Filter"
|
||||||
value={input}
|
value={input}
|
||||||
onInput={e => setInput(e.target.value)}
|
onInput={(e: any) => setInput(e.target.value)}
|
||||||
classes={{
|
classes={{
|
||||||
root: classes.search,
|
root: classes.search,
|
||||||
focused: classes.searchFocused,
|
focused: classes.searchFocused,
|
||||||
@ -310,21 +287,25 @@ function ListQuery() {
|
|||||||
<FilterToolbar query={query} queryLocation={queryLocation} />
|
<FilterToolbar query={query} queryLocation={queryLocation} />
|
||||||
{content}
|
{content}
|
||||||
<div className={classes.pagination}>
|
<div className={classes.pagination}>
|
||||||
<IconButton
|
{previousPage ? (
|
||||||
component={hasPreviousPage ? Link : 'button'}
|
<IconButton component={Link} to={previousPage}>
|
||||||
to={previousPage}
|
<KeyboardArrowLeft />
|
||||||
disabled={!hasPreviousPage}
|
</IconButton>
|
||||||
>
|
) : (
|
||||||
<KeyboardArrowLeft />
|
<IconButton disabled>
|
||||||
</IconButton>
|
<KeyboardArrowLeft />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
<div>{loading ? 'Loading' : `Total: ${count}`}</div>
|
<div>{loading ? 'Loading' : `Total: ${count}`}</div>
|
||||||
<IconButton
|
{nextPage ? (
|
||||||
component={hasNextPage ? Link : 'button'}
|
<IconButton component={Link} to={nextPage}>
|
||||||
to={nextPage}
|
<KeyboardArrowRight />
|
||||||
disabled={!hasNextPage}
|
</IconButton>
|
||||||
>
|
) : (
|
||||||
<KeyboardArrowRight />
|
<IconButton disabled>
|
||||||
</IconButton>
|
<KeyboardArrowRight />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
1
webui/src/react-app-env.d.ts
vendored
Normal file
1
webui/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
|
||||||
import { makeStyles } from '@material-ui/styles';
|
import { makeStyles } from '@material-ui/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
tag: {
|
tag: {
|
||||||
@ -7,7 +7,10 @@ const useStyles = makeStyles({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ImageTag = ({ alt, ...props }) => {
|
const ImageTag = ({
|
||||||
|
alt,
|
||||||
|
...props
|
||||||
|
}: React.ImgHTMLAttributes<HTMLImageElement>) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<a href={props.src} target="_blank" rel="noopener noreferrer nofollow">
|
<a href={props.src} target="_blank" rel="noopener noreferrer nofollow">
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
|
||||||
import { makeStyles } from '@material-ui/styles';
|
import { makeStyles } from '@material-ui/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
tag: {
|
tag: {
|
||||||
@ -8,7 +8,7 @@ const useStyles = makeStyles({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const PreTag = props => {
|
const PreTag = (props: React.HTMLProps<HTMLPreElement>) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return <pre className={classes.tag} {...props}></pre>;
|
return <pre className={classes.tag} {...props}></pre>;
|
||||||
};
|
};
|
20
webui/tsconfig.json
Normal file
20
webui/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"typeRoots": ["node_modules/@types/", "types/"]
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
6
webui/types/remark-html/index.d.ts
vendored
Normal file
6
webui/types/remark-html/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
declare module 'remark-html' {
|
||||||
|
import { Plugin } from 'unified';
|
||||||
|
|
||||||
|
const plugin: Plugin;
|
||||||
|
export default plugin;
|
||||||
|
}
|
6
webui/types/remark-react/index.d.ts
vendored
Normal file
6
webui/types/remark-react/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
declare module 'remark-react' {
|
||||||
|
import { Plugin } from 'unified';
|
||||||
|
|
||||||
|
const plugin: Plugin;
|
||||||
|
export default plugin;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user