diff --git a/.gqlgen.yml b/.gqlgen.yml index 7363cf7..eae3b9c 100644 --- a/.gqlgen.yml +++ b/.gqlgen.yml @@ -42,7 +42,33 @@ models: extraFields: F: type: "*github.com/anacrolix/torrent.PeerConn" - # TorrentProgress: - # fields: - # torrent: - # resolver: true + SimpleDir: + fields: + entries: + resolver: true + extraFields: + Path: + type: string + FS: + type: "git.kmsign.ru/royalcat/tstor/src/host/vfs.Filesystem" + TorrentFS: + fields: + entries: + resolver: true + extraFields: + FS: + type: "*git.kmsign.ru/royalcat/tstor/src/host/vfs.TorrentFS" + ResolverFS: + fields: + entries: + resolver: true + extraFields: + FS: + type: "*git.kmsign.ru/royalcat/tstor/src/host/vfs.ResolverFS" + ArchiveFS: + fields: + entries: + resolver: true + extraFields: + FS: + type: "*git.kmsign.ru/royalcat/tstor/src/host/vfs.ArchiveFS" diff --git a/Makefile b/Makefile index 028fa69..a171a40 100644 --- a/Makefile +++ b/Makefile @@ -1,40 +1,7 @@ -#-include .env +generate-graphql: src/delivery/graphql/generated.go ui/lib/api/schema.graphql -VERSION := $(shell git describe --tags) -BUILD := $(shell git rev-parse --short HEAD) -BIN_OUTPUT ?= bin/tstor-$(VERSION)-`go env GOOS`-`go env GOARCH``go env GOEXE` -PROJECTNAME := $(shell basename "$(PWD)") - -# Use linker flags to provide version/build settings -LDFLAGS=-X=main.Version=$(VERSION) -X=main.Build=$(BUILD) -linkmode external - -# Make is verbose in Linux. Make it silent. -MAKEFLAGS += --silent - -## run: run from code. -run: - go run cmd/tstor/main.go examples/conf_example.yaml - -## build: build binary. -build: go-generate go-build - -## test-race: execute all tests with race enabled. -test-race: - go test -v --race -coverprofile=coverage.out ./... - -## test: execute all tests -test: - go test -v -coverprofile=coverage.out ./... - -go-build: - @echo " > Building binary on $(BIN_OUTPUT)..." - go build -o $(BIN_OUTPUT) -tags "release" -ldflags='$(LDFLAGS)' cmd/tstor/main.go - -go-generate: - @echo " > Generating code files..." - go generate ./... - -generate-graphql: src/delivery/graph/generated.go - -src/delivery/graph/generated.go: .gqlgen.yml graphql/* graphql/types/* cmd/generate-graphql/* +src/delivery/graphql/generated.go: .gqlgen.yml graphql/* graphql/types/* cmd/generate-graphql/* go run cmd/generate-graphql/main.go + +ui/lib/api/schema.graphql: src/delivery/graphql/* cmd/generate-graphql-schema/* + go run cmd/generate-graphql-schema/main.go $@ \ No newline at end of file diff --git a/cmd/generate-graphql-schema/main.go b/cmd/generate-graphql-schema/main.go new file mode 100644 index 0000000..5abdf1c --- /dev/null +++ b/cmd/generate-graphql-schema/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "log" + "os" + + graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql" + "github.com/vektah/gqlparser/v2/formatter" +) + +func main() { + outFile := "schema.graphql" + + if len(os.Args) > 1 { + outFile = os.Args[1] + } + + file, err := os.OpenFile(outFile, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm) + if err != nil { + log.Panic(fmt.Errorf("Failed to open %s: %w", outFile, err)) + } + defer file.Close() + + fmt := formatter.NewFormatter(file) + fmt.FormatSchema(graph.NewExecutableSchema(graph.Config{}).Schema()) +} diff --git a/cmd/tstor/main.go b/cmd/tstor/main.go index d039567..6c80826 100644 --- a/cmd/tstor/main.go +++ b/cmd/tstor/main.go @@ -70,18 +70,15 @@ func run(configPath string) error { } // dlog.Load(&conf.Log) - if conf.OtelHttp != "" { - ctx := context.Background() - client, err := telemetry.Setup(ctx, conf.OtelHttp) - if err != nil { - return err - } - - defer client.Shutdown(ctx) + ctx := context.Background() + client, err := telemetry.Setup(ctx, conf.OtelHttp) + if err != nil { + return err } + defer client.Shutdown(ctx) + log := rlog.Component("run") - ctx := context.Background() // TODO make optional err = syscall.Setpriority(syscall.PRIO_PGRP, 0, 19) diff --git a/go.mod b/go.mod index 7d9e013..8fcdb08 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.kmsign.ru/royalcat/tstor go 1.22.1 require ( - github.com/99designs/gqlgen v0.17.43 + github.com/99designs/gqlgen v0.17.45 github.com/agoda-com/opentelemetry-go/otelslog v0.1.1 github.com/agoda-com/opentelemetry-logs-go v0.3.0 github.com/anacrolix/dht/v2 v2.21.1 @@ -15,7 +15,6 @@ require ( github.com/dgraph-io/badger/v4 v4.2.0 github.com/dgraph-io/ristretto v0.1.1 github.com/dustin/go-humanize v1.0.0 - github.com/gin-contrib/pprof v1.4.0 github.com/gin-gonic/gin v1.9.1 github.com/go-git/go-billy/v5 v5.5.0 github.com/gofrs/uuid/v5 v5.0.0 @@ -29,6 +28,8 @@ require ( github.com/knadh/koanf/providers/file v0.1.0 github.com/knadh/koanf/providers/structs v0.1.0 github.com/knadh/koanf/v2 v2.0.1 + github.com/labstack/echo-contrib v0.17.1 + github.com/labstack/echo/v4 v4.12.0 github.com/nwaples/rardecode/v2 v2.0.0-beta.2 github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 github.com/ravilushqa/otelgqlgen v0.15.0 @@ -36,9 +37,8 @@ require ( github.com/rs/zerolog v1.32.0 github.com/samber/slog-multi v1.0.2 github.com/samber/slog-zerolog v1.0.0 - github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c github.com/stretchr/testify v1.9.0 - github.com/urfave/cli/v2 v2.27.0 + github.com/urfave/cli/v2 v2.27.1 github.com/vektah/gqlparser/v2 v2.5.11 github.com/willscott/go-nfs-client v0.0.0-20240104095149-b44639837b00 github.com/willscott/memphis v0.0.0-20210922141505-529d4987ab7e @@ -50,8 +50,8 @@ require ( go.opentelemetry.io/otel/trace v1.25.0 go.uber.org/multierr v1.11.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b - golang.org/x/net v0.23.0 - golang.org/x/sys v0.18.0 + golang.org/x/net v0.24.0 + golang.org/x/sys v0.19.0 ) require ( @@ -81,7 +81,7 @@ require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -100,6 +100,7 @@ require ( github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -115,6 +116,7 @@ require ( github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/labstack/gommon v0.4.2 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -159,6 +161,8 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/ulikunitz/xz v0.5.11 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect github.com/warpfork/go-errcat v0.0.0-20180917083543-335044ffc86e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.etcd.io/bbolt v1.3.6 // indirect @@ -169,15 +173,15 @@ require ( go.opentelemetry.io/proto/otlp v1.1.0 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/mod v0.16.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect - google.golang.org/grpc v1.63.0 // indirect + google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.22.3 // indirect diff --git a/go.sum b/go.sum index 8356481..ea15142 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/99designs/gqlgen v0.17.43 h1:I4SYg6ahjowErAQcHFVKy5EcWuwJ3+Xw9z2fLpuFCPo= -github.com/99designs/gqlgen v0.17.43/go.mod h1:lO0Zjy8MkZgBdv4T1U91x09r0e0WFOdhVUutlQs1Rsc= +github.com/99designs/gqlgen v0.17.45 h1:bH0AH67vIJo8JKNKPJP+pOPpQhZeuVRQLf53dKIpDik= +github.com/99designs/gqlgen v0.17.45/go.mod h1:Bas0XQ+Jiu/Xm5E33jC8sES3G+iC2esHBMXcq0fUPs0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -144,8 +144,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -195,11 +195,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= -github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -227,21 +224,16 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -251,6 +243,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= @@ -379,7 +373,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -387,12 +380,16 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU= +github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -432,7 +429,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -549,8 +545,6 @@ github.com/samber/slog-zerolog v1.0.0/go.mod h1:N2/g/mNGRY1zqsydIYE0uKipSSFsPDjy github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -592,14 +586,16 @@ github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.27.0 h1:uNs1K8JwTFL84X68j5Fjny6hfANh9nTlJ6dRtZAFAHY= -github.com/urfave/cli/v2 v2.27.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/warpfork/go-errcat v0.0.0-20180917083543-335044ffc86e h1:FIB2fi7XJGHIdf5rWNsfFQqatIKxutT45G+wNuMQNgs= @@ -666,14 +662,13 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -703,8 +698,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -741,8 +736,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -790,8 +785,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -807,8 +800,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -860,8 +853,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -910,8 +903,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -923,7 +916,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -944,7 +936,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/graphql/query.graphql b/graphql/query.graphql index 8161a9f..c01db35 100644 --- a/graphql/query.graphql +++ b/graphql/query.graphql @@ -1,9 +1,10 @@ type Query { torrents(filter: TorrentsFilter, pagination: Pagination): [Torrent!]! - fsListDir(path: String!): ListDirResponse! + fsEntry(path: String!): FsEntry } input TorrentsFilter { + infohash: StringFilter name: StringFilter bytesCompleted: IntFilter bytesMissing: IntFilter @@ -11,11 +12,6 @@ input TorrentsFilter { peersCount: IntFilter } -type ListDirResponse { - root: DirEntry! - entries: [DirEntry!]! -} - input Pagination { offset: Int! limit: Int! diff --git a/graphql/types/fs.graphql b/graphql/types/fs.graphql index 73ca119..4174f04 100644 --- a/graphql/types/fs.graphql +++ b/graphql/types/fs.graphql @@ -1,26 +1,47 @@ -interface DirEntry { +interface FsEntry { name: String! } -type Dir implements DirEntry { +interface Dir implements FsEntry { name: String! + entries: [FsEntry!]! } -type File implements DirEntry { +interface File implements FsEntry { name: String! size: Int! } -type ResolverFS implements DirEntry { +type SimpleDir implements Dir & FsEntry { name: String! + entries: [FsEntry!]! } -type TorrentFS implements DirEntry { +type SimpleFile implements File & FsEntry { + name: String! + size: Int! +} + +type ResolverFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! +} + +type ArchiveFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! + + size: Int! +} + +type TorrentFS implements Dir & FsEntry { name: String! torrent: Torrent! + entries: [FsEntry!]! } -type ArchiveFS implements DirEntry { +type TorrentFileEntry implements File & FsEntry { name: String! + torrent: Torrent! size: Int! } diff --git a/src/delivery/graphql/generated.go b/src/delivery/graphql/generated.go index 3253c0c..63e1b56 100644 --- a/src/delivery/graphql/generated.go +++ b/src/delivery/graphql/generated.go @@ -40,10 +40,14 @@ type Config struct { } type ResolverRoot interface { + ArchiveFS() ArchiveFSResolver Mutation() MutationResolver Query() QueryResolver + ResolverFS() ResolverFSResolver + SimpleDir() SimpleDirResolver Subscription() SubscriptionResolver Torrent() TorrentResolver + TorrentFS() TorrentFSResolver } type DirectiveRoot struct { @@ -53,8 +57,9 @@ type DirectiveRoot struct { type ComplexityRoot struct { ArchiveFS struct { - Name func(childComplexity int) int - Size func(childComplexity int) int + Entries func(childComplexity int) int + Name func(childComplexity int) int + Size func(childComplexity int) int } CleanupResponse struct { @@ -62,24 +67,10 @@ type ComplexityRoot struct { List func(childComplexity int) int } - Dir struct { - Name func(childComplexity int) int - } - DownloadTorrentResponse struct { Task func(childComplexity int) int } - File struct { - Name func(childComplexity int) int - Size func(childComplexity int) int - } - - ListDirResponse struct { - Entries func(childComplexity int) int - Root func(childComplexity int) int - } - Mutation struct { CleanupTorrents func(childComplexity int, files *bool, dryRun bool) int DedupeStorage func(childComplexity int) int @@ -88,12 +79,13 @@ type ComplexityRoot struct { } Query struct { - FsListDir func(childComplexity int, path string) int - Torrents func(childComplexity int, filter *model.TorrentsFilter, pagination *model.Pagination) int + FsEntry func(childComplexity int, path string) int + Torrents func(childComplexity int, filter *model.TorrentsFilter, pagination *model.Pagination) int } ResolverFS struct { - Name func(childComplexity int) int + Entries func(childComplexity int) int + Name func(childComplexity int) int } Schema struct { @@ -101,6 +93,16 @@ type ComplexityRoot struct { Query func(childComplexity int) int } + SimpleDir struct { + Entries func(childComplexity int) int + Name func(childComplexity int) int + } + + SimpleFile struct { + Name func(childComplexity int) int + Size func(childComplexity int) int + } + Subscription struct { TaskProgress func(childComplexity int, taskID string) int TorrentDownloadUpdates func(childComplexity int) int @@ -122,6 +124,7 @@ type ComplexityRoot struct { } TorrentFS struct { + Entries func(childComplexity int) int Name func(childComplexity int) int Torrent func(childComplexity int) int } @@ -132,6 +135,12 @@ type ComplexityRoot struct { Size func(childComplexity int) int } + TorrentFileEntry struct { + Name func(childComplexity int) int + Size func(childComplexity int) int + Torrent func(childComplexity int) int + } + TorrentPeer struct { ClientName func(childComplexity int) int Discovery func(childComplexity int) int @@ -147,6 +156,9 @@ type ComplexityRoot struct { } } +type ArchiveFSResolver interface { + Entries(ctx context.Context, obj *model.ArchiveFs) ([]model.FsEntry, error) +} type MutationResolver interface { ValidateTorrents(ctx context.Context, filter model.TorrentFilter) (bool, error) CleanupTorrents(ctx context.Context, files *bool, dryRun bool) (*model.CleanupResponse, error) @@ -155,7 +167,13 @@ type MutationResolver interface { } type QueryResolver interface { Torrents(ctx context.Context, filter *model.TorrentsFilter, pagination *model.Pagination) ([]*model.Torrent, error) - FsListDir(ctx context.Context, path string) (*model.ListDirResponse, error) + FsEntry(ctx context.Context, path string) (model.FsEntry, error) +} +type ResolverFSResolver interface { + Entries(ctx context.Context, obj *model.ResolverFs) ([]model.FsEntry, error) +} +type SimpleDirResolver interface { + Entries(ctx context.Context, obj *model.SimpleDir) ([]model.FsEntry, error) } type SubscriptionResolver interface { TaskProgress(ctx context.Context, taskID string) (<-chan model.Progress, error) @@ -168,6 +186,9 @@ type TorrentResolver interface { ExcludedFiles(ctx context.Context, obj *model.Torrent) ([]*model.TorrentFile, error) Peers(ctx context.Context, obj *model.Torrent) ([]*model.TorrentPeer, error) } +type TorrentFSResolver interface { + Entries(ctx context.Context, obj *model.TorrentFs) ([]model.FsEntry, error) +} type executableSchema struct { schema *ast.Schema @@ -188,6 +209,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "ArchiveFS.entries": + if e.complexity.ArchiveFS.Entries == nil { + break + } + + return e.complexity.ArchiveFS.Entries(childComplexity), true + case "ArchiveFS.name": if e.complexity.ArchiveFS.Name == nil { break @@ -216,13 +244,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CleanupResponse.List(childComplexity), true - case "Dir.name": - if e.complexity.Dir.Name == nil { - break - } - - return e.complexity.Dir.Name(childComplexity), true - case "DownloadTorrentResponse.task": if e.complexity.DownloadTorrentResponse.Task == nil { break @@ -230,34 +251,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.DownloadTorrentResponse.Task(childComplexity), true - case "File.name": - if e.complexity.File.Name == nil { - break - } - - return e.complexity.File.Name(childComplexity), true - - case "File.size": - if e.complexity.File.Size == nil { - break - } - - return e.complexity.File.Size(childComplexity), true - - case "ListDirResponse.entries": - if e.complexity.ListDirResponse.Entries == nil { - break - } - - return e.complexity.ListDirResponse.Entries(childComplexity), true - - case "ListDirResponse.root": - if e.complexity.ListDirResponse.Root == nil { - break - } - - return e.complexity.ListDirResponse.Root(childComplexity), true - case "Mutation.cleanupTorrents": if e.complexity.Mutation.CleanupTorrents == nil { break @@ -301,17 +294,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.ValidateTorrents(childComplexity, args["filter"].(model.TorrentFilter)), true - case "Query.fsListDir": - if e.complexity.Query.FsListDir == nil { + case "Query.fsEntry": + if e.complexity.Query.FsEntry == nil { break } - args, err := ec.field_Query_fsListDir_args(context.TODO(), rawArgs) + args, err := ec.field_Query_fsEntry_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Query.FsListDir(childComplexity, args["path"].(string)), true + return e.complexity.Query.FsEntry(childComplexity, args["path"].(string)), true case "Query.torrents": if e.complexity.Query.Torrents == nil { @@ -325,6 +318,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Torrents(childComplexity, args["filter"].(*model.TorrentsFilter), args["pagination"].(*model.Pagination)), true + case "ResolverFS.entries": + if e.complexity.ResolverFS.Entries == nil { + break + } + + return e.complexity.ResolverFS.Entries(childComplexity), true + case "ResolverFS.name": if e.complexity.ResolverFS.Name == nil { break @@ -346,6 +346,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Schema.Query(childComplexity), true + case "SimpleDir.entries": + if e.complexity.SimpleDir.Entries == nil { + break + } + + return e.complexity.SimpleDir.Entries(childComplexity), true + + case "SimpleDir.name": + if e.complexity.SimpleDir.Name == nil { + break + } + + return e.complexity.SimpleDir.Name(childComplexity), true + + case "SimpleFile.name": + if e.complexity.SimpleFile.Name == nil { + break + } + + return e.complexity.SimpleFile.Name(childComplexity), true + + case "SimpleFile.size": + if e.complexity.SimpleFile.Size == nil { + break + } + + return e.complexity.SimpleFile.Size(childComplexity), true + case "Subscription.taskProgress": if e.complexity.Subscription.TaskProgress == nil { break @@ -428,6 +456,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Torrent.TorrentFilePath(childComplexity), true + case "TorrentFS.entries": + if e.complexity.TorrentFS.Entries == nil { + break + } + + return e.complexity.TorrentFS.Entries(childComplexity), true + case "TorrentFS.name": if e.complexity.TorrentFS.Name == nil { break @@ -463,6 +498,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.TorrentFile.Size(childComplexity), true + case "TorrentFileEntry.name": + if e.complexity.TorrentFileEntry.Name == nil { + break + } + + return e.complexity.TorrentFileEntry.Name(childComplexity), true + + case "TorrentFileEntry.size": + if e.complexity.TorrentFileEntry.Size == nil { + break + } + + return e.complexity.TorrentFileEntry.Size(childComplexity), true + + case "TorrentFileEntry.torrent": + if e.complexity.TorrentFileEntry.Torrent == nil { + break + } + + return e.complexity.TorrentFileEntry.Torrent(childComplexity), true + case "TorrentPeer.clientName": if e.complexity.TorrentPeer.ClientName == nil { break @@ -676,10 +732,11 @@ type Task { `, BuiltIn: false}, {Name: "../../../graphql/query.graphql", Input: `type Query { torrents(filter: TorrentsFilter, pagination: Pagination): [Torrent!]! - fsListDir(path: String!): ListDirResponse! + fsEntry(path: String!): FsEntry } input TorrentsFilter { + infohash: StringFilter name: StringFilter bytesCompleted: IntFilter bytesMissing: IntFilter @@ -687,11 +744,6 @@ input TorrentsFilter { peersCount: IntFilter } -type ListDirResponse { - root: DirEntry! - entries: [DirEntry!]! -} - input Pagination { offset: Int! limit: Int! @@ -750,30 +802,51 @@ interface Progress { current: Int! total: Int! }`, BuiltIn: false}, - {Name: "../../../graphql/types/fs.graphql", Input: `interface DirEntry { + {Name: "../../../graphql/types/fs.graphql", Input: `interface FsEntry { name: String! } -type Dir implements DirEntry { +interface Dir implements FsEntry { name: String! + entries: [FsEntry!]! } -type File implements DirEntry { +interface File implements FsEntry { name: String! size: Int! } -type ResolverFS implements DirEntry { +type SimpleDir implements Dir & FsEntry { name: String! + entries: [FsEntry!]! } -type TorrentFS implements DirEntry { +type SimpleFile implements File & FsEntry { + name: String! + size: Int! +} + +type ResolverFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! +} + +type ArchiveFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! + + size: Int! +} + +type TorrentFS implements Dir & FsEntry { name: String! torrent: Torrent! + entries: [FsEntry!]! } -type ArchiveFS implements DirEntry { +type TorrentFileEntry implements File & FsEntry { name: String! + torrent: Torrent! size: Int! } `, BuiltIn: false}, @@ -886,7 +959,7 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } -func (ec *executionContext) field_Query_fsListDir_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_fsEntry_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 string @@ -1022,6 +1095,50 @@ func (ec *executionContext) fieldContext_ArchiveFS_name(ctx context.Context, fie return fc, nil } +func (ec *executionContext) _ArchiveFS_entries(ctx context.Context, field graphql.CollectedField, obj *model.ArchiveFs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ArchiveFS_entries(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.ArchiveFS().Entries(rctx, obj) + }) + 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.([]model.FsEntry) + fc.Result = res + return ec.marshalNFsEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ArchiveFS_entries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ArchiveFS", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + func (ec *executionContext) _ArchiveFS_size(ctx context.Context, field graphql.CollectedField, obj *model.ArchiveFs) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ArchiveFS_size(ctx, field) if err != nil { @@ -1154,50 +1271,6 @@ func (ec *executionContext) fieldContext_CleanupResponse_list(ctx context.Contex return fc, nil } -func (ec *executionContext) _Dir_name(ctx context.Context, field graphql.CollectedField, obj *model.Dir) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Dir_name(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, 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.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Dir_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Dir", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _DownloadTorrentResponse_task(ctx context.Context, field graphql.CollectedField, obj *model.DownloadTorrentResponse) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DownloadTorrentResponse_task(ctx, field) if err != nil { @@ -1243,182 +1316,6 @@ func (ec *executionContext) fieldContext_DownloadTorrentResponse_task(ctx contex return fc, nil } -func (ec *executionContext) _File_name(ctx context.Context, field graphql.CollectedField, obj *model.File) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_File_name(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, 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.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_File_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "File", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _File_size(ctx context.Context, field graphql.CollectedField, obj *model.File) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_File_size(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Size, 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.(int64) - fc.Result = res - return ec.marshalNInt2int64(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_File_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "File", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _ListDirResponse_root(ctx context.Context, field graphql.CollectedField, obj *model.ListDirResponse) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ListDirResponse_root(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Root, 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.(model.DirEntry) - fc.Result = res - return ec.marshalNDirEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐDirEntry(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ListDirResponse_root(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ListDirResponse", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") - }, - } - return fc, nil -} - -func (ec *executionContext) _ListDirResponse_entries(ctx context.Context, field graphql.CollectedField, obj *model.ListDirResponse) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ListDirResponse_entries(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Entries, 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.([]model.DirEntry) - fc.Result = res - return ec.marshalNDirEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐDirEntryᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_ListDirResponse_entries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "ListDirResponse", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") - }, - } - return fc, nil -} - func (ec *executionContext) _Mutation_validateTorrents(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_validateTorrents(ctx, field) if err != nil { @@ -1708,8 +1605,8 @@ func (ec *executionContext) fieldContext_Query_torrents(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Query_fsListDir(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_fsListDir(ctx, field) +func (ec *executionContext) _Query_fsEntry(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_fsEntry(ctx, field) if err != nil { return graphql.Null } @@ -1722,37 +1619,28 @@ func (ec *executionContext) _Query_fsListDir(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().FsListDir(rctx, fc.Args["path"].(string)) + return ec.resolvers.Query().FsEntry(rctx, fc.Args["path"].(string)) }) 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.(*model.ListDirResponse) + res := resTmp.(model.FsEntry) fc.Result = res - return ec.marshalNListDirResponse2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐListDirResponse(ctx, field.Selections, res) + return ec.marshalOFsEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntry(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_fsListDir(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_fsEntry(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "root": - return ec.fieldContext_ListDirResponse_root(ctx, field) - case "entries": - return ec.fieldContext_ListDirResponse_entries(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ListDirResponse", field.Name) + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") }, } defer func() { @@ -1762,7 +1650,7 @@ func (ec *executionContext) fieldContext_Query_fsListDir(ctx context.Context, fi } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_fsListDir_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Query_fsEntry_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -1942,6 +1830,50 @@ func (ec *executionContext) fieldContext_ResolverFS_name(ctx context.Context, fi return fc, nil } +func (ec *executionContext) _ResolverFS_entries(ctx context.Context, field graphql.CollectedField, obj *model.ResolverFs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ResolverFS_entries(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.ResolverFS().Entries(rctx, obj) + }) + 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.([]model.FsEntry) + fc.Result = res + return ec.marshalNFsEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ResolverFS_entries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ResolverFS", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + func (ec *executionContext) _Schema_query(ctx context.Context, field graphql.CollectedField, obj *model.Schema) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Schema_query(ctx, field) if err != nil { @@ -1969,8 +1901,8 @@ func (ec *executionContext) fieldContext_Schema_query(ctx context.Context, field switch field.Name { case "torrents": return ec.fieldContext_Query_torrents(ctx, field) - case "fsListDir": - return ec.fieldContext_Query_fsListDir(ctx, field) + case "fsEntry": + return ec.fieldContext_Query_fsEntry(ctx, field) case "__schema": return ec.fieldContext_Query___schema(ctx, field) case "__type": @@ -2022,6 +1954,182 @@ func (ec *executionContext) fieldContext_Schema_mutation(ctx context.Context, fi return fc, nil } +func (ec *executionContext) _SimpleDir_name(ctx context.Context, field graphql.CollectedField, obj *model.SimpleDir) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SimpleDir_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SimpleDir_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SimpleDir", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _SimpleDir_entries(ctx context.Context, field graphql.CollectedField, obj *model.SimpleDir) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SimpleDir_entries(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.SimpleDir().Entries(rctx, obj) + }) + 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.([]model.FsEntry) + fc.Result = res + return ec.marshalNFsEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SimpleDir_entries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SimpleDir", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + +func (ec *executionContext) _SimpleFile_name(ctx context.Context, field graphql.CollectedField, obj *model.SimpleFile) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SimpleFile_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SimpleFile_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SimpleFile", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _SimpleFile_size(ctx context.Context, field graphql.CollectedField, obj *model.SimpleFile) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SimpleFile_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, 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.(int64) + fc.Result = res + return ec.marshalNInt2int64(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SimpleFile_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SimpleFile", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Subscription_taskProgress(ctx context.Context, field graphql.CollectedField) (ret func(ctx context.Context) graphql.Marshaler) { fc, err := ec.fieldContext_Subscription_taskProgress(ctx, field) if err != nil { @@ -2681,6 +2789,50 @@ func (ec *executionContext) fieldContext_TorrentFS_torrent(ctx context.Context, return fc, nil } +func (ec *executionContext) _TorrentFS_entries(ctx context.Context, field graphql.CollectedField, obj *model.TorrentFs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TorrentFS_entries(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.TorrentFS().Entries(rctx, obj) + }) + 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.([]model.FsEntry) + fc.Result = res + return ec.marshalNFsEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TorrentFS_entries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TorrentFS", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + func (ec *executionContext) _TorrentFile_filename(ctx context.Context, field graphql.CollectedField, obj *model.TorrentFile) (ret graphql.Marshaler) { fc, err := ec.fieldContext_TorrentFile_filename(ctx, field) if err != nil { @@ -2813,6 +2965,156 @@ func (ec *executionContext) fieldContext_TorrentFile_bytesCompleted(ctx context. return fc, nil } +func (ec *executionContext) _TorrentFileEntry_name(ctx context.Context, field graphql.CollectedField, obj *model.TorrentFileEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TorrentFileEntry_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TorrentFileEntry_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TorrentFileEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TorrentFileEntry_torrent(ctx context.Context, field graphql.CollectedField, obj *model.TorrentFileEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TorrentFileEntry_torrent(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Torrent, 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.(*model.Torrent) + fc.Result = res + return ec.marshalNTorrent2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐTorrent(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TorrentFileEntry_torrent(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TorrentFileEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_Torrent_name(ctx, field) + case "infohash": + return ec.fieldContext_Torrent_infohash(ctx, field) + case "bytesCompleted": + return ec.fieldContext_Torrent_bytesCompleted(ctx, field) + case "torrentFilePath": + return ec.fieldContext_Torrent_torrentFilePath(ctx, field) + case "bytesMissing": + return ec.fieldContext_Torrent_bytesMissing(ctx, field) + case "files": + return ec.fieldContext_Torrent_files(ctx, field) + case "excludedFiles": + return ec.fieldContext_Torrent_excludedFiles(ctx, field) + case "peers": + return ec.fieldContext_Torrent_peers(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Torrent", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _TorrentFileEntry_size(ctx context.Context, field graphql.CollectedField, obj *model.TorrentFileEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TorrentFileEntry_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, 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.(int64) + fc.Result = res + return ec.marshalNInt2int64(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TorrentFileEntry_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TorrentFileEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _TorrentPeer_ip(ctx context.Context, field graphql.CollectedField, obj *model.TorrentPeer) (ret graphql.Marshaler) { fc, err := ec.fieldContext_TorrentPeer_ip(ctx, field) if err != nil { @@ -5471,109 +5773,48 @@ func (ec *executionContext) unmarshalInputTorrentsFilter(ctx context.Context, ob asMap[k] = v } - fieldsInOrder := [...]string{"name", "bytesCompleted", "bytesMissing", "peersCount"} + fieldsInOrder := [...]string{"infohash", "name", "bytesCompleted", "bytesMissing", "peersCount"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { + case "infohash": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("infohash")) + data, err := ec.unmarshalOStringFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐStringFilter(ctx, v) + if err != nil { + return it, err + } + it.Infohash = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - directive0 := func(ctx context.Context) (interface{}, error) { - return ec.unmarshalOStringFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐStringFilter(ctx, v) - } - directive1 := func(ctx context.Context) (interface{}, error) { - if ec.directives.OneOf == nil { - return nil, errors.New("directive oneOf is not implemented") - } - return ec.directives.OneOf(ctx, obj, directive0) - } - - tmp, err := directive1(ctx) + data, err := ec.unmarshalOStringFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐStringFilter(ctx, v) if err != nil { - return it, graphql.ErrorOnPath(ctx, err) - } - if data, ok := tmp.(*model.StringFilter); ok { - it.Name = data - } else if tmp == nil { - it.Name = nil - } else { - err := fmt.Errorf(`unexpected type %T from directive, should be *git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model.StringFilter`, tmp) - return it, graphql.ErrorOnPath(ctx, err) + return it, err } + it.Name = data case "bytesCompleted": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("bytesCompleted")) - directive0 := func(ctx context.Context) (interface{}, error) { - return ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) - } - directive1 := func(ctx context.Context) (interface{}, error) { - if ec.directives.OneOf == nil { - return nil, errors.New("directive oneOf is not implemented") - } - return ec.directives.OneOf(ctx, obj, directive0) - } - - tmp, err := directive1(ctx) + data, err := ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) if err != nil { - return it, graphql.ErrorOnPath(ctx, err) - } - if data, ok := tmp.(*model.IntFilter); ok { - it.BytesCompleted = data - } else if tmp == nil { - it.BytesCompleted = nil - } else { - err := fmt.Errorf(`unexpected type %T from directive, should be *git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model.IntFilter`, tmp) - return it, graphql.ErrorOnPath(ctx, err) + return it, err } + it.BytesCompleted = data case "bytesMissing": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("bytesMissing")) - directive0 := func(ctx context.Context) (interface{}, error) { - return ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) - } - directive1 := func(ctx context.Context) (interface{}, error) { - if ec.directives.OneOf == nil { - return nil, errors.New("directive oneOf is not implemented") - } - return ec.directives.OneOf(ctx, obj, directive0) - } - - tmp, err := directive1(ctx) + data, err := ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) if err != nil { - return it, graphql.ErrorOnPath(ctx, err) - } - if data, ok := tmp.(*model.IntFilter); ok { - it.BytesMissing = data - } else if tmp == nil { - it.BytesMissing = nil - } else { - err := fmt.Errorf(`unexpected type %T from directive, should be *git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model.IntFilter`, tmp) - return it, graphql.ErrorOnPath(ctx, err) + return it, err } + it.BytesMissing = data case "peersCount": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("peersCount")) - directive0 := func(ctx context.Context) (interface{}, error) { - return ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) - } - directive1 := func(ctx context.Context) (interface{}, error) { - if ec.directives.OneOf == nil { - return nil, errors.New("directive oneOf is not implemented") - } - return ec.directives.OneOf(ctx, obj, directive0) - } - - tmp, err := directive1(ctx) + data, err := ec.unmarshalOIntFilter2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐIntFilter(ctx, v) if err != nil { - return it, graphql.ErrorOnPath(ctx, err) - } - if data, ok := tmp.(*model.IntFilter); ok { - it.PeersCount = data - } else if tmp == nil { - it.PeersCount = nil - } else { - err := fmt.Errorf(`unexpected type %T from directive, should be *git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model.IntFilter`, tmp) - return it, graphql.ErrorOnPath(ctx, err) + return it, err } + it.PeersCount = data } } @@ -5584,24 +5825,17 @@ func (ec *executionContext) unmarshalInputTorrentsFilter(ctx context.Context, ob // region ************************** interface.gotpl *************************** -func (ec *executionContext) _DirEntry(ctx context.Context, sel ast.SelectionSet, obj model.DirEntry) graphql.Marshaler { +func (ec *executionContext) _Dir(ctx context.Context, sel ast.SelectionSet, obj model.Dir) graphql.Marshaler { switch obj := (obj).(type) { case nil: return graphql.Null - case model.Dir: - return ec._Dir(ctx, sel, &obj) - case *model.Dir: + case model.SimpleDir: + return ec._SimpleDir(ctx, sel, &obj) + case *model.SimpleDir: if obj == nil { return graphql.Null } - return ec._Dir(ctx, sel, obj) - case model.File: - return ec._File(ctx, sel, &obj) - case *model.File: - if obj == nil { - return graphql.Null - } - return ec._File(ctx, sel, obj) + return ec._SimpleDir(ctx, sel, obj) case model.ResolverFs: return ec._ResolverFS(ctx, sel, &obj) case *model.ResolverFs: @@ -5609,13 +5843,6 @@ func (ec *executionContext) _DirEntry(ctx context.Context, sel ast.SelectionSet, return graphql.Null } return ec._ResolverFS(ctx, sel, obj) - case model.TorrentFs: - return ec._TorrentFS(ctx, sel, &obj) - case *model.TorrentFs: - if obj == nil { - return graphql.Null - } - return ec._TorrentFS(ctx, sel, obj) case model.ArchiveFs: return ec._ArchiveFS(ctx, sel, &obj) case *model.ArchiveFs: @@ -5623,6 +5850,97 @@ func (ec *executionContext) _DirEntry(ctx context.Context, sel ast.SelectionSet, return graphql.Null } return ec._ArchiveFS(ctx, sel, obj) + case model.TorrentFs: + return ec._TorrentFS(ctx, sel, &obj) + case *model.TorrentFs: + if obj == nil { + return graphql.Null + } + return ec._TorrentFS(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + +func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj model.File) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case model.SimpleFile: + return ec._SimpleFile(ctx, sel, &obj) + case *model.SimpleFile: + if obj == nil { + return graphql.Null + } + return ec._SimpleFile(ctx, sel, obj) + case model.TorrentFileEntry: + return ec._TorrentFileEntry(ctx, sel, &obj) + case *model.TorrentFileEntry: + if obj == nil { + return graphql.Null + } + return ec._TorrentFileEntry(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + +func (ec *executionContext) _FsEntry(ctx context.Context, sel ast.SelectionSet, obj model.FsEntry) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case model.SimpleDir: + return ec._SimpleDir(ctx, sel, &obj) + case *model.SimpleDir: + if obj == nil { + return graphql.Null + } + return ec._SimpleDir(ctx, sel, obj) + case model.SimpleFile: + return ec._SimpleFile(ctx, sel, &obj) + case *model.SimpleFile: + if obj == nil { + return graphql.Null + } + return ec._SimpleFile(ctx, sel, obj) + case model.ResolverFs: + return ec._ResolverFS(ctx, sel, &obj) + case *model.ResolverFs: + if obj == nil { + return graphql.Null + } + return ec._ResolverFS(ctx, sel, obj) + case model.ArchiveFs: + return ec._ArchiveFS(ctx, sel, &obj) + case *model.ArchiveFs: + if obj == nil { + return graphql.Null + } + return ec._ArchiveFS(ctx, sel, obj) + case model.TorrentFs: + return ec._TorrentFS(ctx, sel, &obj) + case *model.TorrentFs: + if obj == nil { + return graphql.Null + } + return ec._TorrentFS(ctx, sel, obj) + case model.TorrentFileEntry: + return ec._TorrentFileEntry(ctx, sel, &obj) + case *model.TorrentFileEntry: + if obj == nil { + return graphql.Null + } + return ec._TorrentFileEntry(ctx, sel, obj) + case model.Dir: + if obj == nil { + return graphql.Null + } + return ec._Dir(ctx, sel, obj) + case model.File: + if obj == nil { + return graphql.Null + } + return ec._File(ctx, sel, obj) default: panic(fmt.Errorf("unexpected type %T", obj)) } @@ -5648,7 +5966,7 @@ func (ec *executionContext) _Progress(ctx context.Context, sel ast.SelectionSet, // region **************************** object.gotpl **************************** -var archiveFSImplementors = []string{"ArchiveFS", "DirEntry"} +var archiveFSImplementors = []string{"ArchiveFS", "Dir", "FsEntry"} func (ec *executionContext) _ArchiveFS(ctx context.Context, sel ast.SelectionSet, obj *model.ArchiveFs) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, archiveFSImplementors) @@ -5662,12 +5980,48 @@ func (ec *executionContext) _ArchiveFS(ctx context.Context, sel ast.SelectionSet case "name": out.Values[i] = ec._ArchiveFS_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "entries": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ArchiveFS_entries(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "size": out.Values[i] = ec._ArchiveFS_size(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -5736,45 +6090,6 @@ func (ec *executionContext) _CleanupResponse(ctx context.Context, sel ast.Select return out } -var dirImplementors = []string{"Dir", "DirEntry"} - -func (ec *executionContext) _Dir(ctx context.Context, sel ast.SelectionSet, obj *model.Dir) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, dirImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Dir") - case "name": - out.Values[i] = ec._Dir_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var downloadTorrentResponseImplementors = []string{"DownloadTorrentResponse"} func (ec *executionContext) _DownloadTorrentResponse(ctx context.Context, sel ast.SelectionSet, obj *model.DownloadTorrentResponse) graphql.Marshaler { @@ -5811,94 +6126,6 @@ func (ec *executionContext) _DownloadTorrentResponse(ctx context.Context, sel as return out } -var fileImplementors = []string{"File", "DirEntry"} - -func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj *model.File) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, fileImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("File") - case "name": - out.Values[i] = ec._File_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "size": - out.Values[i] = ec._File_size(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var listDirResponseImplementors = []string{"ListDirResponse"} - -func (ec *executionContext) _ListDirResponse(ctx context.Context, sel ast.SelectionSet, obj *model.ListDirResponse) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, listDirResponseImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("ListDirResponse") - case "root": - out.Values[i] = ec._ListDirResponse_root(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "entries": - out.Values[i] = ec._ListDirResponse_entries(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var mutationImplementors = []string{"Mutation"} func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { @@ -6007,7 +6234,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "fsListDir": + case "fsEntry": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -6016,10 +6243,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_fsListDir(ctx, field) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } + res = ec._Query_fsEntry(ctx, field) return res } @@ -6060,7 +6284,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return out } -var resolverFSImplementors = []string{"ResolverFS", "DirEntry"} +var resolverFSImplementors = []string{"ResolverFS", "Dir", "FsEntry"} func (ec *executionContext) _ResolverFS(ctx context.Context, sel ast.SelectionSet, obj *model.ResolverFs) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, resolverFSImplementors) @@ -6074,8 +6298,44 @@ func (ec *executionContext) _ResolverFS(ctx context.Context, sel ast.SelectionSe case "name": out.Values[i] = ec._ResolverFS_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "entries": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ResolverFS_entries(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -6137,6 +6397,125 @@ func (ec *executionContext) _Schema(ctx context.Context, sel ast.SelectionSet, o return out } +var simpleDirImplementors = []string{"SimpleDir", "Dir", "FsEntry"} + +func (ec *executionContext) _SimpleDir(ctx context.Context, sel ast.SelectionSet, obj *model.SimpleDir) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, simpleDirImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SimpleDir") + case "name": + out.Values[i] = ec._SimpleDir_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "entries": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._SimpleDir_entries(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var simpleFileImplementors = []string{"SimpleFile", "File", "FsEntry"} + +func (ec *executionContext) _SimpleFile(ctx context.Context, sel ast.SelectionSet, obj *model.SimpleFile) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, simpleFileImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SimpleFile") + case "name": + out.Values[i] = ec._SimpleFile_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "size": + out.Values[i] = ec._SimpleFile_size(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var subscriptionImplementors = []string{"Subscription"} func (ec *executionContext) _Subscription(ctx context.Context, sel ast.SelectionSet) func(ctx context.Context) graphql.Marshaler { @@ -6396,7 +6775,7 @@ func (ec *executionContext) _Torrent(ctx context.Context, sel ast.SelectionSet, return out } -var torrentFSImplementors = []string{"TorrentFS", "DirEntry"} +var torrentFSImplementors = []string{"TorrentFS", "Dir", "FsEntry"} func (ec *executionContext) _TorrentFS(ctx context.Context, sel ast.SelectionSet, obj *model.TorrentFs) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, torrentFSImplementors) @@ -6410,13 +6789,49 @@ func (ec *executionContext) _TorrentFS(ctx context.Context, sel ast.SelectionSet case "name": out.Values[i] = ec._TorrentFS_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "torrent": out.Values[i] = ec._TorrentFS_torrent(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "entries": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._TorrentFS_entries(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -6489,6 +6904,55 @@ func (ec *executionContext) _TorrentFile(ctx context.Context, sel ast.SelectionS return out } +var torrentFileEntryImplementors = []string{"TorrentFileEntry", "File", "FsEntry"} + +func (ec *executionContext) _TorrentFileEntry(ctx context.Context, sel ast.SelectionSet, obj *model.TorrentFileEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, torrentFileEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TorrentFileEntry") + case "name": + out.Values[i] = ec._TorrentFileEntry_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "torrent": + out.Values[i] = ec._TorrentFileEntry_torrent(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "size": + out.Values[i] = ec._TorrentFileEntry_size(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var torrentPeerImplementors = []string{"TorrentPeer"} func (ec *executionContext) _TorrentPeer(ctx context.Context, sel ast.SelectionSet, obj *model.TorrentPeer) graphql.Marshaler { @@ -6952,17 +7416,32 @@ func (ec *executionContext) marshalNCleanupResponse2ᚖgitᚗkmsignᚗruᚋroyal return ec._CleanupResponse(ctx, sel, v) } -func (ec *executionContext) marshalNDirEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐDirEntry(ctx context.Context, sel ast.SelectionSet, v model.DirEntry) graphql.Marshaler { +func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v interface{}) (float64, error) { + res, err := graphql.UnmarshalFloatContext(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + res := graphql.MarshalFloatContext(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return graphql.WrapContextMarshaler(ctx, res) +} + +func (ec *executionContext) marshalNFsEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntry(ctx context.Context, sel ast.SelectionSet, v model.FsEntry) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._DirEntry(ctx, sel, v) + return ec._FsEntry(ctx, sel, v) } -func (ec *executionContext) marshalNDirEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐDirEntryᚄ(ctx context.Context, sel ast.SelectionSet, v []model.DirEntry) graphql.Marshaler { +func (ec *executionContext) marshalNFsEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntryᚄ(ctx context.Context, sel ast.SelectionSet, v []model.FsEntry) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -6986,7 +7465,7 @@ func (ec *executionContext) marshalNDirEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋt if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNDirEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐDirEntry(ctx, sel, v[i]) + ret[i] = ec.marshalNFsEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntry(ctx, sel, v[i]) } if isLen1 { f(i) @@ -7006,21 +7485,6 @@ func (ec *executionContext) marshalNDirEntry2ᚕgitᚗkmsignᚗruᚋroyalcatᚋt return ret } -func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v interface{}) (float64, error) { - res, err := graphql.UnmarshalFloatContext(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { - res := graphql.MarshalFloatContext(v) - if res == graphql.Null { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - } - return graphql.WrapContextMarshaler(ctx, res) -} - func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalID(v) return res, graphql.ErrorOnPath(ctx, err) @@ -7051,20 +7515,6 @@ func (ec *executionContext) marshalNInt2int64(ctx context.Context, sel ast.Selec return res } -func (ec *executionContext) marshalNListDirResponse2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐListDirResponse(ctx context.Context, sel ast.SelectionSet, v model.ListDirResponse) graphql.Marshaler { - return ec._ListDirResponse(ctx, sel, &v) -} - -func (ec *executionContext) marshalNListDirResponse2ᚖgitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐListDirResponse(ctx context.Context, sel ast.SelectionSet, v *model.ListDirResponse) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._ListDirResponse(ctx, sel, v) -} - func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -7581,6 +8031,13 @@ func (ec *executionContext) marshalODownloadTorrentResponse2ᚖgitᚗkmsignᚗru return ec._DownloadTorrentResponse(ctx, sel, v) } +func (ec *executionContext) marshalOFsEntry2gitᚗkmsignᚗruᚋroyalcatᚋtstorᚋsrcᚋdeliveryᚋgraphqlᚋmodelᚐFsEntry(ctx context.Context, sel ast.SelectionSet, v model.FsEntry) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._FsEntry(ctx, sel, v) +} + func (ec *executionContext) unmarshalOInt2ᚕint64ᚄ(ctx context.Context, v interface{}) ([]int64, error) { if v == nil { return nil, nil diff --git a/src/delivery/graphql/model/entry.go b/src/delivery/graphql/model/entry.go new file mode 100644 index 0000000..940e9cc --- /dev/null +++ b/src/delivery/graphql/model/entry.go @@ -0,0 +1,47 @@ +package model + +import ( + "git.kmsign.ru/royalcat/tstor/src/host/vfs" +) + +type FsElem interface { + Name() string + IsDir() bool +} + +func FillFsEntry(e FsElem, fs vfs.Filesystem, path string) FsEntry { + switch e.(type) { + case *vfs.ArchiveFS: + e := e.(*vfs.ArchiveFS) + return ArchiveFs{ + Name: e.Name(), + Size: e.Size(), + FS: e, + } + case *vfs.ResolverFS: + e := e.(*vfs.ResolverFS) + return ResolverFs{ + Name: e.Name(), + FS: e, + } + case *vfs.TorrentFS: + e := e.(*vfs.TorrentFS) + return TorrentFs{ + Name: e.Name(), + Torrent: MapTorrent(e.Torrent), + FS: e, + } + default: + if e.IsDir() { + return SimpleDir{ + Name: e.Name(), + FS: fs, + Path: path, + } + } else { + return SimpleFile{ + Name: e.Name(), + } + } + } +} diff --git a/src/delivery/graphql/model/filter.go b/src/delivery/graphql/model/filter.go index 0c96e46..6ae7fc8 100644 --- a/src/delivery/graphql/model/filter.go +++ b/src/delivery/graphql/model/filter.go @@ -1,9 +1,18 @@ package model -import "slices" +import ( + "slices" + "strings" +) + +type Filter[T any] interface { + Include(v T) bool +} func (f *IntFilter) Include(v int64) bool { - if f.Eq != nil { + if f == nil { + return true + } else if f.Eq != nil { return v == *f.Eq } else if f.Gt != nil { return v > *f.Gt @@ -19,3 +28,17 @@ func (f *IntFilter) Include(v int64) bool { return true } + +func (f *StringFilter) Include(v string) bool { + if f == nil { + return true + } else if f.Eq != nil { + return v == *f.Eq + } else if f.Substr != nil { + return strings.Contains(v, *f.Substr) + } else if f.In != nil { + return slices.Contains(f.In, v) + } + + return true +} diff --git a/src/delivery/graphql/model/models_gen.go b/src/delivery/graphql/model/models_gen.go index 6852932..b7f4807 100644 --- a/src/delivery/graphql/model/models_gen.go +++ b/src/delivery/graphql/model/models_gen.go @@ -6,11 +6,26 @@ import ( "time" "git.kmsign.ru/royalcat/tstor/src/host/controller" + "git.kmsign.ru/royalcat/tstor/src/host/vfs" "github.com/anacrolix/torrent" ) -type DirEntry interface { - IsDirEntry() +type Dir interface { + IsFsEntry() + IsDir() + GetName() string + GetEntries() []FsEntry +} + +type File interface { + IsFsEntry() + IsFile() + GetName() string + GetSize() int64 +} + +type FsEntry interface { + IsFsEntry() GetName() string } @@ -21,12 +36,26 @@ type Progress interface { } type ArchiveFs struct { - Name string `json:"name"` - Size int64 `json:"size"` + Name string `json:"name"` + Entries []FsEntry `json:"entries"` + Size int64 `json:"size"` + FS *vfs.ArchiveFS `json:"-"` } -func (ArchiveFs) IsDirEntry() {} +func (ArchiveFs) IsDir() {} func (this ArchiveFs) GetName() string { return this.Name } +func (this ArchiveFs) GetEntries() []FsEntry { + if this.Entries == nil { + return nil + } + interfaceSlice := make([]FsEntry, 0, len(this.Entries)) + for _, concrete := range this.Entries { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +func (ArchiveFs) IsFsEntry() {} type BooleanFilter struct { Eq *bool `json:"eq,omitempty"` @@ -45,25 +74,10 @@ type DateTimeFilter struct { Lte *time.Time `json:"lte,omitempty"` } -type Dir struct { - Name string `json:"name"` -} - -func (Dir) IsDirEntry() {} -func (this Dir) GetName() string { return this.Name } - type DownloadTorrentResponse struct { Task *Task `json:"task,omitempty"` } -type File struct { - Name string `json:"name"` - Size int64 `json:"size"` -} - -func (File) IsDirEntry() {} -func (this File) GetName() string { return this.Name } - type IntFilter struct { Eq *int64 `json:"eq,omitempty"` Gt *int64 `json:"gt,omitempty"` @@ -73,11 +87,6 @@ type IntFilter struct { In []int64 `json:"in,omitempty"` } -type ListDirResponse struct { - Root DirEntry `json:"root"` - Entries []DirEntry `json:"entries"` -} - type Mutation struct { } @@ -90,17 +99,64 @@ type Query struct { } type ResolverFs struct { - Name string `json:"name"` + Name string `json:"name"` + Entries []FsEntry `json:"entries"` + FS *vfs.ResolverFS `json:"-"` } -func (ResolverFs) IsDirEntry() {} +func (ResolverFs) IsDir() {} func (this ResolverFs) GetName() string { return this.Name } +func (this ResolverFs) GetEntries() []FsEntry { + if this.Entries == nil { + return nil + } + interfaceSlice := make([]FsEntry, 0, len(this.Entries)) + for _, concrete := range this.Entries { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +func (ResolverFs) IsFsEntry() {} type Schema struct { Query *Query `json:"query,omitempty"` Mutation *Mutation `json:"mutation,omitempty"` } +type SimpleDir struct { + Name string `json:"name"` + Entries []FsEntry `json:"entries"` + FS vfs.Filesystem `json:"-"` + Path string `json:"-"` +} + +func (SimpleDir) IsDir() {} +func (this SimpleDir) GetName() string { return this.Name } +func (this SimpleDir) GetEntries() []FsEntry { + if this.Entries == nil { + return nil + } + interfaceSlice := make([]FsEntry, 0, len(this.Entries)) + for _, concrete := range this.Entries { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +func (SimpleDir) IsFsEntry() {} + +type SimpleFile struct { + Name string `json:"name"` + Size int64 `json:"size"` +} + +func (SimpleFile) IsFile() {} +func (this SimpleFile) GetName() string { return this.Name } +func (this SimpleFile) GetSize() int64 { return this.Size } + +func (SimpleFile) IsFsEntry() {} + type StringFilter struct { Eq *string `json:"eq,omitempty"` Substr *string `json:"substr,omitempty"` @@ -127,12 +183,26 @@ type Torrent struct { } type TorrentFs struct { - Name string `json:"name"` - Torrent *Torrent `json:"torrent"` + Name string `json:"name"` + Torrent *Torrent `json:"torrent"` + Entries []FsEntry `json:"entries"` + FS *vfs.TorrentFS `json:"-"` } -func (TorrentFs) IsDirEntry() {} +func (TorrentFs) IsDir() {} func (this TorrentFs) GetName() string { return this.Name } +func (this TorrentFs) GetEntries() []FsEntry { + if this.Entries == nil { + return nil + } + interfaceSlice := make([]FsEntry, 0, len(this.Entries)) + for _, concrete := range this.Entries { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +func (TorrentFs) IsFsEntry() {} type TorrentFile struct { Filename string `json:"filename"` @@ -141,6 +211,18 @@ type TorrentFile struct { F *torrent.File `json:"-"` } +type TorrentFileEntry struct { + Name string `json:"name"` + Torrent *Torrent `json:"torrent"` + Size int64 `json:"size"` +} + +func (TorrentFileEntry) IsFile() {} +func (this TorrentFileEntry) GetName() string { return this.Name } +func (this TorrentFileEntry) GetSize() int64 { return this.Size } + +func (TorrentFileEntry) IsFsEntry() {} + type TorrentFilter struct { Everything *bool `json:"everything,omitempty"` Infohash *string `json:"infohash,omitempty"` @@ -166,6 +248,7 @@ func (this TorrentProgress) GetCurrent() int64 { return this.Current } func (this TorrentProgress) GetTotal() int64 { return this.Total } type TorrentsFilter struct { + Infohash *StringFilter `json:"infohash,omitempty"` Name *StringFilter `json:"name,omitempty"` BytesCompleted *IntFilter `json:"bytesCompleted,omitempty"` BytesMissing *IntFilter `json:"bytesMissing,omitempty"` diff --git a/src/delivery/graphql/resolver/fs.resolvers.go b/src/delivery/graphql/resolver/fs.resolvers.go new file mode 100644 index 0000000..999fd9b --- /dev/null +++ b/src/delivery/graphql/resolver/fs.resolvers.go @@ -0,0 +1,81 @@ +package resolver + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.45 + +import ( + "context" + + graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql" + "git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model" +) + +// Entries is the resolver for the entries field. +func (r *archiveFSResolver) Entries(ctx context.Context, obj *model.ArchiveFs) ([]model.FsEntry, error) { + entries, err := obj.FS.ReadDir(ctx, ".") + if err != nil { + return nil, err + } + out := []model.FsEntry{} + for _, e := range entries { + out = append(out, model.FillFsEntry(e, obj.FS, ".")) + } + return out, nil +} + +// Entries is the resolver for the entries field. +func (r *resolverFSResolver) Entries(ctx context.Context, obj *model.ResolverFs) ([]model.FsEntry, error) { + entries, err := obj.FS.ReadDir(ctx, ".") + if err != nil { + return nil, err + } + out := []model.FsEntry{} + for _, e := range entries { + out = append(out, model.FillFsEntry(e, obj.FS, ".")) + } + return out, nil +} + +// Entries is the resolver for the entries field. +func (r *simpleDirResolver) Entries(ctx context.Context, obj *model.SimpleDir) ([]model.FsEntry, error) { + entries, err := obj.FS.ReadDir(ctx, obj.Path) + if err != nil { + return nil, err + } + out := []model.FsEntry{} + for _, e := range entries { + out = append(out, model.FillFsEntry(e, obj.FS, obj.Path)) + } + return out, nil +} + +// Entries is the resolver for the entries field. +func (r *torrentFSResolver) Entries(ctx context.Context, obj *model.TorrentFs) ([]model.FsEntry, error) { + entries, err := obj.FS.ReadDir(ctx, ".") + if err != nil { + return nil, err + } + out := []model.FsEntry{} + for _, e := range entries { + out = append(out, model.FillFsEntry(e, obj.FS, ".")) + } + return out, nil +} + +// ArchiveFS returns graph.ArchiveFSResolver implementation. +func (r *Resolver) ArchiveFS() graph.ArchiveFSResolver { return &archiveFSResolver{r} } + +// ResolverFS returns graph.ResolverFSResolver implementation. +func (r *Resolver) ResolverFS() graph.ResolverFSResolver { return &resolverFSResolver{r} } + +// SimpleDir returns graph.SimpleDirResolver implementation. +func (r *Resolver) SimpleDir() graph.SimpleDirResolver { return &simpleDirResolver{r} } + +// TorrentFS returns graph.TorrentFSResolver implementation. +func (r *Resolver) TorrentFS() graph.TorrentFSResolver { return &torrentFSResolver{r} } + +type archiveFSResolver struct{ *Resolver } +type resolverFSResolver struct{ *Resolver } +type simpleDirResolver struct{ *Resolver } +type torrentFSResolver struct{ *Resolver } diff --git a/src/delivery/graphql/resolver/mutation.resolvers.go b/src/delivery/graphql/resolver/mutation.resolvers.go index ed7dc31..0ec91aa 100644 --- a/src/delivery/graphql/resolver/mutation.resolvers.go +++ b/src/delivery/graphql/resolver/mutation.resolvers.go @@ -2,7 +2,7 @@ package resolver // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.43 +// Code generated by github.com/99designs/gqlgen version v0.17.45 import ( "context" diff --git a/src/delivery/graphql/resolver/query.resolvers.go b/src/delivery/graphql/resolver/query.resolvers.go index 1b6aa43..705cb65 100644 --- a/src/delivery/graphql/resolver/query.resolvers.go +++ b/src/delivery/graphql/resolver/query.resolvers.go @@ -2,15 +2,16 @@ package resolver // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.43 +// Code generated by github.com/99designs/gqlgen version v0.17.45 import ( "context" - "io/fs" + "slices" + "strings" graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql" "git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model" - "git.kmsign.ru/royalcat/tstor/src/host/vfs" + "git.kmsign.ru/royalcat/tstor/src/host/controller" ) // Torrents is the resolver for the torrents field. @@ -40,6 +41,13 @@ func (r *queryResolver) Torrents(ctx context.Context, filter *model.TorrentsFilt ) }) } + if filter.Infohash != nil { + filterFuncs = append(filterFuncs, func(torrent *model.Torrent) bool { + return filter.Infohash.Include( + torrent.Infohash, + ) + }) + } } @@ -62,78 +70,21 @@ func (r *queryResolver) Torrents(ctx context.Context, filter *model.TorrentsFilt tr = append(tr, d) } + slices.SortStableFunc(torrents, func(t1, t2 *controller.Torrent) int { + return strings.Compare(t1.InfoHash(), t2.InfoHash()) + }) + return tr, nil } -type dirEntry interface { - Name() string - IsDir() bool -} - -func fillDirEntry(e dirEntry) model.DirEntry { - switch e.(type) { - case *vfs.ArchiveFS: - e := e.(*vfs.ArchiveFS) - return model.ArchiveFs{ - Name: e.Name(), - Size: e.Size(), - } - case *vfs.ResolverFS: - e := e.(*vfs.ResolverFS) - return model.ResolverFs{ - Name: e.Name(), - } - case *vfs.TorrentFs: - e := e.(*vfs.TorrentFs) - return model.TorrentFs{ - Name: e.Name(), - Torrent: model.MapTorrent(e.Torrent), - } - default: - if e.IsDir() { - return model.Dir{ - Name: e.Name(), - } - } - if de, ok := e.(fs.DirEntry); ok { - info, _ := de.Info() - return model.File{ - Name: e.Name(), - Size: info.Size(), - } - } - - if fe, ok := e.(fs.FileInfo); ok { - return model.File{ - Name: fe.Name(), - Size: fe.Size(), - } - } - } - - panic("this dir entry is strange af") -} - -// FsListDir is the resolver for the fsListDir field. -func (r *queryResolver) FsListDir(ctx context.Context, path string) (*model.ListDirResponse, error) { - root, err := r.VFS.Stat(ctx, path) +// FsEntry is the resolver for the fsEntry field. +func (r *queryResolver) FsEntry(ctx context.Context, path string) (model.FsEntry, error) { + entry, err := r.VFS.Stat(ctx, path) if err != nil { return nil, err } - entries, err := r.VFS.ReadDir(ctx, path) - if err != nil { - return nil, err - } - out := []model.DirEntry{} - for _, e := range entries { - out = append(out, fillDirEntry(e)) - } - - return &model.ListDirResponse{ - Root: fillDirEntry(root), - Entries: out, - }, nil + return model.FillFsEntry(entry, r.VFS, path), nil } // Query returns graph.QueryResolver implementation. diff --git a/src/delivery/graphql/resolver/subscription.resolvers.go b/src/delivery/graphql/resolver/subscription.resolvers.go index f30af50..7011caf 100644 --- a/src/delivery/graphql/resolver/subscription.resolvers.go +++ b/src/delivery/graphql/resolver/subscription.resolvers.go @@ -2,7 +2,7 @@ package resolver // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.43 +// Code generated by github.com/99designs/gqlgen version v0.17.45 import ( "context" diff --git a/src/delivery/graphql/resolver/torrent.resolvers.go b/src/delivery/graphql/resolver/torrent.resolvers.go index ef231ad..dab9b3b 100644 --- a/src/delivery/graphql/resolver/torrent.resolvers.go +++ b/src/delivery/graphql/resolver/torrent.resolvers.go @@ -2,7 +2,7 @@ package resolver // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.43 +// Code generated by github.com/99designs/gqlgen version v0.17.45 import ( "context" diff --git a/src/delivery/http.go b/src/delivery/http.go index 92dbc55..52f7ff4 100644 --- a/src/delivery/http.go +++ b/src/delivery/http.go @@ -5,84 +5,84 @@ import ( "log/slog" "net/http" - "git.kmsign.ru/royalcat/tstor" + "git.kmsign.ru/royalcat/tstor/pkg/rlog" "git.kmsign.ru/royalcat/tstor/src/config" "git.kmsign.ru/royalcat/tstor/src/host/service" "git.kmsign.ru/royalcat/tstor/src/host/vfs" "github.com/anacrolix/missinggo/v2/filecache" - "github.com/gin-contrib/pprof" - "github.com/gin-gonic/gin" - "github.com/shurcooL/httpfs/html/vfstemplate" + echopprof "github.com/labstack/echo-contrib/pprof" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" ) func New(fc *filecache.Cache, ss *service.Stats, s *service.Service, vfs vfs.Filesystem, logPath string, cfg *config.Settings) error { log := slog.With() - gin.SetMode(gin.ReleaseMode) - r := gin.New() - r.Use(gin.Recovery()) - r.Use(gin.ErrorLogger()) - r.Use(Logger()) - pprof.Register(r) + r := echo.New() + r.Use( + middleware.Recover(), + middleware.Gzip(), + middleware.Decompress(), + Logger(), + ) - r.GET("/assets/*filepath", func(c *gin.Context) { - c.FileFromFS(c.Request.URL.Path, http.FS(tstor.Assets)) - }) + echopprof.Register(r) - t, err := vfstemplate.ParseGlob(http.FS(tstor.Templates), nil, "/templates/*") - if err != nil { - return fmt.Errorf("error parsing html: %w", err) - } + // r.GET("/assets/*filepath", func(c *echo.Context) { + // c.FileFromFS(c.Request.URL.Path, http.FS(tstor.Assets)) + // }) - r.SetHTMLTemplate(t) + // t, err := vfstemplate.ParseGlob(http.FS(tstor.Templates), nil, "/templates/*") + // if err != nil { + // return fmt.Errorf("error parsing html: %w", err) + // } - r.GET("/", indexHandler) - // r.GET("/routes", routesHandler(ss)) - r.GET("/logs", logsHandler) - r.GET("/servers", serversFoldersHandler()) - r.Any("/graphql", gin.WrapH(GraphQLHandler(s, vfs))) + // r.SetHTMLTemplate(t) - api := r.Group("/api") - { - api.GET("/log", apiLogHandler(logPath)) - api.GET("/status", apiStatusHandler(fc, ss)) - // api.GET("/servers", apiServersHandler(tss)) - // api.GET("/routes", apiRoutesHandler(ss)) - // api.POST("/routes/:route/torrent", apiAddTorrentHandler(s)) - // api.DELETE("/routes/:route/torrent/:torrent_hash", apiDelTorrentHandler(s)) - } + // r.GET("/", indexHandler) + // // r.GET("/routes", routesHandler(ss)) + // r.GET("/logs", logsHandler) + // r.GET("/servers", serversFoldersHandler()) + r.Any("/graphql", echo.WrapHandler((GraphQLHandler(s, vfs)))) + + // api := r.Group("/api") + // { + // api.GET("/log", apiLogHandler(logPath)) + // api.GET("/status", apiStatusHandler(fc, ss)) + // // api.GET("/servers", apiServersHandler(tss)) + // // api.GET("/routes", apiRoutesHandler(ss)) + // // api.POST("/routes/:route/torrent", apiAddTorrentHandler(s)) + // // api.DELETE("/routes/:route/torrent/:torrent_hash", apiDelTorrentHandler(s)) + // } log.Info("starting webserver", "host", fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)) - if err := r.Run(fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)); err != nil { - return fmt.Errorf("error initializing server: %w", err) - } + // if err := r.Run(fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port)); err != nil { + // return fmt.Errorf("error initializing server: %w", err) + // } + + go r.Start((fmt.Sprintf("%s:%d", cfg.WebUi.IP, cfg.WebUi.Port))) return nil } -func Logger() gin.HandlerFunc { - l := slog.With("component", "http") - return func(c *gin.Context) { - path := c.Request.URL.Path - raw := c.Request.URL.RawQuery - c.Next() - if raw != "" { - path = path + "?" + raw - } - msg := c.Errors.String() - if msg == "" { - msg = "Request" - } - - s := c.Writer.Status() - switch { - case s >= 400 && s < 500: - l.Warn(msg, "path", path, "status", s) - case s >= 500: - l.Error(msg, "path", path, "status", s) - default: - l.Debug(msg, "path", path, "status", s) - } - } +func Logger() echo.MiddlewareFunc { + l := rlog.Component("http") + return middleware.BodyDumpWithConfig(middleware.BodyDumpConfig{ + Skipper: func(c echo.Context) bool { + return c.Request().Method == http.MethodGet + }, + Handler: func(c echo.Context, reqBody, resBody []byte) { + log := l.With( + slog.String("method", c.Request().Method), + slog.String("uri", c.Request().RequestURI), + ) + if c.Request().Header.Get("Content-Type") == "application/json" { + log.Info(c.Request().Context(), "Request body", slog.String("body", string(reqBody))) + } + if c.Response().Header().Get("Content-Type") == "application/json" { + log.Info(c.Request().Context(), "Response body", slog.String("body", string(resBody))) + } + }, + }) } diff --git a/src/delivery/router.go b/src/delivery/router.go index e9e108f..be5f72f 100644 --- a/src/delivery/router.go +++ b/src/delivery/router.go @@ -1,8 +1,11 @@ package delivery import ( + "context" + "log/slog" "net/http" + "git.kmsign.ru/royalcat/tstor/pkg/rlog" graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql" "git.kmsign.ru/royalcat/tstor/src/delivery/graphql/resolver" "git.kmsign.ru/royalcat/tstor/src/host/service" @@ -27,6 +30,15 @@ func GraphQLHandler(service *service.Service, vfs vfs.Filesystem) http.Handler { ), ) + log := rlog.Component("graphql") + + graphqlHandler.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { + resp := next(ctx) + responseJson, _ := resp.Data.MarshalJSON() + log.Info(ctx, "response", slog.String("body", string(responseJson))) + return resp + }) + graphqlHandler.AddTransport(&transport.POST{}) graphqlHandler.AddTransport(&transport.Websocket{}) graphqlHandler.AddTransport(&transport.SSE{}) @@ -39,6 +51,5 @@ func GraphQLHandler(service *service.Service, vfs vfs.Filesystem) http.Handler { return ctx.Field.Directives.ForName("link") != nil }), )) - return graphqlHandler } diff --git a/src/host/service/queue.go b/src/host/service/queue.go index a67f286..16eb4ae 100644 --- a/src/host/service/queue.go +++ b/src/host/service/queue.go @@ -36,10 +36,12 @@ func (s *Service) Download(ctx context.Context, task *TorrentDownloadTask) error } file.Download() - return nil + } else { + for _, file := range t.Files() { + file.Download() + } } - t.DownloadAll() return nil } @@ -111,15 +113,19 @@ func (s *Service) DownloadProgress(ctx context.Context) (<-chan TorrentProgress, defer close(out) for _, t := range torrents { sub := t.Torrent().SubscribePieceStateChanges() - go func() { - for range sub.Values { + go func(t *controller.Torrent) { + for stateChange := range sub.Values { + if !stateChange.Complete && !stateChange.Partial { + continue + } + out <- TorrentProgress{ Torrent: t, Current: t.BytesCompleted(), Total: t.Length(), } } - }() + }(t) defer sub.Close() } diff --git a/src/host/vfs/resolver.go b/src/host/vfs/resolver.go index c81a37b..fa8dd39 100644 --- a/src/host/vfs/resolver.go +++ b/src/host/vfs/resolver.go @@ -342,7 +342,7 @@ func getFile[F File](m map[string]F, name string) (File, error) { func listDirFromFiles[F File](m map[string]F, name string) ([]fs.DirEntry, error) { out := make([]fs.DirEntry, 0, len(m)) - name = AddTrailSlash(name) + name = AddTrailSlash(path.Clean(name)) for p, f := range m { if strings.HasPrefix(p, name) { parts := strings.Split(trimRelPath(p, name), Separator) diff --git a/src/host/vfs/torrent.go b/src/host/vfs/torrent.go index dd75760..1f76470 100644 --- a/src/host/vfs/torrent.go +++ b/src/host/vfs/torrent.go @@ -19,7 +19,7 @@ import ( "golang.org/x/exp/maps" ) -type TorrentFs struct { +type TorrentFS struct { name string mu sync.Mutex @@ -32,64 +32,64 @@ type TorrentFs struct { resolver *resolver } -var _ Filesystem = (*TorrentFs)(nil) +var _ Filesystem = (*TorrentFS)(nil) -func NewTorrentFs(name string, c *controller.Torrent) *TorrentFs { - return &TorrentFs{ +func NewTorrentFs(name string, c *controller.Torrent) *TorrentFS { + return &TorrentFS{ name: name, Torrent: c, resolver: newResolver(ArchiveFactories), } } -var _ fs.DirEntry = (*TorrentFs)(nil) +var _ fs.DirEntry = (*TorrentFS)(nil) // Name implements fs.DirEntry. -func (tfs *TorrentFs) Name() string { +func (tfs *TorrentFS) Name() string { return tfs.name } // Info implements fs.DirEntry. -func (tfs *TorrentFs) Info() (fs.FileInfo, error) { +func (tfs *TorrentFS) Info() (fs.FileInfo, error) { return tfs, nil } // IsDir implements fs.DirEntry. -func (tfs *TorrentFs) IsDir() bool { +func (tfs *TorrentFS) IsDir() bool { return true } // Type implements fs.DirEntry. -func (tfs *TorrentFs) Type() fs.FileMode { +func (tfs *TorrentFS) Type() fs.FileMode { return fs.ModeDir } // ModTime implements fs.FileInfo. -func (tfs *TorrentFs) ModTime() time.Time { +func (tfs *TorrentFS) ModTime() time.Time { return time.Time{} } // Mode implements fs.FileInfo. -func (tfs *TorrentFs) Mode() fs.FileMode { +func (tfs *TorrentFS) Mode() fs.FileMode { return fs.ModeDir } // Size implements fs.FileInfo. -func (tfs *TorrentFs) Size() int64 { +func (tfs *TorrentFS) Size() int64 { return 0 } // Sys implements fs.FileInfo. -func (tfs *TorrentFs) Sys() any { +func (tfs *TorrentFS) Sys() any { return nil } // FsName implements Filesystem. -func (tfs *TorrentFs) FsName() string { +func (tfs *TorrentFS) FsName() string { return "torrentfs" } -func (fs *TorrentFs) files(ctx context.Context) (map[string]File, error) { +func (fs *TorrentFS) files(ctx context.Context) (map[string]File, error) { fs.mu.Lock() defer fs.mu.Unlock() @@ -175,7 +175,7 @@ DEFAULT_DIR: // return true // } -func (fs *TorrentFs) listFilesRecursive(ctx context.Context, vfs Filesystem, start string) (map[string]File, error) { +func (fs *TorrentFS) listFilesRecursive(ctx context.Context, vfs Filesystem, start string) (map[string]File, error) { ctx, span := tracer.Start(ctx, "listFilesRecursive", fs.traceAttrs(attribute.String("start", start)), ) @@ -206,7 +206,7 @@ func (fs *TorrentFs) listFilesRecursive(ctx context.Context, vfs Filesystem, sta return out, nil } -func (fs *TorrentFs) rawOpen(ctx context.Context, filename string) (file File, err error) { +func (fs *TorrentFS) rawOpen(ctx context.Context, filename string) (file File, err error) { ctx, span := tracer.Start(ctx, "rawOpen", fs.traceAttrs(attribute.String("filename", filename)), ) @@ -225,7 +225,7 @@ func (fs *TorrentFs) rawOpen(ctx context.Context, filename string) (file File, e return file, err } -func (fs *TorrentFs) rawStat(ctx context.Context, filename string) (fs.FileInfo, error) { +func (fs *TorrentFS) rawStat(ctx context.Context, filename string) (fs.FileInfo, error) { ctx, span := tracer.Start(ctx, "rawStat", fs.traceAttrs(attribute.String("filename", filename)), ) @@ -243,7 +243,7 @@ func (fs *TorrentFs) rawStat(ctx context.Context, filename string) (fs.FileInfo, return file.Info() } -func (fs *TorrentFs) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption { +func (fs *TorrentFS) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption { return trace.WithAttributes(append([]attribute.KeyValue{ attribute.String("fs", fs.FsName()), attribute.String("torrent", fs.Torrent.Name()), @@ -252,7 +252,7 @@ func (fs *TorrentFs) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption } // Stat implements Filesystem. -func (tfs *TorrentFs) Stat(ctx context.Context, filename string) (fs.FileInfo, error) { +func (tfs *TorrentFS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) { ctx, span := tracer.Start(ctx, "Stat", tfs.traceAttrs(attribute.String("filename", filename)), ) @@ -287,7 +287,7 @@ func (tfs *TorrentFs) Stat(ctx context.Context, filename string) (fs.FileInfo, e return tfs.rawStat(ctx, fsPath) } -func (tfs *TorrentFs) Open(ctx context.Context, filename string) (file File, err error) { +func (tfs *TorrentFS) Open(ctx context.Context, filename string) (file File, err error) { ctx, span := tracer.Start(ctx, "Open", tfs.traceAttrs(attribute.String("filename", filename)), ) @@ -322,7 +322,7 @@ func (tfs *TorrentFs) Open(ctx context.Context, filename string) (file File, err return tfs.rawOpen(ctx, fsPath) } -func (tfs *TorrentFs) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) { +func (tfs *TorrentFS) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, error) { ctx, span := tracer.Start(ctx, "ReadDir", tfs.traceAttrs(attribute.String("name", name)), ) @@ -357,7 +357,7 @@ func (tfs *TorrentFs) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, return listDirFromFiles(files, fsPath) } -func (fs *TorrentFs) Unlink(ctx context.Context, name string) error { +func (fs *TorrentFS) Unlink(ctx context.Context, name string) error { ctx, span := tracer.Start(ctx, "Unlink", fs.traceAttrs(attribute.String("name", name)), ) diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/ui/.graphqlrc.yaml b/ui/.graphqlrc.yaml new file mode 100644 index 0000000..9be136c --- /dev/null +++ b/ui/.graphqlrc.yaml @@ -0,0 +1,5 @@ +schema: + - ../graphql/*.graphql + - ../graphql/**/*.graphql +documents: + - lib/** diff --git a/ui/.metadata b/ui/.metadata new file mode 100644 index 0000000..0ad631d --- /dev/null +++ b/ui/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "29babcb32a591b9e5be8c6a6075d4fe605d46ad3" + channel: "beta" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: android + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: ios + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: linux + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: macos + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: web + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + - platform: windows + create_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + base_revision: 29babcb32a591b9e5be8c6a6075d4fe605d46ad3 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000..50b211a --- /dev/null +++ b/ui/README.md @@ -0,0 +1,16 @@ +# tstor_ui + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/ui/analysis_options.yaml b/ui/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/ui/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/ui/android/.gitignore b/ui/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/ui/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/ui/android/app/build.gradle b/ui/android/app/build.gradle new file mode 100644 index 0000000..76623b8 --- /dev/null +++ b/ui/android/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.example.tstor_ui" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.tstor_ui" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/ui/android/app/src/debug/AndroidManifest.xml b/ui/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/ui/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/ui/android/app/src/main/AndroidManifest.xml b/ui/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..21f7b9e --- /dev/null +++ b/ui/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/android/app/src/main/kotlin/com/example/tstor_ui/MainActivity.kt b/ui/android/app/src/main/kotlin/com/example/tstor_ui/MainActivity.kt new file mode 100644 index 0000000..a697f08 --- /dev/null +++ b/ui/android/app/src/main/kotlin/com/example/tstor_ui/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.tstor_ui + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/ui/android/app/src/main/res/drawable-v21/launch_background.xml b/ui/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/ui/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/ui/android/app/src/main/res/drawable/launch_background.xml b/ui/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/ui/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/ui/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/ui/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/ui/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/ui/android/app/src/main/res/values-night/styles.xml b/ui/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/ui/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/ui/android/app/src/main/res/values/styles.xml b/ui/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/ui/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/ui/android/app/src/profile/AndroidManifest.xml b/ui/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/ui/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/ui/android/build.gradle b/ui/android/build.gradle new file mode 100644 index 0000000..d2ffbff --- /dev/null +++ b/ui/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/ui/android/gradle.properties b/ui/android/gradle.properties new file mode 100644 index 0000000..3b5b324 --- /dev/null +++ b/ui/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/ui/android/gradle/wrapper/gradle-wrapper.properties b/ui/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/ui/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/ui/android/settings.gradle b/ui/android/settings.gradle new file mode 100644 index 0000000..536165d --- /dev/null +++ b/ui/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/ui/build.yaml b/ui/build.yaml new file mode 100644 index 0000000..136c524 --- /dev/null +++ b/ui/build.yaml @@ -0,0 +1,15 @@ +targets: + $default: + builders: + graphql_codegen: + options: + scalars: + URL: + type: String + ID: + type: String + DateTime: + type: DateTime + clients: + - graphql + - graphql_flutter diff --git a/ui/devtools_options.yaml b/ui/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/ui/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/ui/fonts/TIcons.ttf b/ui/fonts/TIcons.ttf new file mode 100644 index 0000000..81543a7 Binary files /dev/null and b/ui/fonts/TIcons.ttf differ diff --git a/ui/ios/.gitignore b/ui/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ui/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ui/ios/Flutter/AppFrameworkInfo.plist b/ui/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ui/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ui/ios/Flutter/Debug.xcconfig b/ui/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ui/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ui/ios/Flutter/Release.xcconfig b/ui/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ui/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ui/ios/Runner.xcodeproj/project.pbxproj b/ui/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..de1d473 --- /dev/null +++ b/ui/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ui/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ui/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ui/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ui/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ui/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ui/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..8e3ca5d --- /dev/null +++ b/ui/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/ios/Runner.xcworkspace/contents.xcworkspacedata b/ui/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ui/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ui/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ui/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ui/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ui/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ui/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ui/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ui/ios/Runner/AppDelegate.swift b/ui/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..9074fee --- /dev/null +++ b/ui/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..7353c41 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..6ed2d93 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cd7b00 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..fe73094 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..321773c Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..502f463 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..e9f5fea Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..84ac32a Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..8953cba Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..0467bf1 Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ui/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ui/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ui/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ui/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/ios/Runner/Base.lproj/Main.storyboard b/ui/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ui/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/ios/Runner/Info.plist b/ui/ios/Runner/Info.plist new file mode 100644 index 0000000..d6f3cb1 --- /dev/null +++ b/ui/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Tstor Ui + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + tstor_ui + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/ui/ios/Runner/Runner-Bridging-Header.h b/ui/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ui/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ui/ios/RunnerTests/RunnerTests.swift b/ui/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ui/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/ui/lib/api/client.dart b/ui/lib/api/client.dart new file mode 100644 index 0000000..d1d92e4 --- /dev/null +++ b/ui/lib/api/client.dart @@ -0,0 +1,47 @@ +import 'package:flutter/foundation.dart'; +import 'package:graphql/client.dart'; + +final client = GraphQLClient( + link: _loggerLink.concat(HttpLink("http://localhost:4444/graphql")), + cache: GraphQLCache(store: null), + defaultPolicies: DefaultPolicies( + query: Policies( + fetch: FetchPolicy.noCache, + ), + ), +); + +// final client = GraphQLClient( +// link: HttpLink("http://192.168.217.150:4444/graphql"), +// cache: GraphQLCache(store: null), +// defaultPolicies: DefaultPolicies( +// query: Policies( +// fetch: FetchPolicy.noCache, +// ), +// ), +// ); + +class LoggerLink extends Link { + @override + Stream request( + Request request, [ + NextLink? forward, + ]) { + Stream response = forward!(request).map((Response fetchResult) { + final ioStreamedResponse = fetchResult.context.entry(); + if (kDebugMode) { + print("Request: ${request.toString()}"); + print("Response:${ioStreamedResponse?.toString() ?? "null"}"); + } + return fetchResult; + }).handleError((error) { + throw error; + }); + + return response; + } + + LoggerLink(); +} + +final _loggerLink = LoggerLink(); diff --git a/ui/lib/api/fs_entry.graphql b/ui/lib/api/fs_entry.graphql new file mode 100644 index 0000000..7b8e376 --- /dev/null +++ b/ui/lib/api/fs_entry.graphql @@ -0,0 +1,45 @@ +fragment File on File { + name + size +} + +fragment TorrentDir on TorrentFS { + name + torrent { + name + infohash + bytesCompleted + torrentFilePath + bytesMissing + } +} + + +fragment ArchiveDir on ArchiveFS { + name + size +} + +fragment DirEntry on FsEntry { + name + + ...TorrentDir + ...ArchiveDir + ...File +} + + +query ListDir($path: String!) { + fsEntry(path: $path) { + name + + ... on Dir { + entries { + ...DirEntry + } + } + + ...TorrentDir + ...ArchiveDir + } +} \ No newline at end of file diff --git a/ui/lib/api/fs_entry.graphql.dart b/ui/lib/api/fs_entry.graphql.dart new file mode 100644 index 0000000..8b047a6 --- /dev/null +++ b/ui/lib/api/fs_entry.graphql.dart @@ -0,0 +1,4627 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + +class Fragment$File { + Fragment$File({ + required this.name, + required this.size, + required this.$__typename, + }); + + factory Fragment$File.fromJson(Map json) { + switch (json["__typename"] as String) { + case "SimpleFile": + return Fragment$File$$SimpleFile.fromJson(json); + + case "TorrentFileEntry": + return Fragment$File$$TorrentFileEntry.fromJson(json); + + default: + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$File( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + } + + final String name; + + final int size; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$File || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$File on Fragment$File { + CopyWith$Fragment$File get copyWith => CopyWith$Fragment$File( + this, + (i) => i, + ); + _T when<_T>({ + required _T Function(Fragment$File$$SimpleFile) simpleFile, + required _T Function(Fragment$File$$TorrentFileEntry) torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "SimpleFile": + return simpleFile(this as Fragment$File$$SimpleFile); + + case "TorrentFileEntry": + return torrentFileEntry(this as Fragment$File$$TorrentFileEntry); + + default: + return orElse(); + } + } + + _T maybeWhen<_T>({ + _T Function(Fragment$File$$SimpleFile)? simpleFile, + _T Function(Fragment$File$$TorrentFileEntry)? torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "SimpleFile": + if (simpleFile != null) { + return simpleFile(this as Fragment$File$$SimpleFile); + } else { + return orElse(); + } + + case "TorrentFileEntry": + if (torrentFileEntry != null) { + return torrentFileEntry(this as Fragment$File$$TorrentFileEntry); + } else { + return orElse(); + } + + default: + return orElse(); + } + } +} + +abstract class CopyWith$Fragment$File { + factory CopyWith$Fragment$File( + Fragment$File instance, + TRes Function(Fragment$File) then, + ) = _CopyWithImpl$Fragment$File; + + factory CopyWith$Fragment$File.stub(TRes res) = + _CopyWithStubImpl$Fragment$File; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$File + implements CopyWith$Fragment$File { + _CopyWithImpl$Fragment$File( + this._instance, + this._then, + ); + + final Fragment$File _instance; + + final TRes Function(Fragment$File) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$File( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$File + implements CopyWith$Fragment$File { + _CopyWithStubImpl$Fragment$File(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionFile = FragmentDefinitionNode( + name: NameNode(value: 'File'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'File'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'size'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentFile = DocumentNode(definitions: [ + fragmentDefinitionFile, +]); + +extension ClientExtension$Fragment$File on graphql.GraphQLClient { + void writeFragment$File({ + required Fragment$File data, + required Map idFields, + bool broadcast = true, + }) => + writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'File', + document: documentNodeFragmentFile, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$File? readFragment$File({ + required Map idFields, + bool optimistic = true, + }) { + final result = readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'File', + document: documentNodeFragmentFile, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$File.fromJson(result); + } +} + +class Fragment$File$$SimpleFile implements Fragment$File { + Fragment$File$$SimpleFile({ + required this.name, + required this.size, + this.$__typename = 'SimpleFile', + }); + + factory Fragment$File$$SimpleFile.fromJson(Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$File$$SimpleFile( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final int size; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$File$$SimpleFile || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$File$$SimpleFile + on Fragment$File$$SimpleFile { + CopyWith$Fragment$File$$SimpleFile get copyWith => + CopyWith$Fragment$File$$SimpleFile( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$File$$SimpleFile { + factory CopyWith$Fragment$File$$SimpleFile( + Fragment$File$$SimpleFile instance, + TRes Function(Fragment$File$$SimpleFile) then, + ) = _CopyWithImpl$Fragment$File$$SimpleFile; + + factory CopyWith$Fragment$File$$SimpleFile.stub(TRes res) = + _CopyWithStubImpl$Fragment$File$$SimpleFile; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$File$$SimpleFile + implements CopyWith$Fragment$File$$SimpleFile { + _CopyWithImpl$Fragment$File$$SimpleFile( + this._instance, + this._then, + ); + + final Fragment$File$$SimpleFile _instance; + + final TRes Function(Fragment$File$$SimpleFile) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$File$$SimpleFile( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$File$$SimpleFile + implements CopyWith$Fragment$File$$SimpleFile { + _CopyWithStubImpl$Fragment$File$$SimpleFile(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +class Fragment$File$$TorrentFileEntry implements Fragment$File { + Fragment$File$$TorrentFileEntry({ + required this.name, + required this.size, + this.$__typename = 'TorrentFileEntry', + }); + + factory Fragment$File$$TorrentFileEntry.fromJson(Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$File$$TorrentFileEntry( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final int size; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$File$$TorrentFileEntry || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$File$$TorrentFileEntry + on Fragment$File$$TorrentFileEntry { + CopyWith$Fragment$File$$TorrentFileEntry + get copyWith => CopyWith$Fragment$File$$TorrentFileEntry( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$File$$TorrentFileEntry { + factory CopyWith$Fragment$File$$TorrentFileEntry( + Fragment$File$$TorrentFileEntry instance, + TRes Function(Fragment$File$$TorrentFileEntry) then, + ) = _CopyWithImpl$Fragment$File$$TorrentFileEntry; + + factory CopyWith$Fragment$File$$TorrentFileEntry.stub(TRes res) = + _CopyWithStubImpl$Fragment$File$$TorrentFileEntry; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$File$$TorrentFileEntry + implements CopyWith$Fragment$File$$TorrentFileEntry { + _CopyWithImpl$Fragment$File$$TorrentFileEntry( + this._instance, + this._then, + ); + + final Fragment$File$$TorrentFileEntry _instance; + + final TRes Function(Fragment$File$$TorrentFileEntry) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$File$$TorrentFileEntry( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$File$$TorrentFileEntry + implements CopyWith$Fragment$File$$TorrentFileEntry { + _CopyWithStubImpl$Fragment$File$$TorrentFileEntry(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +class Fragment$TorrentDir { + Fragment$TorrentDir({ + required this.name, + required this.torrent, + this.$__typename = 'TorrentFS', + }); + + factory Fragment$TorrentDir.fromJson(Map json) { + final l$name = json['name']; + final l$torrent = json['torrent']; + final l$$__typename = json['__typename']; + return Fragment$TorrentDir( + name: (l$name as String), + torrent: Fragment$TorrentDir$torrent.fromJson( + (l$torrent as Map)), + $__typename: (l$$__typename as String), + ); + } + + final String name; + + final Fragment$TorrentDir$torrent torrent; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$torrent = torrent; + resultData['torrent'] = l$torrent.toJson(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$torrent = torrent; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$torrent, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$TorrentDir || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$torrent = torrent; + final lOther$torrent = other.torrent; + if (l$torrent != lOther$torrent) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$TorrentDir on Fragment$TorrentDir { + CopyWith$Fragment$TorrentDir get copyWith => + CopyWith$Fragment$TorrentDir( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$TorrentDir { + factory CopyWith$Fragment$TorrentDir( + Fragment$TorrentDir instance, + TRes Function(Fragment$TorrentDir) then, + ) = _CopyWithImpl$Fragment$TorrentDir; + + factory CopyWith$Fragment$TorrentDir.stub(TRes res) = + _CopyWithStubImpl$Fragment$TorrentDir; + + TRes call({ + String? name, + Fragment$TorrentDir$torrent? torrent, + String? $__typename, + }); + CopyWith$Fragment$TorrentDir$torrent get torrent; +} + +class _CopyWithImpl$Fragment$TorrentDir + implements CopyWith$Fragment$TorrentDir { + _CopyWithImpl$Fragment$TorrentDir( + this._instance, + this._then, + ); + + final Fragment$TorrentDir _instance; + + final TRes Function(Fragment$TorrentDir) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? torrent = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$TorrentDir( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + torrent: torrent == _undefined || torrent == null + ? _instance.torrent + : (torrent as Fragment$TorrentDir$torrent), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + CopyWith$Fragment$TorrentDir$torrent get torrent { + final local$torrent = _instance.torrent; + return CopyWith$Fragment$TorrentDir$torrent( + local$torrent, (e) => call(torrent: e)); + } +} + +class _CopyWithStubImpl$Fragment$TorrentDir + implements CopyWith$Fragment$TorrentDir { + _CopyWithStubImpl$Fragment$TorrentDir(this._res); + + final TRes _res; + + @override + call({ + String? name, + Fragment$TorrentDir$torrent? torrent, + String? $__typename, + }) => + _res; + + @override + CopyWith$Fragment$TorrentDir$torrent get torrent => + CopyWith$Fragment$TorrentDir$torrent.stub(_res); +} + +const fragmentDefinitionTorrentDir = FragmentDefinitionNode( + name: NameNode(value: 'TorrentDir'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'TorrentFS'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'torrent'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'infohash'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'bytesCompleted'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'torrentFilePath'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'bytesMissing'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentTorrentDir = DocumentNode(definitions: [ + fragmentDefinitionTorrentDir, +]); + +extension ClientExtension$Fragment$TorrentDir on graphql.GraphQLClient { + void writeFragment$TorrentDir({ + required Fragment$TorrentDir data, + required Map idFields, + bool broadcast = true, + }) => + writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'TorrentDir', + document: documentNodeFragmentTorrentDir, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$TorrentDir? readFragment$TorrentDir({ + required Map idFields, + bool optimistic = true, + }) { + final result = readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'TorrentDir', + document: documentNodeFragmentTorrentDir, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$TorrentDir.fromJson(result); + } +} + +class Fragment$TorrentDir$torrent { + Fragment$TorrentDir$torrent({ + required this.name, + required this.infohash, + required this.bytesCompleted, + required this.torrentFilePath, + required this.bytesMissing, + this.$__typename = 'Torrent', + }); + + factory Fragment$TorrentDir$torrent.fromJson(Map json) { + final l$name = json['name']; + final l$infohash = json['infohash']; + final l$bytesCompleted = json['bytesCompleted']; + final l$torrentFilePath = json['torrentFilePath']; + final l$bytesMissing = json['bytesMissing']; + final l$$__typename = json['__typename']; + return Fragment$TorrentDir$torrent( + name: (l$name as String), + infohash: (l$infohash as String), + bytesCompleted: (l$bytesCompleted as int), + torrentFilePath: (l$torrentFilePath as String), + bytesMissing: (l$bytesMissing as int), + $__typename: (l$$__typename as String), + ); + } + + final String name; + + final String infohash; + + final int bytesCompleted; + + final String torrentFilePath; + + final int bytesMissing; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$infohash = infohash; + resultData['infohash'] = l$infohash; + final l$bytesCompleted = bytesCompleted; + resultData['bytesCompleted'] = l$bytesCompleted; + final l$torrentFilePath = torrentFilePath; + resultData['torrentFilePath'] = l$torrentFilePath; + final l$bytesMissing = bytesMissing; + resultData['bytesMissing'] = l$bytesMissing; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$infohash = infohash; + final l$bytesCompleted = bytesCompleted; + final l$torrentFilePath = torrentFilePath; + final l$bytesMissing = bytesMissing; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$infohash, + l$bytesCompleted, + l$torrentFilePath, + l$bytesMissing, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$TorrentDir$torrent || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (l$infohash != lOther$infohash) { + return false; + } + final l$bytesCompleted = bytesCompleted; + final lOther$bytesCompleted = other.bytesCompleted; + if (l$bytesCompleted != lOther$bytesCompleted) { + return false; + } + final l$torrentFilePath = torrentFilePath; + final lOther$torrentFilePath = other.torrentFilePath; + if (l$torrentFilePath != lOther$torrentFilePath) { + return false; + } + final l$bytesMissing = bytesMissing; + final lOther$bytesMissing = other.bytesMissing; + if (l$bytesMissing != lOther$bytesMissing) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$TorrentDir$torrent + on Fragment$TorrentDir$torrent { + CopyWith$Fragment$TorrentDir$torrent + get copyWith => CopyWith$Fragment$TorrentDir$torrent( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$TorrentDir$torrent { + factory CopyWith$Fragment$TorrentDir$torrent( + Fragment$TorrentDir$torrent instance, + TRes Function(Fragment$TorrentDir$torrent) then, + ) = _CopyWithImpl$Fragment$TorrentDir$torrent; + + factory CopyWith$Fragment$TorrentDir$torrent.stub(TRes res) = + _CopyWithStubImpl$Fragment$TorrentDir$torrent; + + TRes call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$TorrentDir$torrent + implements CopyWith$Fragment$TorrentDir$torrent { + _CopyWithImpl$Fragment$TorrentDir$torrent( + this._instance, + this._then, + ); + + final Fragment$TorrentDir$torrent _instance; + + final TRes Function(Fragment$TorrentDir$torrent) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? infohash = _undefined, + Object? bytesCompleted = _undefined, + Object? torrentFilePath = _undefined, + Object? bytesMissing = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$TorrentDir$torrent( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + infohash: infohash == _undefined || infohash == null + ? _instance.infohash + : (infohash as String), + bytesCompleted: bytesCompleted == _undefined || bytesCompleted == null + ? _instance.bytesCompleted + : (bytesCompleted as int), + torrentFilePath: + torrentFilePath == _undefined || torrentFilePath == null + ? _instance.torrentFilePath + : (torrentFilePath as String), + bytesMissing: bytesMissing == _undefined || bytesMissing == null + ? _instance.bytesMissing + : (bytesMissing as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$TorrentDir$torrent + implements CopyWith$Fragment$TorrentDir$torrent { + _CopyWithStubImpl$Fragment$TorrentDir$torrent(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }) => + _res; +} + +class Fragment$ArchiveDir { + Fragment$ArchiveDir({ + required this.name, + required this.size, + this.$__typename = 'ArchiveFS', + }); + + factory Fragment$ArchiveDir.fromJson(Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$ArchiveDir( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + final String name; + + final int size; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$ArchiveDir || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$ArchiveDir on Fragment$ArchiveDir { + CopyWith$Fragment$ArchiveDir get copyWith => + CopyWith$Fragment$ArchiveDir( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$ArchiveDir { + factory CopyWith$Fragment$ArchiveDir( + Fragment$ArchiveDir instance, + TRes Function(Fragment$ArchiveDir) then, + ) = _CopyWithImpl$Fragment$ArchiveDir; + + factory CopyWith$Fragment$ArchiveDir.stub(TRes res) = + _CopyWithStubImpl$Fragment$ArchiveDir; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$ArchiveDir + implements CopyWith$Fragment$ArchiveDir { + _CopyWithImpl$Fragment$ArchiveDir( + this._instance, + this._then, + ); + + final Fragment$ArchiveDir _instance; + + final TRes Function(Fragment$ArchiveDir) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$ArchiveDir( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$ArchiveDir + implements CopyWith$Fragment$ArchiveDir { + _CopyWithStubImpl$Fragment$ArchiveDir(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionArchiveDir = FragmentDefinitionNode( + name: NameNode(value: 'ArchiveDir'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'ArchiveFS'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'size'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentArchiveDir = DocumentNode(definitions: [ + fragmentDefinitionArchiveDir, +]); + +extension ClientExtension$Fragment$ArchiveDir on graphql.GraphQLClient { + void writeFragment$ArchiveDir({ + required Fragment$ArchiveDir data, + required Map idFields, + bool broadcast = true, + }) => + writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'ArchiveDir', + document: documentNodeFragmentArchiveDir, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$ArchiveDir? readFragment$ArchiveDir({ + required Map idFields, + bool optimistic = true, + }) { + final result = readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'ArchiveDir', + document: documentNodeFragmentArchiveDir, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$ArchiveDir.fromJson(result); + } +} + +class Fragment$DirEntry { + Fragment$DirEntry({ + required this.name, + required this.$__typename, + }); + + factory Fragment$DirEntry.fromJson(Map json) { + switch (json["__typename"] as String) { + case "ArchiveFS": + return Fragment$DirEntry$$ArchiveFS.fromJson(json); + + case "ResolverFS": + return Fragment$DirEntry$$ResolverFS.fromJson(json); + + case "SimpleDir": + return Fragment$DirEntry$$SimpleDir.fromJson(json); + + case "SimpleFile": + return Fragment$DirEntry$$SimpleFile.fromJson(json); + + case "TorrentFS": + return Fragment$DirEntry$$TorrentFS.fromJson(json); + + case "TorrentFileEntry": + return Fragment$DirEntry$$TorrentFileEntry.fromJson(json); + + default: + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + } + + final String name; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry on Fragment$DirEntry { + CopyWith$Fragment$DirEntry get copyWith => + CopyWith$Fragment$DirEntry( + this, + (i) => i, + ); + _T when<_T>({ + required _T Function(Fragment$DirEntry$$ArchiveFS) archiveFS, + required _T Function(Fragment$DirEntry$$ResolverFS) resolverFS, + required _T Function(Fragment$DirEntry$$SimpleDir) simpleDir, + required _T Function(Fragment$DirEntry$$SimpleFile) simpleFile, + required _T Function(Fragment$DirEntry$$TorrentFS) torrentFS, + required _T Function(Fragment$DirEntry$$TorrentFileEntry) torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ArchiveFS": + return archiveFS(this as Fragment$DirEntry$$ArchiveFS); + + case "ResolverFS": + return resolverFS(this as Fragment$DirEntry$$ResolverFS); + + case "SimpleDir": + return simpleDir(this as Fragment$DirEntry$$SimpleDir); + + case "SimpleFile": + return simpleFile(this as Fragment$DirEntry$$SimpleFile); + + case "TorrentFS": + return torrentFS(this as Fragment$DirEntry$$TorrentFS); + + case "TorrentFileEntry": + return torrentFileEntry(this as Fragment$DirEntry$$TorrentFileEntry); + + default: + return orElse(); + } + } + + _T maybeWhen<_T>({ + _T Function(Fragment$DirEntry$$ArchiveFS)? archiveFS, + _T Function(Fragment$DirEntry$$ResolverFS)? resolverFS, + _T Function(Fragment$DirEntry$$SimpleDir)? simpleDir, + _T Function(Fragment$DirEntry$$SimpleFile)? simpleFile, + _T Function(Fragment$DirEntry$$TorrentFS)? torrentFS, + _T Function(Fragment$DirEntry$$TorrentFileEntry)? torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ArchiveFS": + if (archiveFS != null) { + return archiveFS(this as Fragment$DirEntry$$ArchiveFS); + } else { + return orElse(); + } + + case "ResolverFS": + if (resolverFS != null) { + return resolverFS(this as Fragment$DirEntry$$ResolverFS); + } else { + return orElse(); + } + + case "SimpleDir": + if (simpleDir != null) { + return simpleDir(this as Fragment$DirEntry$$SimpleDir); + } else { + return orElse(); + } + + case "SimpleFile": + if (simpleFile != null) { + return simpleFile(this as Fragment$DirEntry$$SimpleFile); + } else { + return orElse(); + } + + case "TorrentFS": + if (torrentFS != null) { + return torrentFS(this as Fragment$DirEntry$$TorrentFS); + } else { + return orElse(); + } + + case "TorrentFileEntry": + if (torrentFileEntry != null) { + return torrentFileEntry(this as Fragment$DirEntry$$TorrentFileEntry); + } else { + return orElse(); + } + + default: + return orElse(); + } + } +} + +abstract class CopyWith$Fragment$DirEntry { + factory CopyWith$Fragment$DirEntry( + Fragment$DirEntry instance, + TRes Function(Fragment$DirEntry) then, + ) = _CopyWithImpl$Fragment$DirEntry; + + factory CopyWith$Fragment$DirEntry.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry + implements CopyWith$Fragment$DirEntry { + _CopyWithImpl$Fragment$DirEntry( + this._instance, + this._then, + ); + + final Fragment$DirEntry _instance; + + final TRes Function(Fragment$DirEntry) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry + implements CopyWith$Fragment$DirEntry { + _CopyWithStubImpl$Fragment$DirEntry(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionDirEntry = FragmentDefinitionNode( + name: NameNode(value: 'DirEntry'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'FsEntry'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FragmentSpreadNode( + name: NameNode(value: 'TorrentDir'), + directives: [], + ), + FragmentSpreadNode( + name: NameNode(value: 'ArchiveDir'), + directives: [], + ), + FragmentSpreadNode( + name: NameNode(value: 'File'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentDirEntry = DocumentNode(definitions: [ + fragmentDefinitionDirEntry, + fragmentDefinitionTorrentDir, + fragmentDefinitionArchiveDir, + fragmentDefinitionFile, +]); + +extension ClientExtension$Fragment$DirEntry on graphql.GraphQLClient { + void writeFragment$DirEntry({ + required Fragment$DirEntry data, + required Map idFields, + bool broadcast = true, + }) => + writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'DirEntry', + document: documentNodeFragmentDirEntry, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$DirEntry? readFragment$DirEntry({ + required Map idFields, + bool optimistic = true, + }) { + final result = readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'DirEntry', + document: documentNodeFragmentDirEntry, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$DirEntry.fromJson(result); + } +} + +class Fragment$DirEntry$$ArchiveFS + implements Fragment$ArchiveDir, Fragment$DirEntry { + Fragment$DirEntry$$ArchiveFS({ + required this.name, + required this.size, + this.$__typename = 'ArchiveFS', + }); + + factory Fragment$DirEntry$$ArchiveFS.fromJson(Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$ArchiveFS( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final int size; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$ArchiveFS || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$ArchiveFS + on Fragment$DirEntry$$ArchiveFS { + CopyWith$Fragment$DirEntry$$ArchiveFS + get copyWith => CopyWith$Fragment$DirEntry$$ArchiveFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$ArchiveFS { + factory CopyWith$Fragment$DirEntry$$ArchiveFS( + Fragment$DirEntry$$ArchiveFS instance, + TRes Function(Fragment$DirEntry$$ArchiveFS) then, + ) = _CopyWithImpl$Fragment$DirEntry$$ArchiveFS; + + factory CopyWith$Fragment$DirEntry$$ArchiveFS.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$ArchiveFS; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$ArchiveFS + implements CopyWith$Fragment$DirEntry$$ArchiveFS { + _CopyWithImpl$Fragment$DirEntry$$ArchiveFS( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$ArchiveFS _instance; + + final TRes Function(Fragment$DirEntry$$ArchiveFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$ArchiveFS( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$ArchiveFS + implements CopyWith$Fragment$DirEntry$$ArchiveFS { + _CopyWithStubImpl$Fragment$DirEntry$$ArchiveFS(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +class Fragment$DirEntry$$ResolverFS implements Fragment$DirEntry { + Fragment$DirEntry$$ResolverFS({ + required this.name, + this.$__typename = 'ResolverFS', + }); + + factory Fragment$DirEntry$$ResolverFS.fromJson(Map json) { + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$ResolverFS( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$ResolverFS || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$ResolverFS + on Fragment$DirEntry$$ResolverFS { + CopyWith$Fragment$DirEntry$$ResolverFS + get copyWith => CopyWith$Fragment$DirEntry$$ResolverFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$ResolverFS { + factory CopyWith$Fragment$DirEntry$$ResolverFS( + Fragment$DirEntry$$ResolverFS instance, + TRes Function(Fragment$DirEntry$$ResolverFS) then, + ) = _CopyWithImpl$Fragment$DirEntry$$ResolverFS; + + factory CopyWith$Fragment$DirEntry$$ResolverFS.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$ResolverFS; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$ResolverFS + implements CopyWith$Fragment$DirEntry$$ResolverFS { + _CopyWithImpl$Fragment$DirEntry$$ResolverFS( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$ResolverFS _instance; + + final TRes Function(Fragment$DirEntry$$ResolverFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$ResolverFS( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$ResolverFS + implements CopyWith$Fragment$DirEntry$$ResolverFS { + _CopyWithStubImpl$Fragment$DirEntry$$ResolverFS(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} + +class Fragment$DirEntry$$SimpleDir implements Fragment$DirEntry { + Fragment$DirEntry$$SimpleDir({ + required this.name, + this.$__typename = 'SimpleDir', + }); + + factory Fragment$DirEntry$$SimpleDir.fromJson(Map json) { + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$SimpleDir( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$SimpleDir || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$SimpleDir + on Fragment$DirEntry$$SimpleDir { + CopyWith$Fragment$DirEntry$$SimpleDir + get copyWith => CopyWith$Fragment$DirEntry$$SimpleDir( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$SimpleDir { + factory CopyWith$Fragment$DirEntry$$SimpleDir( + Fragment$DirEntry$$SimpleDir instance, + TRes Function(Fragment$DirEntry$$SimpleDir) then, + ) = _CopyWithImpl$Fragment$DirEntry$$SimpleDir; + + factory CopyWith$Fragment$DirEntry$$SimpleDir.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$SimpleDir; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$SimpleDir + implements CopyWith$Fragment$DirEntry$$SimpleDir { + _CopyWithImpl$Fragment$DirEntry$$SimpleDir( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$SimpleDir _instance; + + final TRes Function(Fragment$DirEntry$$SimpleDir) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$SimpleDir( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$SimpleDir + implements CopyWith$Fragment$DirEntry$$SimpleDir { + _CopyWithStubImpl$Fragment$DirEntry$$SimpleDir(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} + +class Fragment$DirEntry$$SimpleFile + implements Fragment$File$$SimpleFile, Fragment$DirEntry { + Fragment$DirEntry$$SimpleFile({ + required this.name, + required this.size, + this.$__typename = 'SimpleFile', + }); + + factory Fragment$DirEntry$$SimpleFile.fromJson(Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$SimpleFile( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final int size; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$SimpleFile || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$SimpleFile + on Fragment$DirEntry$$SimpleFile { + CopyWith$Fragment$DirEntry$$SimpleFile + get copyWith => CopyWith$Fragment$DirEntry$$SimpleFile( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$SimpleFile { + factory CopyWith$Fragment$DirEntry$$SimpleFile( + Fragment$DirEntry$$SimpleFile instance, + TRes Function(Fragment$DirEntry$$SimpleFile) then, + ) = _CopyWithImpl$Fragment$DirEntry$$SimpleFile; + + factory CopyWith$Fragment$DirEntry$$SimpleFile.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$SimpleFile; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$SimpleFile + implements CopyWith$Fragment$DirEntry$$SimpleFile { + _CopyWithImpl$Fragment$DirEntry$$SimpleFile( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$SimpleFile _instance; + + final TRes Function(Fragment$DirEntry$$SimpleFile) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$SimpleFile( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$SimpleFile + implements CopyWith$Fragment$DirEntry$$SimpleFile { + _CopyWithStubImpl$Fragment$DirEntry$$SimpleFile(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +class Fragment$DirEntry$$TorrentFS + implements Fragment$TorrentDir, Fragment$DirEntry { + Fragment$DirEntry$$TorrentFS({ + required this.name, + required this.torrent, + this.$__typename = 'TorrentFS', + }); + + factory Fragment$DirEntry$$TorrentFS.fromJson(Map json) { + final l$name = json['name']; + final l$torrent = json['torrent']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$TorrentFS( + name: (l$name as String), + torrent: Fragment$DirEntry$$TorrentFS$torrent.fromJson( + (l$torrent as Map)), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final Fragment$DirEntry$$TorrentFS$torrent torrent; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$torrent = torrent; + resultData['torrent'] = l$torrent.toJson(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$torrent = torrent; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$torrent, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$TorrentFS || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$torrent = torrent; + final lOther$torrent = other.torrent; + if (l$torrent != lOther$torrent) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$TorrentFS + on Fragment$DirEntry$$TorrentFS { + CopyWith$Fragment$DirEntry$$TorrentFS + get copyWith => CopyWith$Fragment$DirEntry$$TorrentFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$TorrentFS { + factory CopyWith$Fragment$DirEntry$$TorrentFS( + Fragment$DirEntry$$TorrentFS instance, + TRes Function(Fragment$DirEntry$$TorrentFS) then, + ) = _CopyWithImpl$Fragment$DirEntry$$TorrentFS; + + factory CopyWith$Fragment$DirEntry$$TorrentFS.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS; + + TRes call({ + String? name, + Fragment$DirEntry$$TorrentFS$torrent? torrent, + String? $__typename, + }); + CopyWith$Fragment$DirEntry$$TorrentFS$torrent get torrent; +} + +class _CopyWithImpl$Fragment$DirEntry$$TorrentFS + implements CopyWith$Fragment$DirEntry$$TorrentFS { + _CopyWithImpl$Fragment$DirEntry$$TorrentFS( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$TorrentFS _instance; + + final TRes Function(Fragment$DirEntry$$TorrentFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? torrent = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$TorrentFS( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + torrent: torrent == _undefined || torrent == null + ? _instance.torrent + : (torrent as Fragment$DirEntry$$TorrentFS$torrent), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + CopyWith$Fragment$DirEntry$$TorrentFS$torrent get torrent { + final local$torrent = _instance.torrent; + return CopyWith$Fragment$DirEntry$$TorrentFS$torrent( + local$torrent, (e) => call(torrent: e)); + } +} + +class _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS + implements CopyWith$Fragment$DirEntry$$TorrentFS { + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS(this._res); + + final TRes _res; + + @override + call({ + String? name, + Fragment$DirEntry$$TorrentFS$torrent? torrent, + String? $__typename, + }) => + _res; + + @override + CopyWith$Fragment$DirEntry$$TorrentFS$torrent get torrent => + CopyWith$Fragment$DirEntry$$TorrentFS$torrent.stub(_res); +} + +class Fragment$DirEntry$$TorrentFS$torrent + implements Fragment$TorrentDir$torrent { + Fragment$DirEntry$$TorrentFS$torrent({ + required this.name, + required this.infohash, + required this.bytesCompleted, + required this.torrentFilePath, + required this.bytesMissing, + this.$__typename = 'Torrent', + }); + + factory Fragment$DirEntry$$TorrentFS$torrent.fromJson( + Map json) { + final l$name = json['name']; + final l$infohash = json['infohash']; + final l$bytesCompleted = json['bytesCompleted']; + final l$torrentFilePath = json['torrentFilePath']; + final l$bytesMissing = json['bytesMissing']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$TorrentFS$torrent( + name: (l$name as String), + infohash: (l$infohash as String), + bytesCompleted: (l$bytesCompleted as int), + torrentFilePath: (l$torrentFilePath as String), + bytesMissing: (l$bytesMissing as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String infohash; + + @override + final int bytesCompleted; + + @override + final String torrentFilePath; + + @override + final int bytesMissing; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$infohash = infohash; + resultData['infohash'] = l$infohash; + final l$bytesCompleted = bytesCompleted; + resultData['bytesCompleted'] = l$bytesCompleted; + final l$torrentFilePath = torrentFilePath; + resultData['torrentFilePath'] = l$torrentFilePath; + final l$bytesMissing = bytesMissing; + resultData['bytesMissing'] = l$bytesMissing; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$infohash = infohash; + final l$bytesCompleted = bytesCompleted; + final l$torrentFilePath = torrentFilePath; + final l$bytesMissing = bytesMissing; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$infohash, + l$bytesCompleted, + l$torrentFilePath, + l$bytesMissing, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$TorrentFS$torrent || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (l$infohash != lOther$infohash) { + return false; + } + final l$bytesCompleted = bytesCompleted; + final lOther$bytesCompleted = other.bytesCompleted; + if (l$bytesCompleted != lOther$bytesCompleted) { + return false; + } + final l$torrentFilePath = torrentFilePath; + final lOther$torrentFilePath = other.torrentFilePath; + if (l$torrentFilePath != lOther$torrentFilePath) { + return false; + } + final l$bytesMissing = bytesMissing; + final lOther$bytesMissing = other.bytesMissing; + if (l$bytesMissing != lOther$bytesMissing) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$TorrentFS$torrent + on Fragment$DirEntry$$TorrentFS$torrent { + CopyWith$Fragment$DirEntry$$TorrentFS$torrent< + Fragment$DirEntry$$TorrentFS$torrent> + get copyWith => CopyWith$Fragment$DirEntry$$TorrentFS$torrent( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$TorrentFS$torrent { + factory CopyWith$Fragment$DirEntry$$TorrentFS$torrent( + Fragment$DirEntry$$TorrentFS$torrent instance, + TRes Function(Fragment$DirEntry$$TorrentFS$torrent) then, + ) = _CopyWithImpl$Fragment$DirEntry$$TorrentFS$torrent; + + factory CopyWith$Fragment$DirEntry$$TorrentFS$torrent.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS$torrent; + + TRes call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$TorrentFS$torrent + implements CopyWith$Fragment$DirEntry$$TorrentFS$torrent { + _CopyWithImpl$Fragment$DirEntry$$TorrentFS$torrent( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$TorrentFS$torrent _instance; + + final TRes Function(Fragment$DirEntry$$TorrentFS$torrent) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? infohash = _undefined, + Object? bytesCompleted = _undefined, + Object? torrentFilePath = _undefined, + Object? bytesMissing = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$TorrentFS$torrent( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + infohash: infohash == _undefined || infohash == null + ? _instance.infohash + : (infohash as String), + bytesCompleted: bytesCompleted == _undefined || bytesCompleted == null + ? _instance.bytesCompleted + : (bytesCompleted as int), + torrentFilePath: + torrentFilePath == _undefined || torrentFilePath == null + ? _instance.torrentFilePath + : (torrentFilePath as String), + bytesMissing: bytesMissing == _undefined || bytesMissing == null + ? _instance.bytesMissing + : (bytesMissing as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS$torrent + implements CopyWith$Fragment$DirEntry$$TorrentFS$torrent { + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFS$torrent(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }) => + _res; +} + +class Fragment$DirEntry$$TorrentFileEntry + implements Fragment$File$$TorrentFileEntry, Fragment$DirEntry { + Fragment$DirEntry$$TorrentFileEntry({ + required this.name, + required this.size, + this.$__typename = 'TorrentFileEntry', + }); + + factory Fragment$DirEntry$$TorrentFileEntry.fromJson( + Map json) { + final l$name = json['name']; + final l$size = json['size']; + final l$$__typename = json['__typename']; + return Fragment$DirEntry$$TorrentFileEntry( + name: (l$name as String), + size: (l$size as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final int size; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$size = size; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$size, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Fragment$DirEntry$$TorrentFileEntry || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$DirEntry$$TorrentFileEntry + on Fragment$DirEntry$$TorrentFileEntry { + CopyWith$Fragment$DirEntry$$TorrentFileEntry< + Fragment$DirEntry$$TorrentFileEntry> + get copyWith => CopyWith$Fragment$DirEntry$$TorrentFileEntry( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$DirEntry$$TorrentFileEntry { + factory CopyWith$Fragment$DirEntry$$TorrentFileEntry( + Fragment$DirEntry$$TorrentFileEntry instance, + TRes Function(Fragment$DirEntry$$TorrentFileEntry) then, + ) = _CopyWithImpl$Fragment$DirEntry$$TorrentFileEntry; + + factory CopyWith$Fragment$DirEntry$$TorrentFileEntry.stub(TRes res) = + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFileEntry; + + TRes call({ + String? name, + int? size, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$DirEntry$$TorrentFileEntry + implements CopyWith$Fragment$DirEntry$$TorrentFileEntry { + _CopyWithImpl$Fragment$DirEntry$$TorrentFileEntry( + this._instance, + this._then, + ); + + final Fragment$DirEntry$$TorrentFileEntry _instance; + + final TRes Function(Fragment$DirEntry$$TorrentFileEntry) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? size = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$DirEntry$$TorrentFileEntry( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$DirEntry$$TorrentFileEntry + implements CopyWith$Fragment$DirEntry$$TorrentFileEntry { + _CopyWithStubImpl$Fragment$DirEntry$$TorrentFileEntry(this._res); + + final TRes _res; + + @override + call({ + String? name, + int? size, + String? $__typename, + }) => + _res; +} + +class Variables$Query$ListDir { + factory Variables$Query$ListDir({required String path}) => + Variables$Query$ListDir._({ + r'path': path, + }); + + Variables$Query$ListDir._(this._$data); + + factory Variables$Query$ListDir.fromJson(Map data) { + final result$data = {}; + final l$path = data['path']; + result$data['path'] = (l$path as String); + return Variables$Query$ListDir._(result$data); + } + + Map _$data; + + String get path => (_$data['path'] as String); + + Map toJson() { + final result$data = {}; + final l$path = path; + result$data['path'] = l$path; + return result$data; + } + + CopyWith$Variables$Query$ListDir get copyWith => + CopyWith$Variables$Query$ListDir( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Variables$Query$ListDir || + runtimeType != other.runtimeType) { + return false; + } + final l$path = path; + final lOther$path = other.path; + if (l$path != lOther$path) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$path = path; + return Object.hashAll([l$path]); + } +} + +abstract class CopyWith$Variables$Query$ListDir { + factory CopyWith$Variables$Query$ListDir( + Variables$Query$ListDir instance, + TRes Function(Variables$Query$ListDir) then, + ) = _CopyWithImpl$Variables$Query$ListDir; + + factory CopyWith$Variables$Query$ListDir.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$ListDir; + + TRes call({String? path}); +} + +class _CopyWithImpl$Variables$Query$ListDir + implements CopyWith$Variables$Query$ListDir { + _CopyWithImpl$Variables$Query$ListDir( + this._instance, + this._then, + ); + + final Variables$Query$ListDir _instance; + + final TRes Function(Variables$Query$ListDir) _then; + + static const _undefined = {}; + + @override + TRes call({Object? path = _undefined}) => _then(Variables$Query$ListDir._({ + ..._instance._$data, + if (path != _undefined && path != null) 'path': (path as String), + })); +} + +class _CopyWithStubImpl$Variables$Query$ListDir + implements CopyWith$Variables$Query$ListDir { + _CopyWithStubImpl$Variables$Query$ListDir(this._res); + + final TRes _res; + + @override + call({String? path}) => _res; +} + +class Query$ListDir { + Query$ListDir({ + this.fsEntry, + this.$__typename = 'Query', + }); + + factory Query$ListDir.fromJson(Map json) { + final l$fsEntry = json['fsEntry']; + final l$$__typename = json['__typename']; + return Query$ListDir( + fsEntry: l$fsEntry == null + ? null + : Query$ListDir$fsEntry.fromJson((l$fsEntry as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$ListDir$fsEntry? fsEntry; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$fsEntry = fsEntry; + resultData['fsEntry'] = l$fsEntry?.toJson(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$fsEntry = fsEntry; + final l$$__typename = $__typename; + return Object.hashAll([ + l$fsEntry, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir || runtimeType != other.runtimeType) { + return false; + } + final l$fsEntry = fsEntry; + final lOther$fsEntry = other.fsEntry; + if (l$fsEntry != lOther$fsEntry) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir on Query$ListDir { + CopyWith$Query$ListDir get copyWith => CopyWith$Query$ListDir( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir { + factory CopyWith$Query$ListDir( + Query$ListDir instance, + TRes Function(Query$ListDir) then, + ) = _CopyWithImpl$Query$ListDir; + + factory CopyWith$Query$ListDir.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir; + + TRes call({ + Query$ListDir$fsEntry? fsEntry, + String? $__typename, + }); + CopyWith$Query$ListDir$fsEntry get fsEntry; +} + +class _CopyWithImpl$Query$ListDir + implements CopyWith$Query$ListDir { + _CopyWithImpl$Query$ListDir( + this._instance, + this._then, + ); + + final Query$ListDir _instance; + + final TRes Function(Query$ListDir) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? fsEntry = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListDir( + fsEntry: fsEntry == _undefined + ? _instance.fsEntry + : (fsEntry as Query$ListDir$fsEntry?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + CopyWith$Query$ListDir$fsEntry get fsEntry { + final local$fsEntry = _instance.fsEntry; + return local$fsEntry == null + ? CopyWith$Query$ListDir$fsEntry.stub(_then(_instance)) + : CopyWith$Query$ListDir$fsEntry( + local$fsEntry, (e) => call(fsEntry: e)); + } +} + +class _CopyWithStubImpl$Query$ListDir + implements CopyWith$Query$ListDir { + _CopyWithStubImpl$Query$ListDir(this._res); + + final TRes _res; + + @override + call({ + Query$ListDir$fsEntry? fsEntry, + String? $__typename, + }) => + _res; + + @override + CopyWith$Query$ListDir$fsEntry get fsEntry => + CopyWith$Query$ListDir$fsEntry.stub(_res); +} + +const documentNodeQueryListDir = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'ListDir'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'path')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'fsEntry'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'path'), + value: VariableNode(name: NameNode(value: 'path')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + InlineFragmentNode( + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Dir'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'entries'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'DirEntry'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FragmentSpreadNode( + name: NameNode(value: 'TorrentDir'), + directives: [], + ), + FragmentSpreadNode( + name: NameNode(value: 'ArchiveDir'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionDirEntry, + fragmentDefinitionTorrentDir, + fragmentDefinitionArchiveDir, + fragmentDefinitionFile, +]); +Query$ListDir _parserFn$Query$ListDir(Map data) => + Query$ListDir.fromJson(data); +typedef OnQueryComplete$Query$ListDir = FutureOr Function( + Map?, + Query$ListDir?, +); + +class Options$Query$ListDir extends graphql.QueryOptions { + Options$Query$ListDir({ + super.operationName, + required Variables$Query$ListDir variables, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Query$ListDir? typedOptimisticResult, + super.pollInterval, + super.context, + OnQueryComplete$Query$ListDir? onComplete, + super.onError, + }) : onCompleteWithParsed = onComplete, + super( + variables: variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$ListDir(data), + ), + document: documentNodeQueryListDir, + parserFn: _parserFn$Query$ListDir, + ); + + final OnQueryComplete$Query$ListDir? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$ListDir + extends graphql.WatchQueryOptions { + WatchOptions$Query$ListDir({ + super.operationName, + required Variables$Query$ListDir variables, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Query$ListDir? typedOptimisticResult, + super.context, + super.pollInterval, + super.eagerlyFetchResults, + super.carryForwardDataOnException, + super.fetchResults, + }) : super( + variables: variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + document: documentNodeQueryListDir, + parserFn: _parserFn$Query$ListDir, + ); +} + +class FetchMoreOptions$Query$ListDir extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$ListDir({ + required super.updateQuery, + required Variables$Query$ListDir variables, + }) : super( + variables: variables.toJson(), + document: documentNodeQueryListDir, + ); +} + +extension ClientExtension$Query$ListDir on graphql.GraphQLClient { + Future> query$ListDir( + Options$Query$ListDir options) async => + await query(options); + graphql.ObservableQuery watchQuery$ListDir( + WatchOptions$Query$ListDir options) => + watchQuery(options); + void writeQuery$ListDir({ + required Query$ListDir data, + required Variables$Query$ListDir variables, + bool broadcast = true, + }) => + writeQuery( + graphql.Request( + operation: const graphql.Operation(document: documentNodeQueryListDir), + variables: variables.toJson(), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Query$ListDir? readQuery$ListDir({ + required Variables$Query$ListDir variables, + bool optimistic = true, + }) { + final result = readQuery( + graphql.Request( + operation: const graphql.Operation(document: documentNodeQueryListDir), + variables: variables.toJson(), + ), + optimistic: optimistic, + ); + return result == null ? null : Query$ListDir.fromJson(result); + } +} + +graphql_flutter.QueryHookResult useQuery$ListDir( + Options$Query$ListDir options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery useWatchQuery$ListDir( + WatchOptions$Query$ListDir options) => + graphql_flutter.useWatchQuery(options); + +class Query$ListDir$Widget extends graphql_flutter.Query { + const Query$ListDir$Widget({ + super.key, + required Options$Query$ListDir super.options, + required super.builder, + }); +} + +class Query$ListDir$fsEntry { + Query$ListDir$fsEntry({ + required this.name, + required this.$__typename, + }); + + factory Query$ListDir$fsEntry.fromJson(Map json) { + switch (json["__typename"] as String) { + case "ArchiveFS": + return Query$ListDir$fsEntry$$ArchiveFS.fromJson(json); + + case "ResolverFS": + return Query$ListDir$fsEntry$$ResolverFS.fromJson(json); + + case "SimpleDir": + return Query$ListDir$fsEntry$$SimpleDir.fromJson(json); + + case "TorrentFS": + return Query$ListDir$fsEntry$$TorrentFS.fromJson(json); + + case "SimpleFile": + return Query$ListDir$fsEntry$$SimpleFile.fromJson(json); + + case "TorrentFileEntry": + return Query$ListDir$fsEntry$$TorrentFileEntry.fromJson(json); + + default: + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$ListDir$fsEntry( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + } + + final String name; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry on Query$ListDir$fsEntry { + CopyWith$Query$ListDir$fsEntry get copyWith => + CopyWith$Query$ListDir$fsEntry( + this, + (i) => i, + ); + _T when<_T>({ + required _T Function(Query$ListDir$fsEntry$$ArchiveFS) archiveFS, + required _T Function(Query$ListDir$fsEntry$$ResolverFS) resolverFS, + required _T Function(Query$ListDir$fsEntry$$SimpleDir) simpleDir, + required _T Function(Query$ListDir$fsEntry$$TorrentFS) torrentFS, + required _T Function(Query$ListDir$fsEntry$$SimpleFile) simpleFile, + required _T Function(Query$ListDir$fsEntry$$TorrentFileEntry) + torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ArchiveFS": + return archiveFS(this as Query$ListDir$fsEntry$$ArchiveFS); + + case "ResolverFS": + return resolverFS(this as Query$ListDir$fsEntry$$ResolverFS); + + case "SimpleDir": + return simpleDir(this as Query$ListDir$fsEntry$$SimpleDir); + + case "TorrentFS": + return torrentFS(this as Query$ListDir$fsEntry$$TorrentFS); + + case "SimpleFile": + return simpleFile(this as Query$ListDir$fsEntry$$SimpleFile); + + case "TorrentFileEntry": + return torrentFileEntry( + this as Query$ListDir$fsEntry$$TorrentFileEntry); + + default: + return orElse(); + } + } + + _T maybeWhen<_T>({ + _T Function(Query$ListDir$fsEntry$$ArchiveFS)? archiveFS, + _T Function(Query$ListDir$fsEntry$$ResolverFS)? resolverFS, + _T Function(Query$ListDir$fsEntry$$SimpleDir)? simpleDir, + _T Function(Query$ListDir$fsEntry$$TorrentFS)? torrentFS, + _T Function(Query$ListDir$fsEntry$$SimpleFile)? simpleFile, + _T Function(Query$ListDir$fsEntry$$TorrentFileEntry)? torrentFileEntry, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ArchiveFS": + if (archiveFS != null) { + return archiveFS(this as Query$ListDir$fsEntry$$ArchiveFS); + } else { + return orElse(); + } + + case "ResolverFS": + if (resolverFS != null) { + return resolverFS(this as Query$ListDir$fsEntry$$ResolverFS); + } else { + return orElse(); + } + + case "SimpleDir": + if (simpleDir != null) { + return simpleDir(this as Query$ListDir$fsEntry$$SimpleDir); + } else { + return orElse(); + } + + case "TorrentFS": + if (torrentFS != null) { + return torrentFS(this as Query$ListDir$fsEntry$$TorrentFS); + } else { + return orElse(); + } + + case "SimpleFile": + if (simpleFile != null) { + return simpleFile(this as Query$ListDir$fsEntry$$SimpleFile); + } else { + return orElse(); + } + + case "TorrentFileEntry": + if (torrentFileEntry != null) { + return torrentFileEntry( + this as Query$ListDir$fsEntry$$TorrentFileEntry); + } else { + return orElse(); + } + + default: + return orElse(); + } + } +} + +abstract class CopyWith$Query$ListDir$fsEntry { + factory CopyWith$Query$ListDir$fsEntry( + Query$ListDir$fsEntry instance, + TRes Function(Query$ListDir$fsEntry) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry; + + factory CopyWith$Query$ListDir$fsEntry.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$ListDir$fsEntry + implements CopyWith$Query$ListDir$fsEntry { + _CopyWithImpl$Query$ListDir$fsEntry( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry _instance; + + final TRes Function(Query$ListDir$fsEntry) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListDir$fsEntry( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry + implements CopyWith$Query$ListDir$fsEntry { + _CopyWithStubImpl$Query$ListDir$fsEntry(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} + +class Query$ListDir$fsEntry$$ArchiveFS + implements Fragment$ArchiveDir, Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$ArchiveFS({ + required this.entries, + this.$__typename = 'ArchiveFS', + required this.name, + required this.size, + }); + + factory Query$ListDir$fsEntry$$ArchiveFS.fromJson(Map json) { + final l$entries = json['entries']; + final l$$__typename = json['__typename']; + final l$name = json['name']; + final l$size = json['size']; + return Query$ListDir$fsEntry$$ArchiveFS( + entries: (l$entries as List) + .map((e) => Fragment$DirEntry.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + name: (l$name as String), + size: (l$size as int), + ); + } + + final List entries; + + @override + final String $__typename; + + @override + final String name; + + @override + final int size; + + @override + Map toJson() { + final resultData = {}; + final l$entries = entries; + resultData['entries'] = l$entries.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + final l$name = name; + resultData['name'] = l$name; + final l$size = size; + resultData['size'] = l$size; + return resultData; + } + + @override + int get hashCode { + final l$entries = entries; + final l$$__typename = $__typename; + final l$name = name; + final l$size = size; + return Object.hashAll([ + Object.hashAll(l$entries.map((v) => v)), + l$$__typename, + l$name, + l$size, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$ArchiveFS || + runtimeType != other.runtimeType) { + return false; + } + final l$entries = entries; + final lOther$entries = other.entries; + if (l$entries.length != lOther$entries.length) { + return false; + } + for (int i = 0; i < l$entries.length; i++) { + final l$entries$entry = l$entries[i]; + final lOther$entries$entry = lOther$entries[i]; + if (l$entries$entry != lOther$entries$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$size = size; + final lOther$size = other.size; + if (l$size != lOther$size) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$ArchiveFS + on Query$ListDir$fsEntry$$ArchiveFS { + CopyWith$Query$ListDir$fsEntry$$ArchiveFS + get copyWith => CopyWith$Query$ListDir$fsEntry$$ArchiveFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$ArchiveFS { + factory CopyWith$Query$ListDir$fsEntry$$ArchiveFS( + Query$ListDir$fsEntry$$ArchiveFS instance, + TRes Function(Query$ListDir$fsEntry$$ArchiveFS) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$ArchiveFS; + + factory CopyWith$Query$ListDir$fsEntry$$ArchiveFS.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$ArchiveFS; + + TRes call({ + List? entries, + String? $__typename, + String? name, + int? size, + }); + TRes entries( + Iterable Function( + Iterable>) + fn); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$ArchiveFS + implements CopyWith$Query$ListDir$fsEntry$$ArchiveFS { + _CopyWithImpl$Query$ListDir$fsEntry$$ArchiveFS( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$ArchiveFS _instance; + + final TRes Function(Query$ListDir$fsEntry$$ArchiveFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? entries = _undefined, + Object? $__typename = _undefined, + Object? name = _undefined, + Object? size = _undefined, + }) => + _then(Query$ListDir$fsEntry$$ArchiveFS( + entries: entries == _undefined || entries == null + ? _instance.entries + : (entries as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + size: + size == _undefined || size == null ? _instance.size : (size as int), + )); + + @override + TRes entries( + Iterable Function( + Iterable>) + fn) => + call( + entries: fn(_instance.entries.map((e) => CopyWith$Fragment$DirEntry( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$ArchiveFS + implements CopyWith$Query$ListDir$fsEntry$$ArchiveFS { + _CopyWithStubImpl$Query$ListDir$fsEntry$$ArchiveFS(this._res); + + final TRes _res; + + @override + call({ + List? entries, + String? $__typename, + String? name, + int? size, + }) => + _res; + + @override + entries(fn) => _res; +} + +class Query$ListDir$fsEntry$$ResolverFS implements Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$ResolverFS({ + required this.entries, + this.$__typename = 'ResolverFS', + required this.name, + }); + + factory Query$ListDir$fsEntry$$ResolverFS.fromJson( + Map json) { + final l$entries = json['entries']; + final l$$__typename = json['__typename']; + final l$name = json['name']; + return Query$ListDir$fsEntry$$ResolverFS( + entries: (l$entries as List) + .map((e) => Fragment$DirEntry.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + name: (l$name as String), + ); + } + + final List entries; + + @override + final String $__typename; + + @override + final String name; + + @override + Map toJson() { + final resultData = {}; + final l$entries = entries; + resultData['entries'] = l$entries.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + final l$name = name; + resultData['name'] = l$name; + return resultData; + } + + @override + int get hashCode { + final l$entries = entries; + final l$$__typename = $__typename; + final l$name = name; + return Object.hashAll([ + Object.hashAll(l$entries.map((v) => v)), + l$$__typename, + l$name, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$ResolverFS || + runtimeType != other.runtimeType) { + return false; + } + final l$entries = entries; + final lOther$entries = other.entries; + if (l$entries.length != lOther$entries.length) { + return false; + } + for (int i = 0; i < l$entries.length; i++) { + final l$entries$entry = l$entries[i]; + final lOther$entries$entry = lOther$entries[i]; + if (l$entries$entry != lOther$entries$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$ResolverFS + on Query$ListDir$fsEntry$$ResolverFS { + CopyWith$Query$ListDir$fsEntry$$ResolverFS + get copyWith => CopyWith$Query$ListDir$fsEntry$$ResolverFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$ResolverFS { + factory CopyWith$Query$ListDir$fsEntry$$ResolverFS( + Query$ListDir$fsEntry$$ResolverFS instance, + TRes Function(Query$ListDir$fsEntry$$ResolverFS) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$ResolverFS; + + factory CopyWith$Query$ListDir$fsEntry$$ResolverFS.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$ResolverFS; + + TRes call({ + List? entries, + String? $__typename, + String? name, + }); + TRes entries( + Iterable Function( + Iterable>) + fn); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$ResolverFS + implements CopyWith$Query$ListDir$fsEntry$$ResolverFS { + _CopyWithImpl$Query$ListDir$fsEntry$$ResolverFS( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$ResolverFS _instance; + + final TRes Function(Query$ListDir$fsEntry$$ResolverFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? entries = _undefined, + Object? $__typename = _undefined, + Object? name = _undefined, + }) => + _then(Query$ListDir$fsEntry$$ResolverFS( + entries: entries == _undefined || entries == null + ? _instance.entries + : (entries as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + )); + + @override + TRes entries( + Iterable Function( + Iterable>) + fn) => + call( + entries: fn(_instance.entries.map((e) => CopyWith$Fragment$DirEntry( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$ResolverFS + implements CopyWith$Query$ListDir$fsEntry$$ResolverFS { + _CopyWithStubImpl$Query$ListDir$fsEntry$$ResolverFS(this._res); + + final TRes _res; + + @override + call({ + List? entries, + String? $__typename, + String? name, + }) => + _res; + + @override + entries(fn) => _res; +} + +class Query$ListDir$fsEntry$$SimpleDir implements Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$SimpleDir({ + required this.entries, + this.$__typename = 'SimpleDir', + required this.name, + }); + + factory Query$ListDir$fsEntry$$SimpleDir.fromJson(Map json) { + final l$entries = json['entries']; + final l$$__typename = json['__typename']; + final l$name = json['name']; + return Query$ListDir$fsEntry$$SimpleDir( + entries: (l$entries as List) + .map((e) => Fragment$DirEntry.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + name: (l$name as String), + ); + } + + final List entries; + + @override + final String $__typename; + + @override + final String name; + + @override + Map toJson() { + final resultData = {}; + final l$entries = entries; + resultData['entries'] = l$entries.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + final l$name = name; + resultData['name'] = l$name; + return resultData; + } + + @override + int get hashCode { + final l$entries = entries; + final l$$__typename = $__typename; + final l$name = name; + return Object.hashAll([ + Object.hashAll(l$entries.map((v) => v)), + l$$__typename, + l$name, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$SimpleDir || + runtimeType != other.runtimeType) { + return false; + } + final l$entries = entries; + final lOther$entries = other.entries; + if (l$entries.length != lOther$entries.length) { + return false; + } + for (int i = 0; i < l$entries.length; i++) { + final l$entries$entry = l$entries[i]; + final lOther$entries$entry = lOther$entries[i]; + if (l$entries$entry != lOther$entries$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$SimpleDir + on Query$ListDir$fsEntry$$SimpleDir { + CopyWith$Query$ListDir$fsEntry$$SimpleDir + get copyWith => CopyWith$Query$ListDir$fsEntry$$SimpleDir( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$SimpleDir { + factory CopyWith$Query$ListDir$fsEntry$$SimpleDir( + Query$ListDir$fsEntry$$SimpleDir instance, + TRes Function(Query$ListDir$fsEntry$$SimpleDir) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$SimpleDir; + + factory CopyWith$Query$ListDir$fsEntry$$SimpleDir.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleDir; + + TRes call({ + List? entries, + String? $__typename, + String? name, + }); + TRes entries( + Iterable Function( + Iterable>) + fn); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$SimpleDir + implements CopyWith$Query$ListDir$fsEntry$$SimpleDir { + _CopyWithImpl$Query$ListDir$fsEntry$$SimpleDir( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$SimpleDir _instance; + + final TRes Function(Query$ListDir$fsEntry$$SimpleDir) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? entries = _undefined, + Object? $__typename = _undefined, + Object? name = _undefined, + }) => + _then(Query$ListDir$fsEntry$$SimpleDir( + entries: entries == _undefined || entries == null + ? _instance.entries + : (entries as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + )); + + @override + TRes entries( + Iterable Function( + Iterable>) + fn) => + call( + entries: fn(_instance.entries.map((e) => CopyWith$Fragment$DirEntry( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleDir + implements CopyWith$Query$ListDir$fsEntry$$SimpleDir { + _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleDir(this._res); + + final TRes _res; + + @override + call({ + List? entries, + String? $__typename, + String? name, + }) => + _res; + + @override + entries(fn) => _res; +} + +class Query$ListDir$fsEntry$$TorrentFS + implements Fragment$TorrentDir, Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$TorrentFS({ + required this.entries, + this.$__typename = 'TorrentFS', + required this.name, + required this.torrent, + }); + + factory Query$ListDir$fsEntry$$TorrentFS.fromJson(Map json) { + final l$entries = json['entries']; + final l$$__typename = json['__typename']; + final l$name = json['name']; + final l$torrent = json['torrent']; + return Query$ListDir$fsEntry$$TorrentFS( + entries: (l$entries as List) + .map((e) => Fragment$DirEntry.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + name: (l$name as String), + torrent: Query$ListDir$fsEntry$$TorrentFS$torrent.fromJson( + (l$torrent as Map)), + ); + } + + final List entries; + + @override + final String $__typename; + + @override + final String name; + + @override + final Query$ListDir$fsEntry$$TorrentFS$torrent torrent; + + @override + Map toJson() { + final resultData = {}; + final l$entries = entries; + resultData['entries'] = l$entries.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + final l$name = name; + resultData['name'] = l$name; + final l$torrent = torrent; + resultData['torrent'] = l$torrent.toJson(); + return resultData; + } + + @override + int get hashCode { + final l$entries = entries; + final l$$__typename = $__typename; + final l$name = name; + final l$torrent = torrent; + return Object.hashAll([ + Object.hashAll(l$entries.map((v) => v)), + l$$__typename, + l$name, + l$torrent, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$TorrentFS || + runtimeType != other.runtimeType) { + return false; + } + final l$entries = entries; + final lOther$entries = other.entries; + if (l$entries.length != lOther$entries.length) { + return false; + } + for (int i = 0; i < l$entries.length; i++) { + final l$entries$entry = l$entries[i]; + final lOther$entries$entry = lOther$entries[i]; + if (l$entries$entry != lOther$entries$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$torrent = torrent; + final lOther$torrent = other.torrent; + if (l$torrent != lOther$torrent) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$TorrentFS + on Query$ListDir$fsEntry$$TorrentFS { + CopyWith$Query$ListDir$fsEntry$$TorrentFS + get copyWith => CopyWith$Query$ListDir$fsEntry$$TorrentFS( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$TorrentFS { + factory CopyWith$Query$ListDir$fsEntry$$TorrentFS( + Query$ListDir$fsEntry$$TorrentFS instance, + TRes Function(Query$ListDir$fsEntry$$TorrentFS) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS; + + factory CopyWith$Query$ListDir$fsEntry$$TorrentFS.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS; + + TRes call({ + List? entries, + String? $__typename, + String? name, + Query$ListDir$fsEntry$$TorrentFS$torrent? torrent, + }); + TRes entries( + Iterable Function( + Iterable>) + fn); + CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent get torrent; +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS + implements CopyWith$Query$ListDir$fsEntry$$TorrentFS { + _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$TorrentFS _instance; + + final TRes Function(Query$ListDir$fsEntry$$TorrentFS) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? entries = _undefined, + Object? $__typename = _undefined, + Object? name = _undefined, + Object? torrent = _undefined, + }) => + _then(Query$ListDir$fsEntry$$TorrentFS( + entries: entries == _undefined || entries == null + ? _instance.entries + : (entries as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + torrent: torrent == _undefined || torrent == null + ? _instance.torrent + : (torrent as Query$ListDir$fsEntry$$TorrentFS$torrent), + )); + + @override + TRes entries( + Iterable Function( + Iterable>) + fn) => + call( + entries: fn(_instance.entries.map((e) => CopyWith$Fragment$DirEntry( + e, + (i) => i, + ))).toList()); + + @override + CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent get torrent { + final local$torrent = _instance.torrent; + return CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent( + local$torrent, (e) => call(torrent: e)); + } +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS + implements CopyWith$Query$ListDir$fsEntry$$TorrentFS { + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS(this._res); + + final TRes _res; + + @override + call({ + List? entries, + String? $__typename, + String? name, + Query$ListDir$fsEntry$$TorrentFS$torrent? torrent, + }) => + _res; + + @override + entries(fn) => _res; + + @override + CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent get torrent => + CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent.stub(_res); +} + +class Query$ListDir$fsEntry$$SimpleFile implements Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$SimpleFile({ + required this.name, + this.$__typename = 'SimpleFile', + }); + + factory Query$ListDir$fsEntry$$SimpleFile.fromJson( + Map json) { + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$ListDir$fsEntry$$SimpleFile( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$SimpleFile || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$SimpleFile + on Query$ListDir$fsEntry$$SimpleFile { + CopyWith$Query$ListDir$fsEntry$$SimpleFile + get copyWith => CopyWith$Query$ListDir$fsEntry$$SimpleFile( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$SimpleFile { + factory CopyWith$Query$ListDir$fsEntry$$SimpleFile( + Query$ListDir$fsEntry$$SimpleFile instance, + TRes Function(Query$ListDir$fsEntry$$SimpleFile) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$SimpleFile; + + factory CopyWith$Query$ListDir$fsEntry$$SimpleFile.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleFile; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$SimpleFile + implements CopyWith$Query$ListDir$fsEntry$$SimpleFile { + _CopyWithImpl$Query$ListDir$fsEntry$$SimpleFile( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$SimpleFile _instance; + + final TRes Function(Query$ListDir$fsEntry$$SimpleFile) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListDir$fsEntry$$SimpleFile( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleFile + implements CopyWith$Query$ListDir$fsEntry$$SimpleFile { + _CopyWithStubImpl$Query$ListDir$fsEntry$$SimpleFile(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} + +class Query$ListDir$fsEntry$$TorrentFS$torrent + implements Fragment$TorrentDir$torrent { + Query$ListDir$fsEntry$$TorrentFS$torrent({ + required this.name, + required this.infohash, + required this.bytesCompleted, + required this.torrentFilePath, + required this.bytesMissing, + this.$__typename = 'Torrent', + }); + + factory Query$ListDir$fsEntry$$TorrentFS$torrent.fromJson( + Map json) { + final l$name = json['name']; + final l$infohash = json['infohash']; + final l$bytesCompleted = json['bytesCompleted']; + final l$torrentFilePath = json['torrentFilePath']; + final l$bytesMissing = json['bytesMissing']; + final l$$__typename = json['__typename']; + return Query$ListDir$fsEntry$$TorrentFS$torrent( + name: (l$name as String), + infohash: (l$infohash as String), + bytesCompleted: (l$bytesCompleted as int), + torrentFilePath: (l$torrentFilePath as String), + bytesMissing: (l$bytesMissing as int), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String infohash; + + @override + final int bytesCompleted; + + @override + final String torrentFilePath; + + @override + final int bytesMissing; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$infohash = infohash; + resultData['infohash'] = l$infohash; + final l$bytesCompleted = bytesCompleted; + resultData['bytesCompleted'] = l$bytesCompleted; + final l$torrentFilePath = torrentFilePath; + resultData['torrentFilePath'] = l$torrentFilePath; + final l$bytesMissing = bytesMissing; + resultData['bytesMissing'] = l$bytesMissing; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$infohash = infohash; + final l$bytesCompleted = bytesCompleted; + final l$torrentFilePath = torrentFilePath; + final l$bytesMissing = bytesMissing; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$infohash, + l$bytesCompleted, + l$torrentFilePath, + l$bytesMissing, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$TorrentFS$torrent || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (l$infohash != lOther$infohash) { + return false; + } + final l$bytesCompleted = bytesCompleted; + final lOther$bytesCompleted = other.bytesCompleted; + if (l$bytesCompleted != lOther$bytesCompleted) { + return false; + } + final l$torrentFilePath = torrentFilePath; + final lOther$torrentFilePath = other.torrentFilePath; + if (l$torrentFilePath != lOther$torrentFilePath) { + return false; + } + final l$bytesMissing = bytesMissing; + final lOther$bytesMissing = other.bytesMissing; + if (l$bytesMissing != lOther$bytesMissing) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$TorrentFS$torrent + on Query$ListDir$fsEntry$$TorrentFS$torrent { + CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent< + Query$ListDir$fsEntry$$TorrentFS$torrent> + get copyWith => CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent { + factory CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent( + Query$ListDir$fsEntry$$TorrentFS$torrent instance, + TRes Function(Query$ListDir$fsEntry$$TorrentFS$torrent) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS$torrent; + + factory CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS$torrent; + + TRes call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS$torrent + implements CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent { + _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFS$torrent( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$TorrentFS$torrent _instance; + + final TRes Function(Query$ListDir$fsEntry$$TorrentFS$torrent) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? infohash = _undefined, + Object? bytesCompleted = _undefined, + Object? torrentFilePath = _undefined, + Object? bytesMissing = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListDir$fsEntry$$TorrentFS$torrent( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + infohash: infohash == _undefined || infohash == null + ? _instance.infohash + : (infohash as String), + bytesCompleted: bytesCompleted == _undefined || bytesCompleted == null + ? _instance.bytesCompleted + : (bytesCompleted as int), + torrentFilePath: + torrentFilePath == _undefined || torrentFilePath == null + ? _instance.torrentFilePath + : (torrentFilePath as String), + bytesMissing: bytesMissing == _undefined || bytesMissing == null + ? _instance.bytesMissing + : (bytesMissing as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS$torrent + implements CopyWith$Query$ListDir$fsEntry$$TorrentFS$torrent { + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFS$torrent(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? infohash, + int? bytesCompleted, + String? torrentFilePath, + int? bytesMissing, + String? $__typename, + }) => + _res; +} + +class Query$ListDir$fsEntry$$TorrentFileEntry implements Query$ListDir$fsEntry { + Query$ListDir$fsEntry$$TorrentFileEntry({ + required this.name, + this.$__typename = 'TorrentFileEntry', + }); + + factory Query$ListDir$fsEntry$$TorrentFileEntry.fromJson( + Map json) { + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$ListDir$fsEntry$$TorrentFileEntry( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + @override + final String name; + + @override + final String $__typename; + + @override + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListDir$fsEntry$$TorrentFileEntry || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListDir$fsEntry$$TorrentFileEntry + on Query$ListDir$fsEntry$$TorrentFileEntry { + CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry< + Query$ListDir$fsEntry$$TorrentFileEntry> + get copyWith => CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry { + factory CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry( + Query$ListDir$fsEntry$$TorrentFileEntry instance, + TRes Function(Query$ListDir$fsEntry$$TorrentFileEntry) then, + ) = _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFileEntry; + + factory CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry.stub(TRes res) = + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFileEntry; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFileEntry + implements CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry { + _CopyWithImpl$Query$ListDir$fsEntry$$TorrentFileEntry( + this._instance, + this._then, + ); + + final Query$ListDir$fsEntry$$TorrentFileEntry _instance; + + final TRes Function(Query$ListDir$fsEntry$$TorrentFileEntry) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListDir$fsEntry$$TorrentFileEntry( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFileEntry + implements CopyWith$Query$ListDir$fsEntry$$TorrentFileEntry { + _CopyWithStubImpl$Query$ListDir$fsEntry$$TorrentFileEntry(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? $__typename, + }) => + _res; +} diff --git a/ui/lib/api/schema.graphql b/ui/lib/api/schema.graphql new file mode 100644 index 0000000..2d019a8 --- /dev/null +++ b/ui/lib/api/schema.graphql @@ -0,0 +1,138 @@ +directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION +directive @stream on FIELD_DEFINITION +type ArchiveFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! + size: Int! +} +input BooleanFilter @oneOf { + eq: Boolean +} +type CleanupResponse { + count: Int! + list: [String!]! +} +scalar DateTime +input DateTimeFilter @oneOf { + eq: DateTime + gt: DateTime + lt: DateTime + gte: DateTime + lte: DateTime +} +interface Dir implements FsEntry { + name: String! + entries: [FsEntry!]! +} +type DownloadTorrentResponse { + task: Task +} +interface File implements FsEntry { + name: String! + size: Int! +} +interface FsEntry { + name: String! +} +input IntFilter @oneOf { + eq: Int + gt: Int + lt: Int + gte: Int + lte: Int + in: [Int!] +} +type Mutation { + validateTorrents(filter: TorrentFilter!): Boolean! + cleanupTorrents(files: Boolean, dryRun: Boolean!): CleanupResponse! + downloadTorrent(infohash: String!, file: String): DownloadTorrentResponse + dedupeStorage: Int! +} +input Pagination { + offset: Int! + limit: Int! +} +interface Progress { + current: Int! + total: Int! +} +type Query { + torrents(filter: TorrentsFilter, pagination: Pagination): [Torrent!]! + fsEntry(path: String!): FsEntry +} +type ResolverFS implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! +} +type Schema { + query: Query + mutation: Mutation +} +type SimpleDir implements Dir & FsEntry { + name: String! + entries: [FsEntry!]! +} +type SimpleFile implements File & FsEntry { + name: String! + size: Int! +} +input StringFilter @oneOf { + eq: String + substr: String + in: [String!] +} +type Subscription { + taskProgress(taskID: ID!): Progress + torrentDownloadUpdates: TorrentProgress +} +type Task { + id: ID! +} +type Torrent { + name: String! + infohash: String! + bytesCompleted: Int! + torrentFilePath: String! + bytesMissing: Int! + files: [TorrentFile!]! + excludedFiles: [TorrentFile!]! + peers: [TorrentPeer!]! +} +type TorrentFS implements Dir & FsEntry { + name: String! + torrent: Torrent! + entries: [FsEntry!]! +} +type TorrentFile { + filename: String! + size: Int! + bytesCompleted: Int! +} +type TorrentFileEntry implements File & FsEntry { + name: String! + torrent: Torrent! + size: Int! +} +input TorrentFilter @oneOf { + everything: Boolean + infohash: String +} +type TorrentPeer { + ip: String! + downloadRate: Float! + discovery: String! + port: Int! + clientName: String! +} +type TorrentProgress implements Progress { + torrent: Torrent! + current: Int! + total: Int! +} +input TorrentsFilter { + infohash: StringFilter + name: StringFilter + bytesCompleted: IntFilter + bytesMissing: IntFilter + peersCount: IntFilter +} diff --git a/ui/lib/api/schema.graphql.dart b/ui/lib/api/schema.graphql.dart new file mode 100644 index 0000000..f9e0b80 --- /dev/null +++ b/ui/lib/api/schema.graphql.dart @@ -0,0 +1,1514 @@ +class Input$BooleanFilter { + factory Input$BooleanFilter({bool? eq}) => Input$BooleanFilter._({ + if (eq != null) r'eq': eq, + }); + + Input$BooleanFilter._(this._$data); + + factory Input$BooleanFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('eq')) { + final l$eq = data['eq']; + result$data['eq'] = (l$eq as bool?); + } + return Input$BooleanFilter._(result$data); + } + + Map _$data; + + bool? get eq => (_$data['eq'] as bool?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('eq')) { + final l$eq = eq; + result$data['eq'] = l$eq; + } + return result$data; + } + + CopyWith$Input$BooleanFilter get copyWith => + CopyWith$Input$BooleanFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$BooleanFilter || runtimeType != other.runtimeType) { + return false; + } + final l$eq = eq; + final lOther$eq = other.eq; + if (_$data.containsKey('eq') != other._$data.containsKey('eq')) { + return false; + } + if (l$eq != lOther$eq) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$eq = eq; + return Object.hashAll([_$data.containsKey('eq') ? l$eq : const {}]); + } +} + +abstract class CopyWith$Input$BooleanFilter { + factory CopyWith$Input$BooleanFilter( + Input$BooleanFilter instance, + TRes Function(Input$BooleanFilter) then, + ) = _CopyWithImpl$Input$BooleanFilter; + + factory CopyWith$Input$BooleanFilter.stub(TRes res) = + _CopyWithStubImpl$Input$BooleanFilter; + + TRes call({bool? eq}); +} + +class _CopyWithImpl$Input$BooleanFilter + implements CopyWith$Input$BooleanFilter { + _CopyWithImpl$Input$BooleanFilter( + this._instance, + this._then, + ); + + final Input$BooleanFilter _instance; + + final TRes Function(Input$BooleanFilter) _then; + + static const _undefined = {}; + + @override + TRes call({Object? eq = _undefined}) => _then(Input$BooleanFilter._({ + ..._instance._$data, + if (eq != _undefined) 'eq': (eq as bool?), + })); +} + +class _CopyWithStubImpl$Input$BooleanFilter + implements CopyWith$Input$BooleanFilter { + _CopyWithStubImpl$Input$BooleanFilter(this._res); + + final TRes _res; + + @override + call({bool? eq}) => _res; +} + +class Input$DateTimeFilter { + factory Input$DateTimeFilter({ + DateTime? eq, + DateTime? gt, + DateTime? lt, + DateTime? gte, + DateTime? lte, + }) => + Input$DateTimeFilter._({ + if (eq != null) r'eq': eq, + if (gt != null) r'gt': gt, + if (lt != null) r'lt': lt, + if (gte != null) r'gte': gte, + if (lte != null) r'lte': lte, + }); + + Input$DateTimeFilter._(this._$data); + + factory Input$DateTimeFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('eq')) { + final l$eq = data['eq']; + result$data['eq'] = + l$eq == null ? null : DateTime.parse((l$eq as String)); + } + if (data.containsKey('gt')) { + final l$gt = data['gt']; + result$data['gt'] = + l$gt == null ? null : DateTime.parse((l$gt as String)); + } + if (data.containsKey('lt')) { + final l$lt = data['lt']; + result$data['lt'] = + l$lt == null ? null : DateTime.parse((l$lt as String)); + } + if (data.containsKey('gte')) { + final l$gte = data['gte']; + result$data['gte'] = + l$gte == null ? null : DateTime.parse((l$gte as String)); + } + if (data.containsKey('lte')) { + final l$lte = data['lte']; + result$data['lte'] = + l$lte == null ? null : DateTime.parse((l$lte as String)); + } + return Input$DateTimeFilter._(result$data); + } + + Map _$data; + + DateTime? get eq => (_$data['eq'] as DateTime?); + + DateTime? get gt => (_$data['gt'] as DateTime?); + + DateTime? get lt => (_$data['lt'] as DateTime?); + + DateTime? get gte => (_$data['gte'] as DateTime?); + + DateTime? get lte => (_$data['lte'] as DateTime?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('eq')) { + final l$eq = eq; + result$data['eq'] = l$eq?.toIso8601String(); + } + if (_$data.containsKey('gt')) { + final l$gt = gt; + result$data['gt'] = l$gt?.toIso8601String(); + } + if (_$data.containsKey('lt')) { + final l$lt = lt; + result$data['lt'] = l$lt?.toIso8601String(); + } + if (_$data.containsKey('gte')) { + final l$gte = gte; + result$data['gte'] = l$gte?.toIso8601String(); + } + if (_$data.containsKey('lte')) { + final l$lte = lte; + result$data['lte'] = l$lte?.toIso8601String(); + } + return result$data; + } + + CopyWith$Input$DateTimeFilter get copyWith => + CopyWith$Input$DateTimeFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$DateTimeFilter || runtimeType != other.runtimeType) { + return false; + } + final l$eq = eq; + final lOther$eq = other.eq; + if (_$data.containsKey('eq') != other._$data.containsKey('eq')) { + return false; + } + if (l$eq != lOther$eq) { + return false; + } + final l$gt = gt; + final lOther$gt = other.gt; + if (_$data.containsKey('gt') != other._$data.containsKey('gt')) { + return false; + } + if (l$gt != lOther$gt) { + return false; + } + final l$lt = lt; + final lOther$lt = other.lt; + if (_$data.containsKey('lt') != other._$data.containsKey('lt')) { + return false; + } + if (l$lt != lOther$lt) { + return false; + } + final l$gte = gte; + final lOther$gte = other.gte; + if (_$data.containsKey('gte') != other._$data.containsKey('gte')) { + return false; + } + if (l$gte != lOther$gte) { + return false; + } + final l$lte = lte; + final lOther$lte = other.lte; + if (_$data.containsKey('lte') != other._$data.containsKey('lte')) { + return false; + } + if (l$lte != lOther$lte) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$eq = eq; + final l$gt = gt; + final l$lt = lt; + final l$gte = gte; + final l$lte = lte; + return Object.hashAll([ + _$data.containsKey('eq') ? l$eq : const {}, + _$data.containsKey('gt') ? l$gt : const {}, + _$data.containsKey('lt') ? l$lt : const {}, + _$data.containsKey('gte') ? l$gte : const {}, + _$data.containsKey('lte') ? l$lte : const {}, + ]); + } +} + +abstract class CopyWith$Input$DateTimeFilter { + factory CopyWith$Input$DateTimeFilter( + Input$DateTimeFilter instance, + TRes Function(Input$DateTimeFilter) then, + ) = _CopyWithImpl$Input$DateTimeFilter; + + factory CopyWith$Input$DateTimeFilter.stub(TRes res) = + _CopyWithStubImpl$Input$DateTimeFilter; + + TRes call({ + DateTime? eq, + DateTime? gt, + DateTime? lt, + DateTime? gte, + DateTime? lte, + }); +} + +class _CopyWithImpl$Input$DateTimeFilter + implements CopyWith$Input$DateTimeFilter { + _CopyWithImpl$Input$DateTimeFilter( + this._instance, + this._then, + ); + + final Input$DateTimeFilter _instance; + + final TRes Function(Input$DateTimeFilter) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? eq = _undefined, + Object? gt = _undefined, + Object? lt = _undefined, + Object? gte = _undefined, + Object? lte = _undefined, + }) => + _then(Input$DateTimeFilter._({ + ..._instance._$data, + if (eq != _undefined) 'eq': (eq as DateTime?), + if (gt != _undefined) 'gt': (gt as DateTime?), + if (lt != _undefined) 'lt': (lt as DateTime?), + if (gte != _undefined) 'gte': (gte as DateTime?), + if (lte != _undefined) 'lte': (lte as DateTime?), + })); +} + +class _CopyWithStubImpl$Input$DateTimeFilter + implements CopyWith$Input$DateTimeFilter { + _CopyWithStubImpl$Input$DateTimeFilter(this._res); + + final TRes _res; + + @override + call({ + DateTime? eq, + DateTime? gt, + DateTime? lt, + DateTime? gte, + DateTime? lte, + }) => + _res; +} + +class Input$IntFilter { + factory Input$IntFilter({ + int? eq, + int? gt, + int? lt, + int? gte, + int? lte, + List? $in, + }) => + Input$IntFilter._({ + if (eq != null) r'eq': eq, + if (gt != null) r'gt': gt, + if (lt != null) r'lt': lt, + if (gte != null) r'gte': gte, + if (lte != null) r'lte': lte, + if ($in != null) r'in': $in, + }); + + Input$IntFilter._(this._$data); + + factory Input$IntFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('eq')) { + final l$eq = data['eq']; + result$data['eq'] = (l$eq as int?); + } + if (data.containsKey('gt')) { + final l$gt = data['gt']; + result$data['gt'] = (l$gt as int?); + } + if (data.containsKey('lt')) { + final l$lt = data['lt']; + result$data['lt'] = (l$lt as int?); + } + if (data.containsKey('gte')) { + final l$gte = data['gte']; + result$data['gte'] = (l$gte as int?); + } + if (data.containsKey('lte')) { + final l$lte = data['lte']; + result$data['lte'] = (l$lte as int?); + } + if (data.containsKey('in')) { + final l$$in = data['in']; + result$data['in'] = + (l$$in as List?)?.map((e) => (e as int)).toList(); + } + return Input$IntFilter._(result$data); + } + + Map _$data; + + int? get eq => (_$data['eq'] as int?); + + int? get gt => (_$data['gt'] as int?); + + int? get lt => (_$data['lt'] as int?); + + int? get gte => (_$data['gte'] as int?); + + int? get lte => (_$data['lte'] as int?); + + List? get $in => (_$data['in'] as List?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('eq')) { + final l$eq = eq; + result$data['eq'] = l$eq; + } + if (_$data.containsKey('gt')) { + final l$gt = gt; + result$data['gt'] = l$gt; + } + if (_$data.containsKey('lt')) { + final l$lt = lt; + result$data['lt'] = l$lt; + } + if (_$data.containsKey('gte')) { + final l$gte = gte; + result$data['gte'] = l$gte; + } + if (_$data.containsKey('lte')) { + final l$lte = lte; + result$data['lte'] = l$lte; + } + if (_$data.containsKey('in')) { + final l$$in = $in; + result$data['in'] = l$$in?.map((e) => e).toList(); + } + return result$data; + } + + CopyWith$Input$IntFilter get copyWith => + CopyWith$Input$IntFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$IntFilter || runtimeType != other.runtimeType) { + return false; + } + final l$eq = eq; + final lOther$eq = other.eq; + if (_$data.containsKey('eq') != other._$data.containsKey('eq')) { + return false; + } + if (l$eq != lOther$eq) { + return false; + } + final l$gt = gt; + final lOther$gt = other.gt; + if (_$data.containsKey('gt') != other._$data.containsKey('gt')) { + return false; + } + if (l$gt != lOther$gt) { + return false; + } + final l$lt = lt; + final lOther$lt = other.lt; + if (_$data.containsKey('lt') != other._$data.containsKey('lt')) { + return false; + } + if (l$lt != lOther$lt) { + return false; + } + final l$gte = gte; + final lOther$gte = other.gte; + if (_$data.containsKey('gte') != other._$data.containsKey('gte')) { + return false; + } + if (l$gte != lOther$gte) { + return false; + } + final l$lte = lte; + final lOther$lte = other.lte; + if (_$data.containsKey('lte') != other._$data.containsKey('lte')) { + return false; + } + if (l$lte != lOther$lte) { + return false; + } + final l$$in = $in; + final lOther$$in = other.$in; + if (_$data.containsKey('in') != other._$data.containsKey('in')) { + return false; + } + if (l$$in != null && lOther$$in != null) { + if (l$$in.length != lOther$$in.length) { + return false; + } + for (int i = 0; i < l$$in.length; i++) { + final l$$in$entry = l$$in[i]; + final lOther$$in$entry = lOther$$in[i]; + if (l$$in$entry != lOther$$in$entry) { + return false; + } + } + } else if (l$$in != lOther$$in) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$eq = eq; + final l$gt = gt; + final l$lt = lt; + final l$gte = gte; + final l$lte = lte; + final l$$in = $in; + return Object.hashAll([ + _$data.containsKey('eq') ? l$eq : const {}, + _$data.containsKey('gt') ? l$gt : const {}, + _$data.containsKey('lt') ? l$lt : const {}, + _$data.containsKey('gte') ? l$gte : const {}, + _$data.containsKey('lte') ? l$lte : const {}, + _$data.containsKey('in') + ? l$$in == null + ? null + : Object.hashAll(l$$in.map((v) => v)) + : const {}, + ]); + } +} + +abstract class CopyWith$Input$IntFilter { + factory CopyWith$Input$IntFilter( + Input$IntFilter instance, + TRes Function(Input$IntFilter) then, + ) = _CopyWithImpl$Input$IntFilter; + + factory CopyWith$Input$IntFilter.stub(TRes res) = + _CopyWithStubImpl$Input$IntFilter; + + TRes call({ + int? eq, + int? gt, + int? lt, + int? gte, + int? lte, + List? $in, + }); +} + +class _CopyWithImpl$Input$IntFilter + implements CopyWith$Input$IntFilter { + _CopyWithImpl$Input$IntFilter( + this._instance, + this._then, + ); + + final Input$IntFilter _instance; + + final TRes Function(Input$IntFilter) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? eq = _undefined, + Object? gt = _undefined, + Object? lt = _undefined, + Object? gte = _undefined, + Object? lte = _undefined, + Object? $in = _undefined, + }) => + _then(Input$IntFilter._({ + ..._instance._$data, + if (eq != _undefined) 'eq': (eq as int?), + if (gt != _undefined) 'gt': (gt as int?), + if (lt != _undefined) 'lt': (lt as int?), + if (gte != _undefined) 'gte': (gte as int?), + if (lte != _undefined) 'lte': (lte as int?), + if ($in != _undefined) 'in': ($in as List?), + })); +} + +class _CopyWithStubImpl$Input$IntFilter + implements CopyWith$Input$IntFilter { + _CopyWithStubImpl$Input$IntFilter(this._res); + + final TRes _res; + + @override + call({ + int? eq, + int? gt, + int? lt, + int? gte, + int? lte, + List? $in, + }) => + _res; +} + +class Input$Pagination { + factory Input$Pagination({ + required int offset, + required int limit, + }) => + Input$Pagination._({ + r'offset': offset, + r'limit': limit, + }); + + Input$Pagination._(this._$data); + + factory Input$Pagination.fromJson(Map data) { + final result$data = {}; + final l$offset = data['offset']; + result$data['offset'] = (l$offset as int); + final l$limit = data['limit']; + result$data['limit'] = (l$limit as int); + return Input$Pagination._(result$data); + } + + Map _$data; + + int get offset => (_$data['offset'] as int); + + int get limit => (_$data['limit'] as int); + + Map toJson() { + final result$data = {}; + final l$offset = offset; + result$data['offset'] = l$offset; + final l$limit = limit; + result$data['limit'] = l$limit; + return result$data; + } + + CopyWith$Input$Pagination get copyWith => + CopyWith$Input$Pagination( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$Pagination || runtimeType != other.runtimeType) { + return false; + } + final l$offset = offset; + final lOther$offset = other.offset; + if (l$offset != lOther$offset) { + return false; + } + final l$limit = limit; + final lOther$limit = other.limit; + if (l$limit != lOther$limit) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$offset = offset; + final l$limit = limit; + return Object.hashAll([ + l$offset, + l$limit, + ]); + } +} + +abstract class CopyWith$Input$Pagination { + factory CopyWith$Input$Pagination( + Input$Pagination instance, + TRes Function(Input$Pagination) then, + ) = _CopyWithImpl$Input$Pagination; + + factory CopyWith$Input$Pagination.stub(TRes res) = + _CopyWithStubImpl$Input$Pagination; + + TRes call({ + int? offset, + int? limit, + }); +} + +class _CopyWithImpl$Input$Pagination + implements CopyWith$Input$Pagination { + _CopyWithImpl$Input$Pagination( + this._instance, + this._then, + ); + + final Input$Pagination _instance; + + final TRes Function(Input$Pagination) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? offset = _undefined, + Object? limit = _undefined, + }) => + _then(Input$Pagination._({ + ..._instance._$data, + if (offset != _undefined && offset != null) 'offset': (offset as int), + if (limit != _undefined && limit != null) 'limit': (limit as int), + })); +} + +class _CopyWithStubImpl$Input$Pagination + implements CopyWith$Input$Pagination { + _CopyWithStubImpl$Input$Pagination(this._res); + + final TRes _res; + + @override + call({ + int? offset, + int? limit, + }) => + _res; +} + +class Input$StringFilter { + factory Input$StringFilter({ + String? eq, + String? substr, + List? $in, + }) => + Input$StringFilter._({ + if (eq != null) r'eq': eq, + if (substr != null) r'substr': substr, + if ($in != null) r'in': $in, + }); + + Input$StringFilter._(this._$data); + + factory Input$StringFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('eq')) { + final l$eq = data['eq']; + result$data['eq'] = (l$eq as String?); + } + if (data.containsKey('substr')) { + final l$substr = data['substr']; + result$data['substr'] = (l$substr as String?); + } + if (data.containsKey('in')) { + final l$$in = data['in']; + result$data['in'] = + (l$$in as List?)?.map((e) => (e as String)).toList(); + } + return Input$StringFilter._(result$data); + } + + Map _$data; + + String? get eq => (_$data['eq'] as String?); + + String? get substr => (_$data['substr'] as String?); + + List? get $in => (_$data['in'] as List?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('eq')) { + final l$eq = eq; + result$data['eq'] = l$eq; + } + if (_$data.containsKey('substr')) { + final l$substr = substr; + result$data['substr'] = l$substr; + } + if (_$data.containsKey('in')) { + final l$$in = $in; + result$data['in'] = l$$in?.map((e) => e).toList(); + } + return result$data; + } + + CopyWith$Input$StringFilter get copyWith => + CopyWith$Input$StringFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$StringFilter || runtimeType != other.runtimeType) { + return false; + } + final l$eq = eq; + final lOther$eq = other.eq; + if (_$data.containsKey('eq') != other._$data.containsKey('eq')) { + return false; + } + if (l$eq != lOther$eq) { + return false; + } + final l$substr = substr; + final lOther$substr = other.substr; + if (_$data.containsKey('substr') != other._$data.containsKey('substr')) { + return false; + } + if (l$substr != lOther$substr) { + return false; + } + final l$$in = $in; + final lOther$$in = other.$in; + if (_$data.containsKey('in') != other._$data.containsKey('in')) { + return false; + } + if (l$$in != null && lOther$$in != null) { + if (l$$in.length != lOther$$in.length) { + return false; + } + for (int i = 0; i < l$$in.length; i++) { + final l$$in$entry = l$$in[i]; + final lOther$$in$entry = lOther$$in[i]; + if (l$$in$entry != lOther$$in$entry) { + return false; + } + } + } else if (l$$in != lOther$$in) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$eq = eq; + final l$substr = substr; + final l$$in = $in; + return Object.hashAll([ + _$data.containsKey('eq') ? l$eq : const {}, + _$data.containsKey('substr') ? l$substr : const {}, + _$data.containsKey('in') + ? l$$in == null + ? null + : Object.hashAll(l$$in.map((v) => v)) + : const {}, + ]); + } +} + +abstract class CopyWith$Input$StringFilter { + factory CopyWith$Input$StringFilter( + Input$StringFilter instance, + TRes Function(Input$StringFilter) then, + ) = _CopyWithImpl$Input$StringFilter; + + factory CopyWith$Input$StringFilter.stub(TRes res) = + _CopyWithStubImpl$Input$StringFilter; + + TRes call({ + String? eq, + String? substr, + List? $in, + }); +} + +class _CopyWithImpl$Input$StringFilter + implements CopyWith$Input$StringFilter { + _CopyWithImpl$Input$StringFilter( + this._instance, + this._then, + ); + + final Input$StringFilter _instance; + + final TRes Function(Input$StringFilter) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? eq = _undefined, + Object? substr = _undefined, + Object? $in = _undefined, + }) => + _then(Input$StringFilter._({ + ..._instance._$data, + if (eq != _undefined) 'eq': (eq as String?), + if (substr != _undefined) 'substr': (substr as String?), + if ($in != _undefined) 'in': ($in as List?), + })); +} + +class _CopyWithStubImpl$Input$StringFilter + implements CopyWith$Input$StringFilter { + _CopyWithStubImpl$Input$StringFilter(this._res); + + final TRes _res; + + @override + call({ + String? eq, + String? substr, + List? $in, + }) => + _res; +} + +class Input$TorrentFilter { + factory Input$TorrentFilter({ + bool? everything, + String? infohash, + }) => + Input$TorrentFilter._({ + if (everything != null) r'everything': everything, + if (infohash != null) r'infohash': infohash, + }); + + Input$TorrentFilter._(this._$data); + + factory Input$TorrentFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('everything')) { + final l$everything = data['everything']; + result$data['everything'] = (l$everything as bool?); + } + if (data.containsKey('infohash')) { + final l$infohash = data['infohash']; + result$data['infohash'] = (l$infohash as String?); + } + return Input$TorrentFilter._(result$data); + } + + Map _$data; + + bool? get everything => (_$data['everything'] as bool?); + + String? get infohash => (_$data['infohash'] as String?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('everything')) { + final l$everything = everything; + result$data['everything'] = l$everything; + } + if (_$data.containsKey('infohash')) { + final l$infohash = infohash; + result$data['infohash'] = l$infohash; + } + return result$data; + } + + CopyWith$Input$TorrentFilter get copyWith => + CopyWith$Input$TorrentFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$TorrentFilter || runtimeType != other.runtimeType) { + return false; + } + final l$everything = everything; + final lOther$everything = other.everything; + if (_$data.containsKey('everything') != + other._$data.containsKey('everything')) { + return false; + } + if (l$everything != lOther$everything) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (_$data.containsKey('infohash') != + other._$data.containsKey('infohash')) { + return false; + } + if (l$infohash != lOther$infohash) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$everything = everything; + final l$infohash = infohash; + return Object.hashAll([ + _$data.containsKey('everything') ? l$everything : const {}, + _$data.containsKey('infohash') ? l$infohash : const {}, + ]); + } +} + +abstract class CopyWith$Input$TorrentFilter { + factory CopyWith$Input$TorrentFilter( + Input$TorrentFilter instance, + TRes Function(Input$TorrentFilter) then, + ) = _CopyWithImpl$Input$TorrentFilter; + + factory CopyWith$Input$TorrentFilter.stub(TRes res) = + _CopyWithStubImpl$Input$TorrentFilter; + + TRes call({ + bool? everything, + String? infohash, + }); +} + +class _CopyWithImpl$Input$TorrentFilter + implements CopyWith$Input$TorrentFilter { + _CopyWithImpl$Input$TorrentFilter( + this._instance, + this._then, + ); + + final Input$TorrentFilter _instance; + + final TRes Function(Input$TorrentFilter) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? everything = _undefined, + Object? infohash = _undefined, + }) => + _then(Input$TorrentFilter._({ + ..._instance._$data, + if (everything != _undefined) 'everything': (everything as bool?), + if (infohash != _undefined) 'infohash': (infohash as String?), + })); +} + +class _CopyWithStubImpl$Input$TorrentFilter + implements CopyWith$Input$TorrentFilter { + _CopyWithStubImpl$Input$TorrentFilter(this._res); + + final TRes _res; + + @override + call({ + bool? everything, + String? infohash, + }) => + _res; +} + +class Input$TorrentsFilter { + factory Input$TorrentsFilter({ + Input$StringFilter? name, + Input$IntFilter? bytesCompleted, + Input$IntFilter? bytesMissing, + Input$IntFilter? peersCount, + }) => + Input$TorrentsFilter._({ + if (name != null) r'name': name, + if (bytesCompleted != null) r'bytesCompleted': bytesCompleted, + if (bytesMissing != null) r'bytesMissing': bytesMissing, + if (peersCount != null) r'peersCount': peersCount, + }); + + Input$TorrentsFilter._(this._$data); + + factory Input$TorrentsFilter.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('name')) { + final l$name = data['name']; + result$data['name'] = l$name == null + ? null + : Input$StringFilter.fromJson((l$name as Map)); + } + if (data.containsKey('bytesCompleted')) { + final l$bytesCompleted = data['bytesCompleted']; + result$data['bytesCompleted'] = l$bytesCompleted == null + ? null + : Input$IntFilter.fromJson( + (l$bytesCompleted as Map)); + } + if (data.containsKey('bytesMissing')) { + final l$bytesMissing = data['bytesMissing']; + result$data['bytesMissing'] = l$bytesMissing == null + ? null + : Input$IntFilter.fromJson((l$bytesMissing as Map)); + } + if (data.containsKey('peersCount')) { + final l$peersCount = data['peersCount']; + result$data['peersCount'] = l$peersCount == null + ? null + : Input$IntFilter.fromJson((l$peersCount as Map)); + } + return Input$TorrentsFilter._(result$data); + } + + Map _$data; + + Input$StringFilter? get name => (_$data['name'] as Input$StringFilter?); + + Input$IntFilter? get bytesCompleted => + (_$data['bytesCompleted'] as Input$IntFilter?); + + Input$IntFilter? get bytesMissing => + (_$data['bytesMissing'] as Input$IntFilter?); + + Input$IntFilter? get peersCount => (_$data['peersCount'] as Input$IntFilter?); + + Map toJson() { + final result$data = {}; + if (_$data.containsKey('name')) { + final l$name = name; + result$data['name'] = l$name?.toJson(); + } + if (_$data.containsKey('bytesCompleted')) { + final l$bytesCompleted = bytesCompleted; + result$data['bytesCompleted'] = l$bytesCompleted?.toJson(); + } + if (_$data.containsKey('bytesMissing')) { + final l$bytesMissing = bytesMissing; + result$data['bytesMissing'] = l$bytesMissing?.toJson(); + } + if (_$data.containsKey('peersCount')) { + final l$peersCount = peersCount; + result$data['peersCount'] = l$peersCount?.toJson(); + } + return result$data; + } + + CopyWith$Input$TorrentsFilter get copyWith => + CopyWith$Input$TorrentsFilter( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Input$TorrentsFilter || runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (_$data.containsKey('name') != other._$data.containsKey('name')) { + return false; + } + if (l$name != lOther$name) { + return false; + } + final l$bytesCompleted = bytesCompleted; + final lOther$bytesCompleted = other.bytesCompleted; + if (_$data.containsKey('bytesCompleted') != + other._$data.containsKey('bytesCompleted')) { + return false; + } + if (l$bytesCompleted != lOther$bytesCompleted) { + return false; + } + final l$bytesMissing = bytesMissing; + final lOther$bytesMissing = other.bytesMissing; + if (_$data.containsKey('bytesMissing') != + other._$data.containsKey('bytesMissing')) { + return false; + } + if (l$bytesMissing != lOther$bytesMissing) { + return false; + } + final l$peersCount = peersCount; + final lOther$peersCount = other.peersCount; + if (_$data.containsKey('peersCount') != + other._$data.containsKey('peersCount')) { + return false; + } + if (l$peersCount != lOther$peersCount) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$name = name; + final l$bytesCompleted = bytesCompleted; + final l$bytesMissing = bytesMissing; + final l$peersCount = peersCount; + return Object.hashAll([ + _$data.containsKey('name') ? l$name : const {}, + _$data.containsKey('bytesCompleted') ? l$bytesCompleted : const {}, + _$data.containsKey('bytesMissing') ? l$bytesMissing : const {}, + _$data.containsKey('peersCount') ? l$peersCount : const {}, + ]); + } +} + +abstract class CopyWith$Input$TorrentsFilter { + factory CopyWith$Input$TorrentsFilter( + Input$TorrentsFilter instance, + TRes Function(Input$TorrentsFilter) then, + ) = _CopyWithImpl$Input$TorrentsFilter; + + factory CopyWith$Input$TorrentsFilter.stub(TRes res) = + _CopyWithStubImpl$Input$TorrentsFilter; + + TRes call({ + Input$StringFilter? name, + Input$IntFilter? bytesCompleted, + Input$IntFilter? bytesMissing, + Input$IntFilter? peersCount, + }); + CopyWith$Input$StringFilter get name; + CopyWith$Input$IntFilter get bytesCompleted; + CopyWith$Input$IntFilter get bytesMissing; + CopyWith$Input$IntFilter get peersCount; +} + +class _CopyWithImpl$Input$TorrentsFilter + implements CopyWith$Input$TorrentsFilter { + _CopyWithImpl$Input$TorrentsFilter( + this._instance, + this._then, + ); + + final Input$TorrentsFilter _instance; + + final TRes Function(Input$TorrentsFilter) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? bytesCompleted = _undefined, + Object? bytesMissing = _undefined, + Object? peersCount = _undefined, + }) => + _then(Input$TorrentsFilter._({ + ..._instance._$data, + if (name != _undefined) 'name': (name as Input$StringFilter?), + if (bytesCompleted != _undefined) + 'bytesCompleted': (bytesCompleted as Input$IntFilter?), + if (bytesMissing != _undefined) + 'bytesMissing': (bytesMissing as Input$IntFilter?), + if (peersCount != _undefined) + 'peersCount': (peersCount as Input$IntFilter?), + })); + + @override + CopyWith$Input$StringFilter get name { + final local$name = _instance.name; + return local$name == null + ? CopyWith$Input$StringFilter.stub(_then(_instance)) + : CopyWith$Input$StringFilter(local$name, (e) => call(name: e)); + } + + @override + CopyWith$Input$IntFilter get bytesCompleted { + final local$bytesCompleted = _instance.bytesCompleted; + return local$bytesCompleted == null + ? CopyWith$Input$IntFilter.stub(_then(_instance)) + : CopyWith$Input$IntFilter( + local$bytesCompleted, (e) => call(bytesCompleted: e)); + } + + @override + CopyWith$Input$IntFilter get bytesMissing { + final local$bytesMissing = _instance.bytesMissing; + return local$bytesMissing == null + ? CopyWith$Input$IntFilter.stub(_then(_instance)) + : CopyWith$Input$IntFilter( + local$bytesMissing, (e) => call(bytesMissing: e)); + } + + @override + CopyWith$Input$IntFilter get peersCount { + final local$peersCount = _instance.peersCount; + return local$peersCount == null + ? CopyWith$Input$IntFilter.stub(_then(_instance)) + : CopyWith$Input$IntFilter( + local$peersCount, (e) => call(peersCount: e)); + } +} + +class _CopyWithStubImpl$Input$TorrentsFilter + implements CopyWith$Input$TorrentsFilter { + _CopyWithStubImpl$Input$TorrentsFilter(this._res); + + final TRes _res; + + @override + call({ + Input$StringFilter? name, + Input$IntFilter? bytesCompleted, + Input$IntFilter? bytesMissing, + Input$IntFilter? peersCount, + }) => + _res; + + @override + CopyWith$Input$StringFilter get name => + CopyWith$Input$StringFilter.stub(_res); + + @override + CopyWith$Input$IntFilter get bytesCompleted => + CopyWith$Input$IntFilter.stub(_res); + + @override + CopyWith$Input$IntFilter get bytesMissing => + CopyWith$Input$IntFilter.stub(_res); + + @override + CopyWith$Input$IntFilter get peersCount => + CopyWith$Input$IntFilter.stub(_res); +} + +enum Enum$__TypeKind { + SCALAR, + OBJECT, + INTERFACE, + UNION, + ENUM, + INPUT_OBJECT, + LIST, + NON_NULL, + $unknown; + + factory Enum$__TypeKind.fromJson(String value) => + fromJson$Enum$__TypeKind(value); + + String toJson() => toJson$Enum$__TypeKind(this); +} + +String toJson$Enum$__TypeKind(Enum$__TypeKind e) { + switch (e) { + case Enum$__TypeKind.SCALAR: + return r'SCALAR'; + case Enum$__TypeKind.OBJECT: + return r'OBJECT'; + case Enum$__TypeKind.INTERFACE: + return r'INTERFACE'; + case Enum$__TypeKind.UNION: + return r'UNION'; + case Enum$__TypeKind.ENUM: + return r'ENUM'; + case Enum$__TypeKind.INPUT_OBJECT: + return r'INPUT_OBJECT'; + case Enum$__TypeKind.LIST: + return r'LIST'; + case Enum$__TypeKind.NON_NULL: + return r'NON_NULL'; + case Enum$__TypeKind.$unknown: + return r'$unknown'; + } +} + +Enum$__TypeKind fromJson$Enum$__TypeKind(String value) { + switch (value) { + case r'SCALAR': + return Enum$__TypeKind.SCALAR; + case r'OBJECT': + return Enum$__TypeKind.OBJECT; + case r'INTERFACE': + return Enum$__TypeKind.INTERFACE; + case r'UNION': + return Enum$__TypeKind.UNION; + case r'ENUM': + return Enum$__TypeKind.ENUM; + case r'INPUT_OBJECT': + return Enum$__TypeKind.INPUT_OBJECT; + case r'LIST': + return Enum$__TypeKind.LIST; + case r'NON_NULL': + return Enum$__TypeKind.NON_NULL; + default: + return Enum$__TypeKind.$unknown; + } +} + +enum Enum$__DirectiveLocation { + QUERY, + MUTATION, + SUBSCRIPTION, + FIELD, + FRAGMENT_DEFINITION, + FRAGMENT_SPREAD, + INLINE_FRAGMENT, + VARIABLE_DEFINITION, + SCHEMA, + SCALAR, + OBJECT, + FIELD_DEFINITION, + ARGUMENT_DEFINITION, + INTERFACE, + UNION, + ENUM, + ENUM_VALUE, + INPUT_OBJECT, + INPUT_FIELD_DEFINITION, + $unknown; + + factory Enum$__DirectiveLocation.fromJson(String value) => + fromJson$Enum$__DirectiveLocation(value); + + String toJson() => toJson$Enum$__DirectiveLocation(this); +} + +String toJson$Enum$__DirectiveLocation(Enum$__DirectiveLocation e) { + switch (e) { + case Enum$__DirectiveLocation.QUERY: + return r'QUERY'; + case Enum$__DirectiveLocation.MUTATION: + return r'MUTATION'; + case Enum$__DirectiveLocation.SUBSCRIPTION: + return r'SUBSCRIPTION'; + case Enum$__DirectiveLocation.FIELD: + return r'FIELD'; + case Enum$__DirectiveLocation.FRAGMENT_DEFINITION: + return r'FRAGMENT_DEFINITION'; + case Enum$__DirectiveLocation.FRAGMENT_SPREAD: + return r'FRAGMENT_SPREAD'; + case Enum$__DirectiveLocation.INLINE_FRAGMENT: + return r'INLINE_FRAGMENT'; + case Enum$__DirectiveLocation.VARIABLE_DEFINITION: + return r'VARIABLE_DEFINITION'; + case Enum$__DirectiveLocation.SCHEMA: + return r'SCHEMA'; + case Enum$__DirectiveLocation.SCALAR: + return r'SCALAR'; + case Enum$__DirectiveLocation.OBJECT: + return r'OBJECT'; + case Enum$__DirectiveLocation.FIELD_DEFINITION: + return r'FIELD_DEFINITION'; + case Enum$__DirectiveLocation.ARGUMENT_DEFINITION: + return r'ARGUMENT_DEFINITION'; + case Enum$__DirectiveLocation.INTERFACE: + return r'INTERFACE'; + case Enum$__DirectiveLocation.UNION: + return r'UNION'; + case Enum$__DirectiveLocation.ENUM: + return r'ENUM'; + case Enum$__DirectiveLocation.ENUM_VALUE: + return r'ENUM_VALUE'; + case Enum$__DirectiveLocation.INPUT_OBJECT: + return r'INPUT_OBJECT'; + case Enum$__DirectiveLocation.INPUT_FIELD_DEFINITION: + return r'INPUT_FIELD_DEFINITION'; + case Enum$__DirectiveLocation.$unknown: + return r'$unknown'; + } +} + +Enum$__DirectiveLocation fromJson$Enum$__DirectiveLocation(String value) { + switch (value) { + case r'QUERY': + return Enum$__DirectiveLocation.QUERY; + case r'MUTATION': + return Enum$__DirectiveLocation.MUTATION; + case r'SUBSCRIPTION': + return Enum$__DirectiveLocation.SUBSCRIPTION; + case r'FIELD': + return Enum$__DirectiveLocation.FIELD; + case r'FRAGMENT_DEFINITION': + return Enum$__DirectiveLocation.FRAGMENT_DEFINITION; + case r'FRAGMENT_SPREAD': + return Enum$__DirectiveLocation.FRAGMENT_SPREAD; + case r'INLINE_FRAGMENT': + return Enum$__DirectiveLocation.INLINE_FRAGMENT; + case r'VARIABLE_DEFINITION': + return Enum$__DirectiveLocation.VARIABLE_DEFINITION; + case r'SCHEMA': + return Enum$__DirectiveLocation.SCHEMA; + case r'SCALAR': + return Enum$__DirectiveLocation.SCALAR; + case r'OBJECT': + return Enum$__DirectiveLocation.OBJECT; + case r'FIELD_DEFINITION': + return Enum$__DirectiveLocation.FIELD_DEFINITION; + case r'ARGUMENT_DEFINITION': + return Enum$__DirectiveLocation.ARGUMENT_DEFINITION; + case r'INTERFACE': + return Enum$__DirectiveLocation.INTERFACE; + case r'UNION': + return Enum$__DirectiveLocation.UNION; + case r'ENUM': + return Enum$__DirectiveLocation.ENUM; + case r'ENUM_VALUE': + return Enum$__DirectiveLocation.ENUM_VALUE; + case r'INPUT_OBJECT': + return Enum$__DirectiveLocation.INPUT_OBJECT; + case r'INPUT_FIELD_DEFINITION': + return Enum$__DirectiveLocation.INPUT_FIELD_DEFINITION; + default: + return Enum$__DirectiveLocation.$unknown; + } +} + +const possibleTypesMap = >{ + 'Dir': { + 'ArchiveFS', + 'ResolverFS', + 'SimpleDir', + 'TorrentFS', + }, + 'FsEntry': { + 'ArchiveFS', + 'ResolverFS', + 'SimpleDir', + 'SimpleFile', + 'TorrentFS', + 'TorrentFileEntry', + }, + 'File': { + 'SimpleFile', + 'TorrentFileEntry', + }, + 'Progress': {'TorrentProgress'}, +}; diff --git a/ui/lib/api/torrent.graphql b/ui/lib/api/torrent.graphql new file mode 100644 index 0000000..25be873 --- /dev/null +++ b/ui/lib/api/torrent.graphql @@ -0,0 +1,16 @@ +mutation MarkTorrentDownload($infohash: String!) { + downloadTorrent(infohash: $infohash) { + task { + id + } + } +} + +query ListTorrents { + torrents { + name + infohash + bytesCompleted + bytesMissing + } +} \ No newline at end of file diff --git a/ui/lib/api/torrent.graphql.dart b/ui/lib/api/torrent.graphql.dart new file mode 100644 index 0000000..8b707af --- /dev/null +++ b/ui/lib/api/torrent.graphql.dart @@ -0,0 +1,1323 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + +class Variables$Mutation$MarkTorrentDownload { + factory Variables$Mutation$MarkTorrentDownload({required String infohash}) => + Variables$Mutation$MarkTorrentDownload._({ + r'infohash': infohash, + }); + + Variables$Mutation$MarkTorrentDownload._(this._$data); + + factory Variables$Mutation$MarkTorrentDownload.fromJson( + Map data) { + final result$data = {}; + final l$infohash = data['infohash']; + result$data['infohash'] = (l$infohash as String); + return Variables$Mutation$MarkTorrentDownload._(result$data); + } + + Map _$data; + + String get infohash => (_$data['infohash'] as String); + + Map toJson() { + final result$data = {}; + final l$infohash = infohash; + result$data['infohash'] = l$infohash; + return result$data; + } + + CopyWith$Variables$Mutation$MarkTorrentDownload< + Variables$Mutation$MarkTorrentDownload> + get copyWith => CopyWith$Variables$Mutation$MarkTorrentDownload( + this, + (i) => i, + ); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Variables$Mutation$MarkTorrentDownload || + runtimeType != other.runtimeType) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (l$infohash != lOther$infohash) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$infohash = infohash; + return Object.hashAll([l$infohash]); + } +} + +abstract class CopyWith$Variables$Mutation$MarkTorrentDownload { + factory CopyWith$Variables$Mutation$MarkTorrentDownload( + Variables$Mutation$MarkTorrentDownload instance, + TRes Function(Variables$Mutation$MarkTorrentDownload) then, + ) = _CopyWithImpl$Variables$Mutation$MarkTorrentDownload; + + factory CopyWith$Variables$Mutation$MarkTorrentDownload.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$MarkTorrentDownload; + + TRes call({String? infohash}); +} + +class _CopyWithImpl$Variables$Mutation$MarkTorrentDownload + implements CopyWith$Variables$Mutation$MarkTorrentDownload { + _CopyWithImpl$Variables$Mutation$MarkTorrentDownload( + this._instance, + this._then, + ); + + final Variables$Mutation$MarkTorrentDownload _instance; + + final TRes Function(Variables$Mutation$MarkTorrentDownload) _then; + + static const _undefined = {}; + + @override + TRes call({Object? infohash = _undefined}) => + _then(Variables$Mutation$MarkTorrentDownload._({ + ..._instance._$data, + if (infohash != _undefined && infohash != null) + 'infohash': (infohash as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$MarkTorrentDownload + implements CopyWith$Variables$Mutation$MarkTorrentDownload { + _CopyWithStubImpl$Variables$Mutation$MarkTorrentDownload(this._res); + + final TRes _res; + + @override + call({String? infohash}) => _res; +} + +class Mutation$MarkTorrentDownload { + Mutation$MarkTorrentDownload({ + this.downloadTorrent, + this.$__typename = 'Mutation', + }); + + factory Mutation$MarkTorrentDownload.fromJson(Map json) { + final l$downloadTorrent = json['downloadTorrent']; + final l$$__typename = json['__typename']; + return Mutation$MarkTorrentDownload( + downloadTorrent: l$downloadTorrent == null + ? null + : Mutation$MarkTorrentDownload$downloadTorrent.fromJson( + (l$downloadTorrent as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$MarkTorrentDownload$downloadTorrent? downloadTorrent; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$downloadTorrent = downloadTorrent; + resultData['downloadTorrent'] = l$downloadTorrent?.toJson(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$downloadTorrent = downloadTorrent; + final l$$__typename = $__typename; + return Object.hashAll([ + l$downloadTorrent, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Mutation$MarkTorrentDownload || + runtimeType != other.runtimeType) { + return false; + } + final l$downloadTorrent = downloadTorrent; + final lOther$downloadTorrent = other.downloadTorrent; + if (l$downloadTorrent != lOther$downloadTorrent) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MarkTorrentDownload + on Mutation$MarkTorrentDownload { + CopyWith$Mutation$MarkTorrentDownload + get copyWith => CopyWith$Mutation$MarkTorrentDownload( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MarkTorrentDownload { + factory CopyWith$Mutation$MarkTorrentDownload( + Mutation$MarkTorrentDownload instance, + TRes Function(Mutation$MarkTorrentDownload) then, + ) = _CopyWithImpl$Mutation$MarkTorrentDownload; + + factory CopyWith$Mutation$MarkTorrentDownload.stub(TRes res) = + _CopyWithStubImpl$Mutation$MarkTorrentDownload; + + TRes call({ + Mutation$MarkTorrentDownload$downloadTorrent? downloadTorrent, + String? $__typename, + }); + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent + get downloadTorrent; +} + +class _CopyWithImpl$Mutation$MarkTorrentDownload + implements CopyWith$Mutation$MarkTorrentDownload { + _CopyWithImpl$Mutation$MarkTorrentDownload( + this._instance, + this._then, + ); + + final Mutation$MarkTorrentDownload _instance; + + final TRes Function(Mutation$MarkTorrentDownload) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? downloadTorrent = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MarkTorrentDownload( + downloadTorrent: downloadTorrent == _undefined + ? _instance.downloadTorrent + : (downloadTorrent + as Mutation$MarkTorrentDownload$downloadTorrent?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent + get downloadTorrent { + final local$downloadTorrent = _instance.downloadTorrent; + return local$downloadTorrent == null + ? CopyWith$Mutation$MarkTorrentDownload$downloadTorrent.stub( + _then(_instance)) + : CopyWith$Mutation$MarkTorrentDownload$downloadTorrent( + local$downloadTorrent, (e) => call(downloadTorrent: e)); + } +} + +class _CopyWithStubImpl$Mutation$MarkTorrentDownload + implements CopyWith$Mutation$MarkTorrentDownload { + _CopyWithStubImpl$Mutation$MarkTorrentDownload(this._res); + + final TRes _res; + + @override + call({ + Mutation$MarkTorrentDownload$downloadTorrent? downloadTorrent, + String? $__typename, + }) => + _res; + + @override + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent + get downloadTorrent => + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent.stub(_res); +} + +const documentNodeMutationMarkTorrentDownload = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'MarkTorrentDownload'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'infohash')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'downloadTorrent'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'infohash'), + value: VariableNode(name: NameNode(value: 'infohash')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'task'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$MarkTorrentDownload _parserFn$Mutation$MarkTorrentDownload( + Map data) => + Mutation$MarkTorrentDownload.fromJson(data); +typedef OnMutationCompleted$Mutation$MarkTorrentDownload = FutureOr + Function( + Map?, + Mutation$MarkTorrentDownload?, +); + +class Options$Mutation$MarkTorrentDownload + extends graphql.MutationOptions { + Options$Mutation$MarkTorrentDownload({ + super.operationName, + required Variables$Mutation$MarkTorrentDownload variables, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Mutation$MarkTorrentDownload? typedOptimisticResult, + super.context, + OnMutationCompleted$Mutation$MarkTorrentDownload? onCompleted, + super.update, + super.onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$MarkTorrentDownload(data), + ), + document: documentNodeMutationMarkTorrentDownload, + parserFn: _parserFn$Mutation$MarkTorrentDownload, + ); + + final OnMutationCompleted$Mutation$MarkTorrentDownload? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$MarkTorrentDownload + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$MarkTorrentDownload({ + super.operationName, + required Variables$Mutation$MarkTorrentDownload variables, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Mutation$MarkTorrentDownload? typedOptimisticResult, + super.context, + super.pollInterval, + super.eagerlyFetchResults, + super.carryForwardDataOnException, + super.fetchResults, + }) : super( + variables: variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + document: documentNodeMutationMarkTorrentDownload, + parserFn: _parserFn$Mutation$MarkTorrentDownload, + ); +} + +extension ClientExtension$Mutation$MarkTorrentDownload + on graphql.GraphQLClient { + Future> + mutate$MarkTorrentDownload( + Options$Mutation$MarkTorrentDownload options) async => + await mutate(options); + graphql.ObservableQuery + watchMutation$MarkTorrentDownload( + WatchOptions$Mutation$MarkTorrentDownload options) => + watchMutation(options); +} + +class Mutation$MarkTorrentDownload$HookResult { + Mutation$MarkTorrentDownload$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$MarkTorrentDownload runMutation; + + final graphql.QueryResult result; +} + +Mutation$MarkTorrentDownload$HookResult useMutation$MarkTorrentDownload( + [WidgetOptions$Mutation$MarkTorrentDownload? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$MarkTorrentDownload()); + return Mutation$MarkTorrentDownload$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery + useWatchMutation$MarkTorrentDownload( + WatchOptions$Mutation$MarkTorrentDownload options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$MarkTorrentDownload + extends graphql.MutationOptions { + WidgetOptions$Mutation$MarkTorrentDownload({ + super.operationName, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Mutation$MarkTorrentDownload? typedOptimisticResult, + super.context, + OnMutationCompleted$Mutation$MarkTorrentDownload? onCompleted, + super.update, + super.onError, + }) : onCompletedWithParsed = onCompleted, + super( + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$MarkTorrentDownload(data), + ), + document: documentNodeMutationMarkTorrentDownload, + parserFn: _parserFn$Mutation$MarkTorrentDownload, + ); + + final OnMutationCompleted$Mutation$MarkTorrentDownload? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$MarkTorrentDownload + = graphql.MultiSourceResult Function( + Variables$Mutation$MarkTorrentDownload, { + Object? optimisticResult, + Mutation$MarkTorrentDownload? typedOptimisticResult, +}); +typedef Builder$Mutation$MarkTorrentDownload = widgets.Widget Function( + RunMutation$Mutation$MarkTorrentDownload, + graphql.QueryResult?, +); + +class Mutation$MarkTorrentDownload$Widget + extends graphql_flutter.Mutation { + Mutation$MarkTorrentDownload$Widget({ + super.key, + WidgetOptions$Mutation$MarkTorrentDownload? options, + required Builder$Mutation$MarkTorrentDownload builder, + }) : super( + options: options ?? WidgetOptions$Mutation$MarkTorrentDownload(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Mutation$MarkTorrentDownload$downloadTorrent { + Mutation$MarkTorrentDownload$downloadTorrent({ + this.task, + this.$__typename = 'DownloadTorrentResponse', + }); + + factory Mutation$MarkTorrentDownload$downloadTorrent.fromJson( + Map json) { + final l$task = json['task']; + final l$$__typename = json['__typename']; + return Mutation$MarkTorrentDownload$downloadTorrent( + task: l$task == null + ? null + : Mutation$MarkTorrentDownload$downloadTorrent$task.fromJson( + (l$task as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$MarkTorrentDownload$downloadTorrent$task? task; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$task = task; + resultData['task'] = l$task?.toJson(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$task = task; + final l$$__typename = $__typename; + return Object.hashAll([ + l$task, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Mutation$MarkTorrentDownload$downloadTorrent || + runtimeType != other.runtimeType) { + return false; + } + final l$task = task; + final lOther$task = other.task; + if (l$task != lOther$task) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MarkTorrentDownload$downloadTorrent + on Mutation$MarkTorrentDownload$downloadTorrent { + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent< + Mutation$MarkTorrentDownload$downloadTorrent> + get copyWith => CopyWith$Mutation$MarkTorrentDownload$downloadTorrent( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MarkTorrentDownload$downloadTorrent { + factory CopyWith$Mutation$MarkTorrentDownload$downloadTorrent( + Mutation$MarkTorrentDownload$downloadTorrent instance, + TRes Function(Mutation$MarkTorrentDownload$downloadTorrent) then, + ) = _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent; + + factory CopyWith$Mutation$MarkTorrentDownload$downloadTorrent.stub(TRes res) = + _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent; + + TRes call({ + Mutation$MarkTorrentDownload$downloadTorrent$task? task, + String? $__typename, + }); + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task get task; +} + +class _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent + implements CopyWith$Mutation$MarkTorrentDownload$downloadTorrent { + _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent( + this._instance, + this._then, + ); + + final Mutation$MarkTorrentDownload$downloadTorrent _instance; + + final TRes Function(Mutation$MarkTorrentDownload$downloadTorrent) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? task = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MarkTorrentDownload$downloadTorrent( + task: task == _undefined + ? _instance.task + : (task as Mutation$MarkTorrentDownload$downloadTorrent$task?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task get task { + final local$task = _instance.task; + return local$task == null + ? CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task.stub( + _then(_instance)) + : CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task( + local$task, (e) => call(task: e)); + } +} + +class _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent + implements CopyWith$Mutation$MarkTorrentDownload$downloadTorrent { + _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent(this._res); + + final TRes _res; + + @override + call({ + Mutation$MarkTorrentDownload$downloadTorrent$task? task, + String? $__typename, + }) => + _res; + + @override + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task get task => + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task.stub(_res); +} + +class Mutation$MarkTorrentDownload$downloadTorrent$task { + Mutation$MarkTorrentDownload$downloadTorrent$task({ + required this.id, + this.$__typename = 'Task', + }); + + factory Mutation$MarkTorrentDownload$downloadTorrent$task.fromJson( + Map json) { + final l$id = json['id']; + final l$$__typename = json['__typename']; + return Mutation$MarkTorrentDownload$downloadTorrent$task( + id: (l$id as String), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$id = id; + resultData['id'] = l$id; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Mutation$MarkTorrentDownload$downloadTorrent$task || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MarkTorrentDownload$downloadTorrent$task + on Mutation$MarkTorrentDownload$downloadTorrent$task { + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task< + Mutation$MarkTorrentDownload$downloadTorrent$task> + get copyWith => + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task< + TRes> { + factory CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task( + Mutation$MarkTorrentDownload$downloadTorrent$task instance, + TRes Function(Mutation$MarkTorrentDownload$downloadTorrent$task) then, + ) = _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent$task; + + factory CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task.stub( + TRes res) = + _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent$task; + + TRes call({ + String? id, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent$task + implements + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task { + _CopyWithImpl$Mutation$MarkTorrentDownload$downloadTorrent$task( + this._instance, + this._then, + ); + + final Mutation$MarkTorrentDownload$downloadTorrent$task _instance; + + final TRes Function(Mutation$MarkTorrentDownload$downloadTorrent$task) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? id = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MarkTorrentDownload$downloadTorrent$task( + id: id == _undefined || id == null ? _instance.id : (id as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent$task + implements + CopyWith$Mutation$MarkTorrentDownload$downloadTorrent$task { + _CopyWithStubImpl$Mutation$MarkTorrentDownload$downloadTorrent$task( + this._res); + + final TRes _res; + + @override + call({ + String? id, + String? $__typename, + }) => + _res; +} + +class Query$ListTorrents { + Query$ListTorrents({ + required this.torrents, + this.$__typename = 'Query', + }); + + factory Query$ListTorrents.fromJson(Map json) { + final l$torrents = json['torrents']; + final l$$__typename = json['__typename']; + return Query$ListTorrents( + torrents: (l$torrents as List) + .map((e) => + Query$ListTorrents$torrents.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List torrents; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$torrents = torrents; + resultData['torrents'] = l$torrents.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$torrents = torrents; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$torrents.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListTorrents || runtimeType != other.runtimeType) { + return false; + } + final l$torrents = torrents; + final lOther$torrents = other.torrents; + if (l$torrents.length != lOther$torrents.length) { + return false; + } + for (int i = 0; i < l$torrents.length; i++) { + final l$torrents$entry = l$torrents[i]; + final lOther$torrents$entry = lOther$torrents[i]; + if (l$torrents$entry != lOther$torrents$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListTorrents on Query$ListTorrents { + CopyWith$Query$ListTorrents get copyWith => + CopyWith$Query$ListTorrents( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListTorrents { + factory CopyWith$Query$ListTorrents( + Query$ListTorrents instance, + TRes Function(Query$ListTorrents) then, + ) = _CopyWithImpl$Query$ListTorrents; + + factory CopyWith$Query$ListTorrents.stub(TRes res) = + _CopyWithStubImpl$Query$ListTorrents; + + TRes call({ + List? torrents, + String? $__typename, + }); + TRes torrents( + Iterable Function( + Iterable< + CopyWith$Query$ListTorrents$torrents< + Query$ListTorrents$torrents>>) + fn); +} + +class _CopyWithImpl$Query$ListTorrents + implements CopyWith$Query$ListTorrents { + _CopyWithImpl$Query$ListTorrents( + this._instance, + this._then, + ); + + final Query$ListTorrents _instance; + + final TRes Function(Query$ListTorrents) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? torrents = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListTorrents( + torrents: torrents == _undefined || torrents == null + ? _instance.torrents + : (torrents as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + @override + TRes torrents( + Iterable Function( + Iterable< + CopyWith$Query$ListTorrents$torrents< + Query$ListTorrents$torrents>>) + fn) => + call( + torrents: fn(_instance.torrents + .map((e) => CopyWith$Query$ListTorrents$torrents( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$ListTorrents + implements CopyWith$Query$ListTorrents { + _CopyWithStubImpl$Query$ListTorrents(this._res); + + final TRes _res; + + @override + call({ + List? torrents, + String? $__typename, + }) => + _res; + + @override + torrents(fn) => _res; +} + +const documentNodeQueryListTorrents = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'ListTorrents'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'torrents'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'infohash'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'bytesCompleted'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'bytesMissing'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$ListTorrents _parserFn$Query$ListTorrents(Map data) => + Query$ListTorrents.fromJson(data); +typedef OnQueryComplete$Query$ListTorrents = FutureOr Function( + Map?, + Query$ListTorrents?, +); + +class Options$Query$ListTorrents + extends graphql.QueryOptions { + Options$Query$ListTorrents({ + super.operationName, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Query$ListTorrents? typedOptimisticResult, + super.pollInterval, + super.context, + OnQueryComplete$Query$ListTorrents? onComplete, + super.onError, + }) : onCompleteWithParsed = onComplete, + super( + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$ListTorrents(data), + ), + document: documentNodeQueryListTorrents, + parserFn: _parserFn$Query$ListTorrents, + ); + + final OnQueryComplete$Query$ListTorrents? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$ListTorrents + extends graphql.WatchQueryOptions { + WatchOptions$Query$ListTorrents({ + super.operationName, + super.fetchPolicy, + super.errorPolicy, + super.cacheRereadPolicy, + Object? optimisticResult, + Query$ListTorrents? typedOptimisticResult, + super.context, + super.pollInterval, + super.eagerlyFetchResults, + super.carryForwardDataOnException, + super.fetchResults, + }) : super( + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + document: documentNodeQueryListTorrents, + parserFn: _parserFn$Query$ListTorrents, + ); +} + +class FetchMoreOptions$Query$ListTorrents extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$ListTorrents( + {required super.updateQuery}) + : super( + document: documentNodeQueryListTorrents, + ); +} + +extension ClientExtension$Query$ListTorrents on graphql.GraphQLClient { + Future> query$ListTorrents( + [Options$Query$ListTorrents? options]) async => + await query(options ?? Options$Query$ListTorrents()); + graphql.ObservableQuery watchQuery$ListTorrents( + [WatchOptions$Query$ListTorrents? options]) => + watchQuery(options ?? WatchOptions$Query$ListTorrents()); + void writeQuery$ListTorrents({ + required Query$ListTorrents data, + bool broadcast = true, + }) => + writeQuery( + const graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryListTorrents)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$ListTorrents? readQuery$ListTorrents({bool optimistic = true}) { + final result = readQuery( + const graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryListTorrents)), + optimistic: optimistic, + ); + return result == null ? null : Query$ListTorrents.fromJson(result); + } +} + +graphql_flutter.QueryHookResult useQuery$ListTorrents( + [Options$Query$ListTorrents? options]) => + graphql_flutter.useQuery(options ?? Options$Query$ListTorrents()); +graphql.ObservableQuery useWatchQuery$ListTorrents( + [WatchOptions$Query$ListTorrents? options]) => + graphql_flutter.useWatchQuery(options ?? WatchOptions$Query$ListTorrents()); + +class Query$ListTorrents$Widget + extends graphql_flutter.Query { + Query$ListTorrents$Widget({ + super.key, + Options$Query$ListTorrents? options, + required super.builder, + }) : super( + options: options ?? Options$Query$ListTorrents(), + ); +} + +class Query$ListTorrents$torrents { + Query$ListTorrents$torrents({ + required this.name, + required this.infohash, + required this.bytesCompleted, + required this.bytesMissing, + this.$__typename = 'Torrent', + }); + + factory Query$ListTorrents$torrents.fromJson(Map json) { + final l$name = json['name']; + final l$infohash = json['infohash']; + final l$bytesCompleted = json['bytesCompleted']; + final l$bytesMissing = json['bytesMissing']; + final l$$__typename = json['__typename']; + return Query$ListTorrents$torrents( + name: (l$name as String), + infohash: (l$infohash as String), + bytesCompleted: (l$bytesCompleted as int), + bytesMissing: (l$bytesMissing as int), + $__typename: (l$$__typename as String), + ); + } + + final String name; + + final String infohash; + + final int bytesCompleted; + + final int bytesMissing; + + final String $__typename; + + Map toJson() { + final resultData = {}; + final l$name = name; + resultData['name'] = l$name; + final l$infohash = infohash; + resultData['infohash'] = l$infohash; + final l$bytesCompleted = bytesCompleted; + resultData['bytesCompleted'] = l$bytesCompleted; + final l$bytesMissing = bytesMissing; + resultData['bytesMissing'] = l$bytesMissing; + final l$$__typename = $__typename; + resultData['__typename'] = l$$__typename; + return resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$infohash = infohash; + final l$bytesCompleted = bytesCompleted; + final l$bytesMissing = bytesMissing; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$infohash, + l$bytesCompleted, + l$bytesMissing, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is! Query$ListTorrents$torrents || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$infohash = infohash; + final lOther$infohash = other.infohash; + if (l$infohash != lOther$infohash) { + return false; + } + final l$bytesCompleted = bytesCompleted; + final lOther$bytesCompleted = other.bytesCompleted; + if (l$bytesCompleted != lOther$bytesCompleted) { + return false; + } + final l$bytesMissing = bytesMissing; + final lOther$bytesMissing = other.bytesMissing; + if (l$bytesMissing != lOther$bytesMissing) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$ListTorrents$torrents + on Query$ListTorrents$torrents { + CopyWith$Query$ListTorrents$torrents + get copyWith => CopyWith$Query$ListTorrents$torrents( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$ListTorrents$torrents { + factory CopyWith$Query$ListTorrents$torrents( + Query$ListTorrents$torrents instance, + TRes Function(Query$ListTorrents$torrents) then, + ) = _CopyWithImpl$Query$ListTorrents$torrents; + + factory CopyWith$Query$ListTorrents$torrents.stub(TRes res) = + _CopyWithStubImpl$Query$ListTorrents$torrents; + + TRes call({ + String? name, + String? infohash, + int? bytesCompleted, + int? bytesMissing, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$ListTorrents$torrents + implements CopyWith$Query$ListTorrents$torrents { + _CopyWithImpl$Query$ListTorrents$torrents( + this._instance, + this._then, + ); + + final Query$ListTorrents$torrents _instance; + + final TRes Function(Query$ListTorrents$torrents) _then; + + static const _undefined = {}; + + @override + TRes call({ + Object? name = _undefined, + Object? infohash = _undefined, + Object? bytesCompleted = _undefined, + Object? bytesMissing = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$ListTorrents$torrents( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + infohash: infohash == _undefined || infohash == null + ? _instance.infohash + : (infohash as String), + bytesCompleted: bytesCompleted == _undefined || bytesCompleted == null + ? _instance.bytesCompleted + : (bytesCompleted as int), + bytesMissing: bytesMissing == _undefined || bytesMissing == null + ? _instance.bytesMissing + : (bytesMissing as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$ListTorrents$torrents + implements CopyWith$Query$ListTorrents$torrents { + _CopyWithStubImpl$Query$ListTorrents$torrents(this._res); + + final TRes _res; + + @override + call({ + String? name, + String? infohash, + int? bytesCompleted, + int? bytesMissing, + String? $__typename, + }) => + _res; +} diff --git a/ui/lib/components/download.dart b/ui/lib/components/download.dart new file mode 100644 index 0000000..f2c91cb --- /dev/null +++ b/ui/lib/components/download.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:tstor_ui/utils/bytes.dart'; + +class DownloadProgress extends StatelessWidget { + final int current; + final int total; + + const DownloadProgress(this.current, this.total, {super.key}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 32, + child: Row( + children: [ + Text("${current.bytesFormat()}/${total.bytesFormat()}"), + const SizedBox(width: 10), + Expanded(child: LinearProgressIndicator(value: current / total)), + const SizedBox(width: 10), + Text("${current / total * 100}%"), + ], + ), + ); + } +} diff --git a/ui/lib/font/t_icons_icons.dart b/ui/lib/font/t_icons_icons.dart new file mode 100644 index 0000000..a0a2ded --- /dev/null +++ b/ui/lib/font/t_icons_icons.dart @@ -0,0 +1,26 @@ +/// Flutter icons TIcons +/// Copyright (C) 2024 by original authors @ fluttericon.com, fontello.com +/// This font was generated by FlutterIcon.com, which is derived from Fontello. +/// +/// To use this font, place it in your fonts/ directory and include the +/// following in your pubspec.yaml +/// +/// flutter: +/// fonts: +/// - family: TIcons +/// fonts: +/// - asset: fonts/TIcons.ttf +/// +/// +/// +library; +import 'package:flutter/widgets.dart'; + +class TIcons { + TIcons._(); + + static const _kFontFam = 'TIcons'; + static const String? _kFontPkg = null; + + static const IconData bittorrent_bttold_logo = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); +} diff --git a/ui/lib/main.dart b/ui/lib/main.dart new file mode 100644 index 0000000..74d71cf --- /dev/null +++ b/ui/lib/main.dart @@ -0,0 +1,196 @@ +import 'package:dynamic_color/dynamic_color.dart'; +import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:tstor_ui/api/client.dart'; +import 'package:tstor_ui/screens/downloads.dart'; +import 'package:tstor_ui/screens/file_view.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return DynamicColorBuilder(builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { + return MaterialApp( + title: 'tStor', + theme: lightDynamic != null ? ThemeData.from(colorScheme: lightDynamic) : ThemeData.light(), + darkTheme: + darkDynamic != null ? ThemeData.from(colorScheme: darkDynamic) : ThemeData.dark(), + home: GraphQLProvider( + client: ValueNotifier(client), + child: const MyHomePage(), + ), + ); + }); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int currentPageIndex = 0; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("tStor"), + ), + body: [ + const FileViewScreen(), + const DownloadsScreen(), + ][currentPageIndex], + bottomNavigationBar: BottomNavigationBar( + currentIndex: currentPageIndex, + onTap: (i) => setState(() { + currentPageIndex = i; + }), + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.folder_copy_outlined), + activeIcon: Icon(Icons.folder_copy), + label: 'Files', + ), + BottomNavigationBarItem( + icon: Icon(Icons.download_outlined), + activeIcon: Icon(Icons.download), + label: 'Downloads', + ), + ], + ), + ); + } +} + +class NavigationExample extends StatefulWidget { + const NavigationExample({super.key}); + + @override + State createState() => _NavigationExampleState(); +} + +class _NavigationExampleState extends State { + int currentPageIndex = 0; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + return Scaffold( + bottomNavigationBar: NavigationBar( + onDestinationSelected: (int index) { + setState(() { + currentPageIndex = index; + }); + }, + indicatorColor: Colors.amber, + selectedIndex: currentPageIndex, + destinations: const [ + NavigationDestination( + selectedIcon: Icon(Icons.home), + icon: Icon(Icons.home_outlined), + label: 'Home', + ), + NavigationDestination( + icon: Badge(child: Icon(Icons.notifications_sharp)), + label: 'Notifications', + ), + NavigationDestination( + icon: Badge( + label: Text('2'), + child: Icon(Icons.messenger_sharp), + ), + label: 'Messages', + ), + ], + ), + body: [ + /// Home page + Card( + shadowColor: Colors.transparent, + margin: const EdgeInsets.all(8.0), + child: SizedBox.expand( + child: Center( + child: Text( + 'Home page', + style: theme.textTheme.titleLarge, + ), + ), + ), + ), + + /// Notifications page + const Padding( + padding: EdgeInsets.all(8.0), + child: Column( + children: [ + Card( + child: ListTile( + leading: Icon(Icons.notifications_sharp), + title: Text('Notification 1'), + subtitle: Text('This is a notification'), + ), + ), + Card( + child: ListTile( + leading: Icon(Icons.notifications_sharp), + title: Text('Notification 2'), + subtitle: Text('This is a notification'), + ), + ), + ], + ), + ), + + /// Messages page + ListView.builder( + reverse: true, + itemCount: 2, + itemBuilder: (BuildContext context, int index) { + if (index == 0) { + return Align( + alignment: Alignment.centerRight, + child: Container( + margin: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: theme.colorScheme.primary, + borderRadius: BorderRadius.circular(8.0), + ), + child: Text( + 'Hello', + style: theme.textTheme.bodyLarge!.copyWith(color: theme.colorScheme.onPrimary), + ), + ), + ); + } + return Align( + alignment: Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: theme.colorScheme.primary, + borderRadius: BorderRadius.circular(8.0), + ), + child: Text( + 'Hi!', + style: theme.textTheme.bodyLarge!.copyWith(color: theme.colorScheme.onPrimary), + ), + ), + ); + }, + ), + ][currentPageIndex], + ); + } +} diff --git a/ui/lib/screens/downloads.dart b/ui/lib/screens/downloads.dart new file mode 100644 index 0000000..2f27004 --- /dev/null +++ b/ui/lib/screens/downloads.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:tstor_ui/api/client.dart'; +import 'package:tstor_ui/api/torrent.graphql.dart'; +import 'package:tstor_ui/components/download.dart'; + +class DownloadsScreen extends StatefulWidget { + const DownloadsScreen({super.key}); + + @override + State createState() => _DownloadsScreenState(); +} + +class _DownloadsScreenState extends State { + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: client.query$ListTorrents(), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data == null) { + return const Center(child: CircularProgressIndicator()); + } + + final torrents = snapshot.data!.parsedData!.torrents; + + return ListView.builder( + itemCount: torrents.length, + itemBuilder: (context, index) { + final torrent = torrents[index]; + return ListTile( + title: Text(torrent.name), + subtitle: DownloadProgress( + torrent.bytesCompleted, torrent.bytesCompleted + torrent.bytesMissing), + trailing: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + IconButton( + onPressed: () => client.mutate$MarkTorrentDownload( + Options$Mutation$MarkTorrentDownload( + variables: Variables$Mutation$MarkTorrentDownload( + infohash: torrent.infohash, + ), + ), + ), + icon: const Icon(Icons.download), + ) + ], + ), + ); + }, + ); + }, + ); + } +} diff --git a/ui/lib/screens/file_view.dart b/ui/lib/screens/file_view.dart new file mode 100644 index 0000000..6e26d3c --- /dev/null +++ b/ui/lib/screens/file_view.dart @@ -0,0 +1,352 @@ +import 'package:flutter/material.dart'; +import 'package:graphql/client.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:tstor_ui/api/client.dart'; +import 'package:tstor_ui/api/fs_entry.graphql.dart'; +import 'package:tstor_ui/api/torrent.graphql.dart'; +import 'package:tstor_ui/components/download.dart'; + +import 'package:tstor_ui/font/t_icons_icons.dart'; +import 'package:path/path.dart' as p; +import 'package:tstor_ui/utils/bytes.dart'; + +class FileViewScreen extends StatefulWidget { + final String initialPath; + + const FileViewScreen({super.key, this.initialPath = "/"}); + + @override + State createState() => _FileViewScreenState(); +} + +class _FileViewScreenState extends State { + late String path; + + late final TextEditingController pathController; + + @override + void initState() { + path = widget.initialPath; + pathController = TextEditingController(text: path); + listDirFuture = client.query$ListDir( + Options$Query$ListDir( + variables: Variables$Query$ListDir(path: path), + ), + ); + super.initState(); + } + + void cd(String part) { + setState(() { + listDirFuture = null; + }); + + setState(() { + path = p.normalize(p.join(path, part)); + pathController.text = path; + listDirFuture = client.query$ListDir( + Options$Query$ListDir( + variables: Variables$Query$ListDir(path: path), + ), + ); + }); + } + + void refresh() { + setState(() { + listDirFuture = null; + }); + + setState(() { + listDirFuture = client.query$ListDir( + Options$Query$ListDir( + variables: Variables$Query$ListDir(path: path), + fetchPolicy: FetchPolicy.noCache, + ), + ); + }); + } + + Future>? listDirFuture; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: TextField( + controller: pathController, + onEditingComplete: () => cd(pathController.text), + ), + leading: IconButton( + onPressed: () => cd(".."), + icon: const Icon(Icons.arrow_upward), + ), + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: refresh, + ) + ], + ), + body: FutureBuilder( + key: GlobalKey(), + future: listDirFuture, + initialData: null, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + final data = snapshot.data!; + + if (data.exception != null) { + return Text("Error\n${data.exception.toString()}"); + } + + final entry = snapshot.data?.parsedData?.fsEntry; + if (entry == null) { + return const Center(child: Text("Entry not exists")); + } + + final entries = _getEntries(entry); + + if (entries == null || entries.isEmpty) { + return const Center(child: Text("Empty dir")); + } + + return CustomScrollView( + slivers: [ + EntryInfoSliver(entry: entry), + SliverList.builder( + itemCount: entries.length, + itemBuilder: (context, index) { + return DirEntry( + entry: entries[index], + onTap: (name, isFile) { + if (!isFile) { + cd(name); + } + }, + ); + }, + ), + ], + ); + }, + ), + ); + } +} + +List? _getEntries(Query$ListDir$fsEntry entry) { + switch (entry) { + case Query$ListDir$fsEntry$$ArchiveFS entry: + return entry.entries; + case Query$ListDir$fsEntry$$ResolverFS entry: + return entry.entries; + case Query$ListDir$fsEntry$$SimpleDir entry: + return entry.entries; + case Query$ListDir$fsEntry$$TorrentFS entry: + return entry.entries; + default: + return null; + } +} + +class DirEntry extends StatelessWidget { + final Fragment$DirEntry entry; + final void Function(String name, bool isFile) onTap; + + const DirEntry({super.key, required this.entry, required this.onTap}); + + @override + Widget build(BuildContext context) { + switch (entry) { + case Fragment$TorrentDir entry: + final completness = entry.torrent.bytesCompleted / + (entry.torrent.bytesCompleted + entry.torrent.bytesMissing); + + return ListTile( + leading: const Icon(TIcons.bittorrent_bttold_logo), + title: Text(entry.name), + subtitle: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text("${completness * 100}%"), + Expanded(child: LinearProgressIndicator(value: completness)), + IconButton( + onPressed: () => client.mutate$MarkTorrentDownload( + Options$Mutation$MarkTorrentDownload( + variables: + Variables$Mutation$MarkTorrentDownload(infohash: entry.torrent.infohash), + ), + ), + icon: const Icon(Icons.download), + ) + ], + ), + onTap: () => onTap(entry.name, false), + ); + case Fragment$DirEntry$$SimpleDir entry: + return ListTile( + leading: const Icon(Icons.folder), + title: Text(entry.name), + onTap: () => onTap(entry.name, false), + ); + case Fragment$DirEntry$$ArchiveFS entry: + return ListTile( + leading: const Icon(Icons.folder_zip), + title: Text(entry.name), + onTap: () => onTap(entry.name, false), + ); + case Fragment$File entry: + return ListTile( + leading: const Icon(Icons.insert_drive_file), + title: Text(entry.name), + onTap: () => onTap(entry.name, true), + ); + default: + return ListTile( + leading: const Icon(Icons.question_mark), + title: Text(entry.name), + ); + } + } +} + +class EntryInfoSliver extends StatelessWidget { + final Query$ListDir$fsEntry entry; + + const EntryInfoSliver({super.key, required this.entry}); + + @override + Widget build(BuildContext context) { + switch (entry) { + case Query$ListDir$fsEntry$$TorrentFS entry: + final total = entry.torrent.bytesCompleted + entry.torrent.bytesMissing; + + return EntryInfoHeader( + icon: TIcons.bittorrent_bttold_logo, + title: Text(entry.torrent.name), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(entry.torrent.name), + Text("Size: ${total.bytesFormat()}"), + Text("InfoHash: ${entry.torrent.infohash}"), + DownloadProgress(entry.torrent.bytesCompleted, total) + ], + ), + actions: [ + IconButton( + icon: const Icon(Icons.download), + onPressed: () => client.mutate$MarkTorrentDownload( + Options$Mutation$MarkTorrentDownload( + variables: Variables$Mutation$MarkTorrentDownload( + infohash: entry.torrent.infohash, + ), + ), + ), + ) + ], + ); + + default: + return EntryInfoHeader( + icon: Icons.folder, + title: Text(entry.name), + body: Text(entry.name), + ); + } + } +} + +class EntryInfoHeader extends StatelessWidget { + final IconData icon; + final Widget title; + final Widget body; + final List? actions; + + const EntryInfoHeader({ + super.key, + required this.icon, + required this.title, + required this.body, + this.actions, + }); + + @override + Widget build(BuildContext context) { + return SliverPersistentHeader( + floating: true, + pinned: false, + delegate: EntryInfoSliverHeaderDelegate(icon: icon, title: title, body: body), + ); + } +} + +class EntryInfoSliverHeaderDelegate extends SliverPersistentHeaderDelegate { + final IconData icon; + final Widget title; + final Widget body; + final List? actions; + final double size; + + const EntryInfoSliverHeaderDelegate({ + required this.icon, + required this.title, + required this.body, + this.actions, + this.size = 150, + }); + + @override + double get maxExtent => size; + + @override + double get minExtent => size; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => true; + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + final content = [ + Icon(icon, size: 50), + Expanded(child: body), + ]; + + if (actions != null && actions!.isNotEmpty) { + content.add(ButtonBar(children: actions!)); + } + + final appBarTheme = AppBarTheme.of(context); + final colorScheme = Theme.of(context).colorScheme; + final onTop = (shrinkOffset == 0); + + return Material( + color: + onTop ? appBarTheme.backgroundColor ?? colorScheme.surface : colorScheme.surfaceContainer, + elevation: onTop ? 0 : appBarTheme.elevation ?? 3, + surfaceTintColor: appBarTheme.surfaceTintColor ?? colorScheme.surfaceTint, + child: ClipRect( + child: SizedBox( + height: maxExtent, + child: Column( + children: [ + const Spacer(), + Row( + children: content, + ), + const Spacer(), + const Divider( + height: 1, + thickness: 1, + ), + ], + ), + ), + ), + ); + } +} diff --git a/ui/lib/utils/bytes.dart b/ui/lib/utils/bytes.dart new file mode 100644 index 0000000..bd70c29 --- /dev/null +++ b/ui/lib/utils/bytes.dart @@ -0,0 +1,30 @@ +extension BytesFormat on num { + /// method returns a human readable string representing a file size + /// size can be passed as number or as string + /// the optional parameter 'round' specifies the number of numbers after comma/point (default is 2) + /// the optional boolean parameter 'useBase1024' specifies if we should count in 1024's (true) or 1000's (false). e.g. 1KB = 1024B (default is true) + String bytesFormat({int round = 2, bool useBase1024 = true}) { + const List affixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + + num divider = useBase1024 ? 1024 : 1000; + + num size = this; + num runningDivider = divider; + num runningPreviousDivider = 0; + int affix = 0; + + while (size >= runningDivider && affix < affixes.length - 1) { + runningPreviousDivider = runningDivider; + runningDivider *= divider; + affix++; + } + + String result = + (runningPreviousDivider == 0 ? size : size / runningPreviousDivider).toStringAsFixed(round); + + //Check if the result ends with .00000 (depending on how many decimals) and remove it if found. + if (result.endsWith("0" * round)) result = result.substring(0, result.length - round - 1); + + return "$result ${affixes[affix]}"; + } +} diff --git a/ui/linux/.gitignore b/ui/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/ui/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/ui/linux/CMakeLists.txt b/ui/linux/CMakeLists.txt new file mode 100644 index 0000000..e8d0ed0 --- /dev/null +++ b/ui/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "tstor_ui") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.tstor_ui") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/ui/linux/flutter/CMakeLists.txt b/ui/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/ui/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/ui/linux/flutter/generated_plugin_registrant.cc b/ui/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..675b719 --- /dev/null +++ b/ui/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) dynamic_color_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); + dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); +} diff --git a/ui/linux/flutter/generated_plugin_registrant.h b/ui/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/ui/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/ui/linux/flutter/generated_plugins.cmake b/ui/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..3e303c1 --- /dev/null +++ b/ui/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + dynamic_color +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/ui/linux/main.cc b/ui/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/ui/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/ui/linux/my_application.cc b/ui/linux/my_application.cc new file mode 100644 index 0000000..23924e7 --- /dev/null +++ b/ui/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "tstor_ui"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "tstor_ui"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/ui/linux/my_application.h b/ui/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/ui/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/ui/macos/.gitignore b/ui/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/ui/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/ui/macos/Flutter/Flutter-Debug.xcconfig b/ui/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/ui/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/ui/macos/Flutter/Flutter-Release.xcconfig b/ui/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/ui/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/ui/macos/Flutter/GeneratedPluginRegistrant.swift b/ui/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..5d96bad --- /dev/null +++ b/ui/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,16 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import connectivity_plus +import dynamic_color +import path_provider_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/ui/macos/Runner.xcodeproj/project.pbxproj b/ui/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c0a515c --- /dev/null +++ b/ui/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* tstor_ui.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tstor_ui.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* tstor_ui.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* tstor_ui.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tstor_ui.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tstor_ui"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tstor_ui.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tstor_ui"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tstor_ui.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tstor_ui"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/ui/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ui/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ui/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..0f8ec36 --- /dev/null +++ b/ui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/macos/Runner.xcworkspace/contents.xcworkspacedata b/ui/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ui/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ui/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ui/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ui/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ui/macos/Runner/AppDelegate.swift b/ui/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/ui/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..82b6f9d Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..13b35eb Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..0a3f5fa Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bdb5722 Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..f083318 Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..326c0e7 Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..2f1632c Binary files /dev/null and b/ui/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/ui/macos/Runner/Base.lproj/MainMenu.xib b/ui/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/ui/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/macos/Runner/Configs/AppInfo.xcconfig b/ui/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..5689a7d --- /dev/null +++ b/ui/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = tstor_ui + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.tstorUi + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/ui/macos/Runner/Configs/Debug.xcconfig b/ui/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/ui/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/ui/macos/Runner/Configs/Release.xcconfig b/ui/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/ui/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/ui/macos/Runner/Configs/Warnings.xcconfig b/ui/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/ui/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/ui/macos/Runner/DebugProfile.entitlements b/ui/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/ui/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/ui/macos/Runner/Info.plist b/ui/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/ui/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/ui/macos/Runner/MainFlutterWindow.swift b/ui/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/ui/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/ui/macos/Runner/Release.entitlements b/ui/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/ui/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/ui/macos/RunnerTests/RunnerTests.swift b/ui/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/ui/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/ui/pubspec.lock b/ui/pubspec.lock new file mode 100644 index 0000000..986f95d --- /dev/null +++ b/ui/pubspec.lock @@ -0,0 +1,866 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.dev" + source: hosted + version: "2.4.9" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.dev" + source: hosted + version: "7.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + dynamic_color: + dependency: "direct main" + description: + name: dynamic_color + sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d + url: "https://pub.dev" + source: hosted + version: "1.7.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_hooks: + dependency: transitive + description: + name: flutter_hooks + sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 + url: "https://pub.dev" + source: hosted + version: "0.20.5" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + gql: + dependency: "direct main" + description: + name: gql + sha256: afe032332ddfa69b79f1dea2ad7d95923d4993c1b269b224fc7bb3d17e32d33c + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1709845491443" + gql_code_builder: + dependency: transitive + description: + name: gql_code_builder + sha256: b70e17fe62af58c3a9a3dfd619bf0b07ca15afe4d285001fbcd0852a40538401 + url: "https://pub.dev" + source: hosted + version: "0.9.1+1" + gql_dedupe_link: + dependency: transitive + description: + name: gql_dedupe_link + sha256: "2971173c68623d5c43f5327ea899bd2ee64ce3461c1263f240b4bb6211f667be" + url: "https://pub.dev" + source: hosted + version: "2.0.4-alpha+1709845491527" + gql_error_link: + dependency: transitive + description: + name: gql_error_link + sha256: "93901458f3c050e33386dedb0ca7173e08cebd7078e4e0deca4bf23ab7a71f63" + url: "https://pub.dev" + source: hosted + version: "1.0.0+1" + gql_exec: + dependency: transitive + description: + name: gql_exec + sha256: "394944626fae900f1d34343ecf2d62e44eb984826189c8979d305f0ae5846e38" + url: "https://pub.dev" + source: hosted + version: "1.1.1-alpha+1699813812660" + gql_http_link: + dependency: transitive + description: + name: gql_http_link + sha256: "1f922eed1b7078fdbfd602187663026f9f659fe9a9499e2207b5d5e01617f658" + url: "https://pub.dev" + source: hosted + version: "1.0.1+1" + gql_link: + dependency: transitive + description: + name: gql_link + sha256: "177500e250b3742d6d2673d57961e8413b6593dc6bd6a512c51865b6cf096f7e" + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1709845491457" + gql_transform_link: + dependency: transitive + description: + name: gql_transform_link + sha256: "0645fdd874ca1be695fd327271fdfb24c0cd6fa40774a64b946062f321a59709" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + gql_tristate_value: + dependency: transitive + description: + name: gql_tristate_value + sha256: ae045e7e272fbfd030084315140c683c9b032a9861a37165f68c2ecd8a759664 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + graphql: + dependency: "direct main" + description: + name: graphql + sha256: d066e53446166c12537458386b507f7426f2b8801ebafc184576aab3cbc64d56 + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.7" + graphql_codegen: + dependency: "direct dev" + description: + name: graphql_codegen + sha256: d984935993e5fbbd37358eb6dc47e5feacc49fe731c227db47967474f9da6034 + url: "https://pub.dev" + source: hosted + version: "0.14.0" + graphql_flutter: + dependency: "direct main" + description: + name: graphql_flutter + sha256: "39b5e830bc654ab02c5b776c31675841d1a8c95840fdd284efba713b1d47e65d" + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.6" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + normalize: + dependency: transitive + description: + name: normalize + sha256: "8a60e37de5b608eeaf9b839273370c71ebba445e9f73b08eee7725e0d92dbc43" + url: "https://pub.dev" + source: hosted + version: "0.8.2+1" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: a75f83f14ad81d5fe4b3319710b90dec37da0e22612326b696c9e1b8f34bbf48 + url: "https://pub.dev" + source: hosted + version: "14.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + url: "https://pub.dev" + source: hosted + version: "2.4.5" + win32: + dependency: transitive + description: + name: win32 + sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" + url: "https://pub.dev" + source: hosted + version: "5.4.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.4.0-282.1.beta <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/ui/pubspec.yaml b/ui/pubspec.yaml new file mode 100644 index 0000000..fda9fa5 --- /dev/null +++ b/ui/pubspec.yaml @@ -0,0 +1,54 @@ +name: tstor_ui +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ">=3.4.0-282.1.beta <4.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.6 + graphql: ^5.2.0-beta.7 + graphql_flutter: ^5.2.0-beta.6 + gql: ^1.0.1-alpha+1709845491443 + provider: ^6.1.2 + + path: any + dynamic_color: ^1.7.0 +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^3.0.0 + build_runner: ^2.4.9 + graphql_codegen: ^0.14.0 + +flutter: + uses-material-design: true + fonts: + - family: TIcons + fonts: + - asset: fonts/TIcons.ttf diff --git a/ui/test/widget_test.dart b/ui/test/widget_test.dart new file mode 100644 index 0000000..12b5e78 --- /dev/null +++ b/ui/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:tstor_ui/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/ui/web/favicon.png b/ui/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/ui/web/favicon.png differ diff --git a/ui/web/icons/Icon-192.png b/ui/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/ui/web/icons/Icon-192.png differ diff --git a/ui/web/icons/Icon-512.png b/ui/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/ui/web/icons/Icon-512.png differ diff --git a/ui/web/icons/Icon-maskable-192.png b/ui/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/ui/web/icons/Icon-maskable-192.png differ diff --git a/ui/web/icons/Icon-maskable-512.png b/ui/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/ui/web/icons/Icon-maskable-512.png differ diff --git a/ui/web/index.html b/ui/web/index.html new file mode 100644 index 0000000..a5fd938 --- /dev/null +++ b/ui/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + tstor_ui + + + + + + diff --git a/ui/web/manifest.json b/ui/web/manifest.json new file mode 100644 index 0000000..f576063 --- /dev/null +++ b/ui/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "tstor_ui", + "short_name": "tstor_ui", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/ui/windows/.gitignore b/ui/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/ui/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/ui/windows/CMakeLists.txt b/ui/windows/CMakeLists.txt new file mode 100644 index 0000000..0760f04 --- /dev/null +++ b/ui/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(tstor_ui LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "tstor_ui") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/ui/windows/flutter/CMakeLists.txt b/ui/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/ui/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/ui/windows/flutter/generated_plugin_registrant.cc b/ui/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..ae0cf3f --- /dev/null +++ b/ui/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + DynamicColorPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); +} diff --git a/ui/windows/flutter/generated_plugin_registrant.h b/ui/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/ui/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/ui/windows/flutter/generated_plugins.cmake b/ui/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..e47678f --- /dev/null +++ b/ui/windows/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus + dynamic_color +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/ui/windows/runner/CMakeLists.txt b/ui/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/ui/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/ui/windows/runner/Runner.rc b/ui/windows/runner/Runner.rc new file mode 100644 index 0000000..793de2a --- /dev/null +++ b/ui/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "tstor_ui" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "tstor_ui" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "tstor_ui.exe" "\0" + VALUE "ProductName", "tstor_ui" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/ui/windows/runner/flutter_window.cpp b/ui/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/ui/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/ui/windows/runner/flutter_window.h b/ui/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/ui/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/ui/windows/runner/main.cpp b/ui/windows/runner/main.cpp new file mode 100644 index 0000000..2930e28 --- /dev/null +++ b/ui/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"tstor_ui", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/ui/windows/runner/resource.h b/ui/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/ui/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ui/windows/runner/resources/app_icon.ico b/ui/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/ui/windows/runner/resources/app_icon.ico differ diff --git a/ui/windows/runner/runner.exe.manifest b/ui/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..a42ea76 --- /dev/null +++ b/ui/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/ui/windows/runner/utils.cpp b/ui/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/ui/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/ui/windows/runner/utils.h b/ui/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/ui/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/ui/windows/runner/win32_window.cpp b/ui/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/ui/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/ui/windows/runner/win32_window.h b/ui/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/ui/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_