diff --git a/backend/internal/domain/repository/item/diagramitem.go b/backend/internal/domain/repository/item/diagramitem.go index dc77af9c7..264eac7a3 100644 --- a/backend/internal/domain/repository/item/diagramitem.go +++ b/backend/internal/domain/repository/item/diagramitem.go @@ -9,7 +9,7 @@ import ( type ItemRepository interface { FindByID(ctx context.Context, userID string, itemID string, isPublic bool) mo.Result[*diagramitem.DiagramItem] - Find(ctx context.Context, userID string, offset, limit int, isPublic bool, isBookmark bool) mo.Result[[]*diagramitem.DiagramItem] + Find(ctx context.Context, userID string, offset, limit int, isPublic bool, isBookmark bool, shouldLoadText bool) mo.Result[[]*diagramitem.DiagramItem] Save(ctx context.Context, userID string, item *diagramitem.DiagramItem, isPublic bool) mo.Result[*diagramitem.DiagramItem] Delete(ctx context.Context, userID string, itemID string, isPublic bool) mo.Result[bool] } diff --git a/backend/internal/domain/service/service.go b/backend/internal/domain/service/service.go index e6ad16086..e86bb2a0d 100644 --- a/backend/internal/domain/service/service.go +++ b/backend/internal/domain/service/service.go @@ -9,8 +9,10 @@ import ( "errors" "net" "os" + "slices" "time" + "github.com/99designs/gqlgen/graphql" jwt "github.com/form3tech-oss/jwt-go" "github.com/harehare/textusm/internal/context/values" "github.com/harehare/textusm/internal/domain/model/item/diagramitem" @@ -55,11 +57,13 @@ func isAuthenticated(ctx context.Context) error { } func (s *Service) Find(ctx context.Context, offset, limit int, isPublic bool, isBookmark bool, fields map[string]struct{}) mo.Result[[]*diagramitem.DiagramItem] { + shouldLoadText := slices.Contains(graphql.CollectAllFields(ctx), "text") + if err := isAuthenticated(ctx); err != nil { return mo.Err[[]*diagramitem.DiagramItem](err) } - return s.repo.Find(ctx, values.GetUID(ctx).OrEmpty(), offset, limit, isPublic, isBookmark) + return s.repo.Find(ctx, values.GetUID(ctx).OrEmpty(), offset, limit, isPublic, isBookmark, shouldLoadText) } func (s *Service) FindByID(ctx context.Context, itemID string, isPublic bool) mo.Result[*diagramitem.DiagramItem] { diff --git a/backend/internal/infra/firebase/item/diagramitem.go b/backend/internal/infra/firebase/item/diagramitem.go index 84e6e0bf3..d77438853 100644 --- a/backend/internal/infra/firebase/item/diagramitem.go +++ b/backend/internal/infra/firebase/item/diagramitem.go @@ -49,7 +49,7 @@ func (r *FirestoreItemRepository) FindByID(ctx context.Context, userID string, i }) } -func (r *FirestoreItemRepository) Find(ctx context.Context, userID string, offset, limit int, isPublic bool, isBookmark bool) mo.Result[[]*diagramitem.DiagramItem] { +func (r *FirestoreItemRepository) Find(ctx context.Context, userID string, offset, limit int, isPublic bool, isBookmark bool, shouldLoadText bool) mo.Result[[]*diagramitem.DiagramItem] { var ( items []*diagramitem.DiagramItem iter *firestore.DocumentIterator @@ -79,7 +79,17 @@ func (r *FirestoreItemRepository) Find(ctx context.Context, userID string, offse } items = append(items, i.Map(func(v *diagramitem.DiagramItem) (*diagramitem.DiagramItem, error) { - return v.ClearText(), nil + if shouldLoadText && v.IsSaveToStorage() { + ret := r.findFromCloudStorage(ctx, userID, v.ID()) + if ret.IsError() { + slog.Error("Failed find diagram", "userID", userID, "itemID", v.ID(), "isPublic", isPublic) + return nil, ret.Error() + } + v.UpdateEncryptedText(ret.OrEmpty()) + return v, nil + } else { + return v.ClearText(), nil + } }).OrEmpty()) } @@ -237,5 +247,5 @@ func (r *FirestoreItemRepository) deleteToFirestore(ctx context.Context, userID func (r *FirestoreItemRepository) deleteToCloudStorage(ctx context.Context, userID, itemID string) mo.Result[bool] { storage := firebase.NewCloudStorage(r.storage) - return storage.Delete(ctx, userID, itemID) + return storage.Delete(ctx, storageRoot, userID, itemID) } diff --git a/backend/internal/infra/firebase/storage.go b/backend/internal/infra/firebase/storage.go index 3c0ad78ce..57b50bc19 100644 --- a/backend/internal/infra/firebase/storage.go +++ b/backend/internal/infra/firebase/storage.go @@ -4,6 +4,7 @@ import ( "bytes" "compress/gzip" "context" + "fmt" "io" "strings" @@ -94,14 +95,16 @@ func (s *CloudStorage) Get(ctx context.Context, prefix string, paths ...string) return mo.Ok(string(body)) } -func (s *CloudStorage) Delete(ctx context.Context, uid, itemID string) mo.Result[bool] { +func (s *CloudStorage) Delete(ctx context.Context, prefix, uid, itemID string) mo.Result[bool] { bucket, err := s.client.DefaultBucket() if err != nil { return mo.Err[bool](err) } - if err = bucket.Object(getObjectName(uid, itemID)).Delete(ctx); err != nil { + fmt.Println(getObjectName(prefix, uid, itemID)) + + if err = bucket.Object(getObjectName(prefix, uid, itemID)).Delete(ctx); err != nil { return mo.Err[bool](err) } diff --git a/firebase.json b/firebase.json index 00947727b..3494a6a03 100644 --- a/firebase.json +++ b/firebase.json @@ -2,7 +2,12 @@ "hosting": { "public": "frontend/dist", "target": "textusm", - "ignore": ["firebase.json", "**/.*", "**/node_modules/**", "**/*.map"], + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**", + "**/*.map" + ], "rewrites": [ { "source": "**", @@ -74,7 +79,8 @@ }, "hosting": { "port": 5000 - } + }, + "singleProjectMode": true }, "storage": { "rules": "storage.rules" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8788fbdec..9236a2366 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "textusm", - "version": "0.14.2", + "version": "0.14.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "textusm", - "version": "0.14.2", + "version": "0.14.3", "license": "MIT", "dependencies": { "@sentry/browser": "^7.73.0", @@ -38,7 +38,7 @@ "elm": "^0.19.1-5", "elm-book": "^1.0.1", "elm-constants": "^1.0.0", - "elm-format": "^0.8.7", + "elm-format": "^0.8.6", "elm-optimize-level-2": "^0.3.5", "elm-review": "^2.10.3", "elm-test": "^0.19.1-revision12", @@ -113,9 +113,9 @@ } }, "node_modules/@avh4/elm-format-darwin-arm64": { - "version": "0.8.7-2", - "resolved": "https://registry.npmjs.org/@avh4/elm-format-darwin-arm64/-/elm-format-darwin-arm64-0.8.7-2.tgz", - "integrity": "sha512-F5JD44mJ3KX960J5GkXMfh1/dtkXuPcQpX2EToHQKjLTZUfnhZ++ytQQt0gAvrJ0bzoOvhNzjNjUHDA1ruTVbg==", + "version": "0.8.6-2", + "resolved": "https://registry.npmjs.org/@avh4/elm-format-darwin-arm64/-/elm-format-darwin-arm64-0.8.6-2.tgz", + "integrity": "sha512-DCK8/BHNnZq1DIn4zVm2oSA/kBt5O4lleMo8XOC6R7ZhxrS4xGw6qmwdinKjEcawI2yV9YHkuuwmkhT+jCyAGg==", "cpu": [ "arm64" ], @@ -126,9 +126,9 @@ ] }, "node_modules/@avh4/elm-format-darwin-x64": { - "version": "0.8.7-2", - "resolved": "https://registry.npmjs.org/@avh4/elm-format-darwin-x64/-/elm-format-darwin-x64-0.8.7-2.tgz", - "integrity": "sha512-4pfF1cl0KyTion+7Mg4XKM3yi4Yc7vP76Kt/DotLVGJOSag4ISGic1og2mt8RZZ7XArybBmHNyYkiUbe/cEiCw==", + "version": "0.8.6-2", + "resolved": "https://registry.npmjs.org/@avh4/elm-format-darwin-x64/-/elm-format-darwin-x64-0.8.6-2.tgz", + "integrity": "sha512-t4sUuJf9d45brhEbPGyP0+O0wwn0rCXV4ojr4xHpni2uy+LxMIrJmhnWImpf7ozQzcr5Lh0GrPniOENOxDlMxw==", "cpu": [ "x64" ], @@ -139,9 +139,9 @@ ] }, "node_modules/@avh4/elm-format-linux-arm64": { - "version": "0.8.7-2", - "resolved": "https://registry.npmjs.org/@avh4/elm-format-linux-arm64/-/elm-format-linux-arm64-0.8.7-2.tgz", - "integrity": "sha512-WkVmuce2zU6s9dupHhqPc886Vaqpea8dZlxv2fpZ4wSzPUbiiKHoHZzoVndMIMTUL0TZukP3Ps0n/lWO5R5+FA==", + "version": "0.8.6-2", + "resolved": "https://registry.npmjs.org/@avh4/elm-format-linux-arm64/-/elm-format-linux-arm64-0.8.6-2.tgz", + "integrity": "sha512-EgG5fDvW2DLN7GX6fj8kr+T4vkojHRy4IMYI/FV6z1W0220B0b+KKvJjBJyRBAz5BFaV1PJNMgaySu14AoTbow==", "cpu": [ "arm64" ], @@ -152,9 +152,9 @@ ] }, "node_modules/@avh4/elm-format-linux-x64": { - "version": "0.8.7-2", - "resolved": "https://registry.npmjs.org/@avh4/elm-format-linux-x64/-/elm-format-linux-x64-0.8.7-2.tgz", - "integrity": "sha512-kmncfJrTBjVT94JtQvMf4M5Pn2Yl0sZt3wo7AzgFiDnB/CiZ+KjJyXuWM64NeGiv4MQqzPq65tsFXUH1CIJeiQ==", + "version": "0.8.6-2", + "resolved": "https://registry.npmjs.org/@avh4/elm-format-linux-x64/-/elm-format-linux-x64-0.8.6-2.tgz", + "integrity": "sha512-bgEXHumfXNbYwGbuBKTuYhSPIQ3bY/pQcUTzTHW/+4tZnaEObMZnLm8tfukIzWHrM/oK5MtYAvj5B7UmmZO5Pg==", "cpu": [ "x64" ], @@ -165,12 +165,13 @@ ] }, "node_modules/@avh4/elm-format-win32-x64": { - "version": "0.8.7-2", - "resolved": "https://registry.npmjs.org/@avh4/elm-format-win32-x64/-/elm-format-win32-x64-0.8.7-2.tgz", - "integrity": "sha512-sBdMBGq/8mD8Y5C+fIr5vlb3N50yB7S1MfgeAq2QEbvkr/sKrCZI540i43lZDH9gWsfA1w2W8wCe0penFYzsGw==", + "version": "0.8.6-2", + "resolved": "https://registry.npmjs.org/@avh4/elm-format-win32-x64/-/elm-format-win32-x64-0.8.6-2.tgz", + "integrity": "sha512-ERzWQ2rutVNeryaBqKQMKghw/HdxVww7ujPBW5mJ99PwWGHYZdJCpw6P4m/s3zOqLKviRM44WCrH0LgDSuwcXQ==", "cpu": [ "x64" ], + "deprecated": "Binary is not statically linked. Use elm-format@0.8.6-windows, or upgrade to elm-format 0.8.7", "dev": true, "optional": true, "os": [ @@ -2562,9 +2563,9 @@ } }, "node_modules/@firebase/firestore/node_modules/@grpc/grpc-js": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.3.tgz", - "integrity": "sha512-b8iWtdrYIeT5fdZdS4Br/6h/kuk0PW5EVBUGk1amSbrpL8DlktJD43CdcCWwRdd6+jgwHhADSbL9CsNnm6EUPA==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.5.tgz", + "integrity": "sha512-iouYNlPxRAwZ2XboDT+OfRKHuaKHiqjB5VFYZ0NFrHkbEF+AV3muIUY9olQsp8uxU4VvRCMiRk9ftzFDGb61aw==", "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" @@ -4289,17 +4290,6 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", @@ -4823,181 +4813,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true - }, "node_modules/@zkochan/retry": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@zkochan/retry/-/retry-0.2.0.tgz", @@ -5071,16 +4886,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -6154,16 +5959,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -6993,9 +6788,9 @@ } }, "node_modules/cypress": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.0.tgz", - "integrity": "sha512-mpI8qcTwLGiA4zEQvTC/U1xGUezVV4V8HQCOYjlEOrVmU1etVvxOjkCXHGwrlYdZU/EPmUiWfsO3yt1o+Q2bgw==", + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.1.tgz", + "integrity": "sha512-g4mJLZxYN+UAF2LMy3Znd4LBnUmS59Vynd81VES59RdW48Yt+QtR2cush3melOoVNz0PPbADpWr8DcUx6mif8Q==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8234,20 +8029,21 @@ } }, "node_modules/elm-format": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/elm-format/-/elm-format-0.8.7.tgz", - "integrity": "sha512-sVzFXfWnb+6rzXK+q3e3Ccgr6/uS5mFbFk1VSmigC+x2XZ28QycAa7lS8owl009ALPhRQk+pZ95Eq5ANjpEZsQ==", + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/elm-format/-/elm-format-0.8.6.tgz", + "integrity": "sha512-hUYo7PNH6iUR6km1r9teEYYyf/sSzBEuPtbCj4aTbHg1u9B2btGz4oxuIL12yXMxyzXiGoggLS9nnsaLQ1C2rg==", + "deprecated": "Windows binary is broken. Recommended to upgrade to elm-format 0.8.7", "dev": true, "hasInstallScript": true, "bin": { "elm-format": "bin/elm-format" }, "optionalDependencies": { - "@avh4/elm-format-darwin-arm64": "0.8.7-2", - "@avh4/elm-format-darwin-x64": "0.8.7-2", - "@avh4/elm-format-linux-arm64": "0.8.7-2", - "@avh4/elm-format-linux-x64": "0.8.7-2", - "@avh4/elm-format-win32-x64": "0.8.7-2" + "@avh4/elm-format-darwin-arm64": "0.8.6-2", + "@avh4/elm-format-darwin-x64": "0.8.6-2", + "@avh4/elm-format-linux-arm64": "0.8.6-2", + "@avh4/elm-format-linux-x64": "0.8.6-2", + "@avh4/elm-format-win32-x64": "0.8.6-2" } }, "node_modules/elm-live": { @@ -9114,20 +8910,6 @@ "node": ">=4.0.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -9224,13 +9006,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", - "dev": true, - "peer": true - }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -10105,20 +9880,6 @@ "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==", "dev": true }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -10597,16 +10358,6 @@ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -11192,9 +10943,9 @@ } }, "node_modules/firebase-tools": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-12.6.2.tgz", - "integrity": "sha512-Z5cEtLLr11ZWDuT1AQEug4z6mqgJZSWpA7bRF4CVaOHfDAZfmxYdurZjHYOoHT1hoL+2JwS/Nf5KSrA/OhC9Lw==", + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-12.7.0.tgz", + "integrity": "sha512-NBXYEbY9FE20BcEivxL0agXkehNTtcFhQmGGPPWrxntVgWOnbrzy4fh1xjZnQSRgZu4VpEWthXDM+oCkn6ouKQ==", "dev": true, "dependencies": { "@google-cloud/pubsub": "^3.0.1", @@ -14354,13 +14105,6 @@ "node": ">= 6" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -14588,16 +14332,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/graphql": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", - "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 10.x" - } - }, "node_modules/graphql-request": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz", @@ -16544,9 +16278,9 @@ } }, "node_modules/knip": { - "version": "2.33.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-2.33.1.tgz", - "integrity": "sha512-26DpeV4sOqFSEI9JC4PXRPnApnqAN4ja8ioqBBqVbDebYkOSEWj39024f3BK6k9im9l3o/HBpuvZegcC3RG2JQ==", + "version": "2.33.3", + "resolved": "https://registry.npmjs.org/knip/-/knip-2.33.3.tgz", + "integrity": "sha512-LOlkJKYUlhD9/YL5c+eR4yyiByKibazgZem45H0mzED4VNG0ajm8UYNAmLxAbQzzgRIiGAKeJMfzjXoeQ0IO7Q==", "dev": true, "dependencies": { "@ericcornelissen/bash-parser": "^0.5.2", @@ -16780,16 +16514,6 @@ "node": ">=8" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.11.5" - } - }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -17825,13 +17549,6 @@ "node": ">= 0.6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -18839,9 +18556,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", "dev": true, "funding": [ { @@ -20172,59 +19889,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, "node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -21545,16 +21209,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -21669,92 +21323,6 @@ "node": ">=10" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -22677,20 +22245,6 @@ "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", "dev": true }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -22705,54 +22259,6 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, - "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index b67103ffe..f7fd79992 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "textusm", "description": "Online tool for Generate a User Story Mapping from indented text.", - "version": "0.14.2", + "version": "0.14.3", "author": "harehare", "bugs": { "url": "https://github.com/harehare/textusm/issues" @@ -36,7 +36,7 @@ "elm": "^0.19.1-5", "elm-book": "^1.0.1", "elm-constants": "^1.0.0", - "elm-format": "^0.8.7", + "elm-format": "^0.8.6", "elm-optimize-level-2": "^0.3.5", "elm-review": "^2.10.3", "elm-test": "^0.19.1-revision12", diff --git a/frontend/src/elm/Api/Graphql/Query.elm b/frontend/src/elm/Api/Graphql/Query.elm index 519439210..5d57bc708 100644 --- a/frontend/src/elm/Api/Graphql/Query.elm +++ b/frontend/src/elm/Api/Graphql/Query.elm @@ -1,6 +1,7 @@ module Api.Graphql.Query exposing ( ShareCondition , allItems + , allItemsWithText , gistItem , gistItems , item @@ -44,6 +45,12 @@ allItems ( offset, limit ) = Selection.allItemsSelection +allItemsWithText : ( Int, Int ) -> SelectionSet (Maybe (List DiagramItem)) RootQuery +allItemsWithText ( offset, limit ) = + Query.allItems (\optionals -> { optionals | offset = Present offset, limit = Present limit }) <| + Selection.allItemsSelectionWithText + + gistItem : String -> SelectionSet DiagramItem RootQuery gistItem id = Query.gistItem { id = Graphql.Scalar.Id id } <| diff --git a/frontend/src/elm/Api/Graphql/Selection.elm b/frontend/src/elm/Api/Graphql/Selection.elm index 3d2382251..73f36c51f 100644 --- a/frontend/src/elm/Api/Graphql/Selection.elm +++ b/frontend/src/elm/Api/Graphql/Selection.elm @@ -1,5 +1,6 @@ module Api.Graphql.Selection exposing ( allItemsSelection + , allItemsSelectionWithText , gistItemSelection , itemSelection , settingsSelection @@ -31,6 +32,14 @@ allItemsSelection = } +allItemsSelectionWithText : SelectionSet DiagramItem Graphql.Union.DiagramItem +allItemsSelectionWithText = + Graphql.Union.DiagramItem.fragments + { onItem = itemSelectionWithText + , onGistItem = gistItemSelection + } + + gistItemSelection : SelectionSet DiagramItem Graphql.Object.GistItem gistItemSelection = SelectionSet.succeed DiagramItem @@ -61,6 +70,21 @@ itemSelection = |> with (Graphql.Object.Item.updatedAt |> DiagramItem.mapToDateTime) +itemSelectionWithText : SelectionSet DiagramItem Graphql.Object.Item +itemSelectionWithText = + SelectionSet.succeed DiagramItem + |> with (Graphql.Object.Item.id |> SelectionSet.map (\(Graphql.Scalar.Id id) -> Just <| DiagramId.fromString id)) + |> with (Graphql.Object.Item.text |> SelectionSet.map Text.fromString) + |> with (Graphql.Object.Item.diagram |> SelectionSet.map DiagramType.fromGraphqlValue) + |> with (Graphql.Object.Item.title |> SelectionSet.map Title.fromString) + |> with Graphql.Object.Item.thumbnail + |> with Graphql.Object.Item.isPublic + |> with Graphql.Object.Item.isBookmark + |> hardcoded (Just DiagramLocation.Remote) + |> with (Graphql.Object.Item.createdAt |> DiagramItem.mapToDateTime) + |> with (Graphql.Object.Item.updatedAt |> DiagramItem.mapToDateTime) + + settingsSelection : SelectionSet DiagramSettings.Settings Graphql.Object.Settings settingsSelection = SelectionSet.succeed DiagramSettings.Settings diff --git a/frontend/src/elm/Api/Request.elm b/frontend/src/elm/Api/Request.elm index 25e594108..2f34b7d4e 100644 --- a/frontend/src/elm/Api/Request.elm +++ b/frontend/src/elm/Api/Request.elm @@ -1,6 +1,8 @@ module Api.Request exposing ( allItems + , allItemsWithText , bookmark + , bulkSave , delete , deleteGist , gistItem @@ -51,6 +53,15 @@ allItems idToken ( offset, limit ) = |> Task.mapError toError +allItemsWithText : Maybe IdToken -> ( Int, Int ) -> Task RequestError (Maybe (List DiagramItem)) +allItemsWithText idToken ( offset, limit ) = + Query.allItemsWithText ( offset, limit ) + |> Http.queryRequest graphQLUrl + |> authHeaders idToken + |> Http.toTask + |> Task.mapError toError + + bookmark : Maybe IdToken -> String -> Bool -> Task RequestError (Maybe DiagramItem) bookmark idToken itemID isBookmark = Mutation.bookmark itemID isBookmark @@ -150,9 +161,17 @@ publicItem idToken id = |> Task.mapError toError -save : Maybe IdToken -> InputItem -> Bool -> Task RequestError DiagramItem +bulkSave : Maybe IdToken -> List InputItem -> Bool -> Task RequestError (List DiagramItem) +bulkSave idToken inputs isPublic = + List.map + (save idToken isPublic) + inputs + |> Task.sequence + + +save : Maybe IdToken -> Bool -> InputItem -> Task RequestError DiagramItem save idToken input isPublic = - Mutation.save input isPublic + Mutation.save isPublic input |> Http.mutationRequest graphQLUrl |> authHeaders idToken |> Http.toTask diff --git a/frontend/src/elm/Effect.elm b/frontend/src/elm/Effect.elm index 1d8baf153..063c8cfbb 100644 --- a/frontend/src/elm/Effect.elm +++ b/frontend/src/elm/Effect.elm @@ -29,8 +29,8 @@ changePublicState : (Result DiagramItem DiagramItem -> msg) -> { isPublic : Bool changePublicState msg { isPublic, item, session } = Request.save (Session.getIdToken session) - (DiagramItem.toInputItem item) isPublic + (DiagramItem.toInputItem item) |> Task.mapError (\_ -> item) |> Task.attempt msg |> Return.command diff --git a/frontend/src/elm/Effect/Diagram.elm b/frontend/src/elm/Effect/Diagram.elm index 2fd85de39..ab774dfe6 100644 --- a/frontend/src/elm/Effect/Diagram.elm +++ b/frontend/src/elm/Effect/Diagram.elm @@ -262,7 +262,7 @@ saveToRemote msg { diagram, session, settings } = Request.saveGist (Session.getIdToken session) accessToken (DiagramItem.toInputGistItem diagram) (Text.toString diagram.text) _ -> - Request.save (Session.getIdToken session) (DiagramItem.toInputItem diagram) diagram.isPublic + Request.save (Session.getIdToken session) diagram.isPublic (DiagramItem.toInputItem diagram) ) |> Task.attempt msg |> Return.command diff --git a/frontend/src/elm/Main.elm b/frontend/src/elm/Main.elm index 4ea9e1402..f16dd62ee 100644 --- a/frontend/src/elm/Main.elm +++ b/frontend/src/elm/Main.elm @@ -1385,13 +1385,19 @@ processDiagramListMsg msg = >> startProgress >> Effect.closeLocalFile + DiagramList.Export -> + startProgress + + DiagramList.GotExportDiagrams (Err e) -> + showErrorMessage <| RequestError.toMessage e + DiagramList.Removed (Err _) -> showErrorMessage Message.messagEerrorOccurred DiagramList.GotDiagrams (Err _) -> showErrorMessage Message.messagEerrorOccurred - DiagramList.ImportComplete json -> + DiagramList.ImportDiagrams json -> case DiagramItem.stringToList json of Ok _ -> showInfoMessage Message.messageImportCompleted @@ -1399,6 +1405,12 @@ processDiagramListMsg msg = Err _ -> showErrorMessage Message.messagEerrorOccurred + DiagramList.ImportedRemoteDiagrams (Ok _) -> + showInfoMessage Message.messageImportCompleted + + DiagramList.ImportedRemoteDiagrams (Err _) -> + showErrorMessage Message.messagEerrorOccurred + _ -> stopProgress diff --git a/frontend/src/elm/Models/Diagram/Item.elm b/frontend/src/elm/Models/Diagram/Item.elm index bc0f8fca7..a04ad28a4 100644 --- a/frontend/src/elm/Models/Diagram/Item.elm +++ b/frontend/src/elm/Models/Diagram/Item.elm @@ -6,6 +6,7 @@ module Models.Diagram.Item exposing , empty , encoder , getId + , id , isRemoteDiagram , listToString , listToValue @@ -220,6 +221,11 @@ location = Lens .location (\b a -> { a | location = b }) +id : Lens DiagramItem (Maybe DiagramId) +id = + Lens .id (\b a -> { a | id = b }) + + stringToList : String -> Result D.Error (List DiagramItem) stringToList json = D.decodeString (D.list decoder) json @@ -229,8 +235,8 @@ toInputGistItem : DiagramItem -> InputGistItem toInputGistItem item = { id = case item.id of - Just id -> - OptionalArgument.Present <| Graphql.Scalar.Id <| DiagramId.toString id + Just id_ -> + OptionalArgument.Present <| Graphql.Scalar.Id <| DiagramId.toString id_ Nothing -> OptionalArgument.Absent @@ -252,8 +258,8 @@ toInputItem : DiagramItem -> InputItem toInputItem item = { id = case item.id of - Just id -> - OptionalArgument.Present <| Graphql.Scalar.Id <| DiagramId.toString id + Just id_ -> + OptionalArgument.Present <| Graphql.Scalar.Id <| DiagramId.toString id_ Nothing -> OptionalArgument.Absent diff --git a/frontend/src/elm/Page/List.elm b/frontend/src/elm/Page/List.elm index 60e7b0701..ac52544bb 100644 --- a/frontend/src/elm/Page/List.elm +++ b/frontend/src/elm/Page/List.elm @@ -90,7 +90,7 @@ import Html.Styled.Lazy as Lazy import Http import Json.Decode as D import Json.Encode as E -import List.Extra exposing (updateIf) +import List.Extra as ListEx import Message exposing (Lang) import Models.Color as Color import Models.Diagram.Id as DiagramId @@ -134,6 +134,7 @@ type Msg | GetDiagrams | GotLocalDiagramsJson D.Value | GotDiagrams (Result RequestError (List DiagramItem)) + | GotExportDiagrams (Result RequestError (List DiagramItem)) | GetPublicDiagrams Int | GotPublicDiagrams (Result RequestError (List DiagramItem)) | GetBookmarkDiagrams Int @@ -144,7 +145,8 @@ type Msg | Export | Import | ImportFile File - | ImportComplete String + | ImportDiagrams String + | ImportedRemoteDiagrams (Result RequestError (List DiagramItem)) | ShowConfirmDialog DiagramItem @@ -780,6 +782,21 @@ reload = Return.andThen <| \m -> Return.return { m | diagramList = DiagramList.notAsked } (getDiagrams ()) +fetchAllItems : Session -> Int -> Task.Task RequestError (List DiagramItem) +fetchAllItems session pageNo = + Request.allItemsWithText (Session.getIdToken session) (pageOffsetAndLimit pageNo) + |> Task.andThen + (\items -> + case items of + Just items_ -> + fetchAllItems session (pageNo + 1) + |> Task.map (\items__ -> items_ ++ items__) + + Nothing -> + Task.succeed [] + ) + + update : Model -> Msg -> Return.ReturnF Msg Model update model message = case message of @@ -977,7 +994,7 @@ update model message = { m | diagramList = DiagramList.create m.diagramList - (RemoteData.withDefault [] list |> updateIf (\item -> item.id == diagram.id) (\item -> { item | isBookmark = not item.isBookmark })) + (RemoteData.withDefault [] list |> ListEx.updateIf (\item -> item.id == diagram.id) (\item -> { item | isBookmark = not item.isBookmark })) pageNo hasMorePage } @@ -995,25 +1012,64 @@ update model message = Return.command <| Select.file [ "application/json" ] ImportFile ImportFile file -> - Return.command <| Task.perform ImportComplete <| File.toString file + Return.command <| Task.perform ImportDiagrams <| File.toString file - ImportComplete json -> + ImportDiagrams json -> DiagramItem.stringToList json |> Result.toMaybe |> Maybe.map (\diagrams -> - Return.command <| importDiagram <| DiagramItem.listToValue diagrams + if Session.isSignedIn model.session then + Request.bulkSave (Session.getIdToken model.session) + (List.map + (\diagram -> + (DiagramItem.location.set (Just DiagramLocation.Remote) >> DiagramItem.id.set Nothing) diagram + |> DiagramItem.toInputItem + ) + diagrams + ) + False + |> Task.attempt ImportedRemoteDiagrams + |> Return.command + + else + Return.command <| importDiagram <| DiagramItem.listToValue diagrams ) |> Maybe.withDefault Return.zero + ImportedRemoteDiagrams (Ok _) -> + reload + + ImportedRemoteDiagrams (Err _) -> + Return.zero + Export -> case model.diagramList of - DiagramList.AllList (Success diagrams) _ _ -> - Return.command <| Download.string "textusm.json" "application/json" <| DiagramItem.listToString diagrams + DiagramList.AllList (Success _) _ _ -> + if Session.isSignedIn model.session && model.isOnline then + Return.command (Task.attempt GotExportDiagrams (fetchAllItems model.session 1)) + + else + Return.command (Task.attempt GotExportDiagrams (Task.succeed [])) + + _ -> + Return.zero + + GotExportDiagrams (Ok diagrams) -> + case model.diagramList of + DiagramList.AllList (Success localDiagrams) _ _ -> + List.concat [ diagrams, localDiagrams ] + |> ListEx.uniqueBy .id + |> DiagramItem.listToString + |> Download.string "textusm.json" "application/json" + |> Return.command _ -> Return.zero + GotExportDiagrams (Err _) -> + Return.zero + CloseDialog -> closeDialog diff --git a/frontend/src/ts/auth.ts b/frontend/src/ts/auth.ts index 914b47920..7c7d5f746 100644 --- a/frontend/src/ts/auth.ts +++ b/frontend/src/ts/auth.ts @@ -2,13 +2,13 @@ import { type FirebaseOptions, initializeApp } from 'firebase/app'; import { type AuthProvider, signOut as firebaseSignOut, - signInWithRedirect, signInWithPopup as firebaseSignInWithPopup, GoogleAuthProvider, GithubAuthProvider, getAuth, onAuthStateChanged as firebaseOnAuthStateChanged, connectAuthEmulator, + type UserCredential, } from 'firebase/auth'; import { getPerformance } from 'firebase/performance'; @@ -38,9 +38,9 @@ if (import.meta.env.DEV && process.env.FIREBASE_AUTH_EMULATOR_HOST) { connectAuthEmulator(auth, `http://${process.env.FIREBASE_AUTH_EMULATOR_HOST}`); } -export const signIn = async (provider: AuthProvider): Promise => +export const signIn = async (provider: AuthProvider): Promise => new Promise((resolve, reject) => { - signInWithRedirect(auth, provider) + firebaseSignInWithPopup(auth, provider) .then((result) => { resolve(result); })