diff --git a/README.md b/README.md index d0a9800..f0d2c61 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,18 @@ Run `ficsit help` to see a list of available commands. * [Go 1.21](https://go.dev/doc/install) * IDE of Choice. Goland or VSCode suggested. +### Code Generation + +If you update any of the GraphQL queries, run this to update generated code: + +```bash +(echo "y") | npx graphqurl https://api.ficsit.app/v2/query --introspect -H 'content-type: application/json' > schema.graphql +go generate -tags tools -x ./... +``` + +If this command fails due to a mismatched schema, +you may need to use the url `https://api.ficsit.dev/v2/query` instead. + ## Building ```bash @@ -97,3 +109,23 @@ go build ``` Will produce `ficsit-cli.exe` in the repo root directory. + +### Linting + +Install `golangci-lint` via the directions [here](https://golangci-lint.run/usage/install/#local-installation), +but make sure to install the version specified in `.github/workflows/push.yaml` instead of whatever it suggests. + +Then, to run it, use: + +```bash +golangci-lint run --fix +``` + +### Updating generated docs + +The files within `./docs` are generated using cobra, use the following to update +them. + +```bash +go run tools.go +``` diff --git a/cli/disk/local.go b/cli/disk/local.go index 4f509dc..f4bc5d2 100644 --- a/cli/disk/local.go +++ b/cli/disk/local.go @@ -40,7 +40,7 @@ func (l localDisk) Read(path string) ([]byte, error) { } func (l localDisk) Write(path string, data []byte) error { - return os.WriteFile(path, data, 0777) //nolint + return os.WriteFile(path, data, 0o777) //nolint } func (l localDisk) Remove(path string) error { @@ -48,7 +48,7 @@ func (l localDisk) Remove(path string) error { } func (l localDisk) MkDir(path string) error { - return os.MkdirAll(path, 0777) //nolint + return os.MkdirAll(path, 0o777) //nolint } func (l localDisk) ReadDir(path string) ([]Entry, error) { @@ -68,5 +68,5 @@ func (l localDisk) ReadDir(path string) ([]Entry, error) { } func (l localDisk) Open(path string, flag int) (io.WriteCloser, error) { - return os.OpenFile(path, flag, 0777) //nolint + return os.OpenFile(path, flag, 0o777) //nolint } diff --git a/cspell.json b/cspell.json index 92278f2..b9ab9fa 100644 --- a/cspell.json +++ b/cspell.json @@ -7,7 +7,14 @@ // words - list of words to be always considered correct "words": [ "ficsit", + "gofumpt", "Goland", + "golangci", + "goquery", + "graphqurl", + "mvdan", + "pgdn", + "pgup", "wordwrap" ], // flagWords - list of words to be always considered incorrect diff --git a/docs/ficsit.md b/docs/ficsit.md index 1f6160c..4bf3b7b 100644 --- a/docs/ficsit.md +++ b/docs/ficsit.md @@ -7,13 +7,13 @@ cli mod manager for satisfactory ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") -h, --help help for ficsit --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_apply.md b/docs/ficsit_apply.md index 7285c79..0a135a7 100644 --- a/docs/ficsit_apply.md +++ b/docs/ficsit_apply.md @@ -17,12 +17,12 @@ ficsit apply [installation] ... [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_cli.md b/docs/ficsit_cli.md index 2ad9277..e1a1a61 100644 --- a/docs/ficsit_cli.md +++ b/docs/ficsit_cli.md @@ -17,12 +17,12 @@ ficsit cli [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_installation.md b/docs/ficsit_installation.md index 6aba002..f82afd9 100644 --- a/docs/ficsit_installation.md +++ b/docs/ficsit_installation.md @@ -13,12 +13,12 @@ Manage installations ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_installation_add.md b/docs/ficsit_installation_add.md index 331114a..07f0908 100644 --- a/docs/ficsit_installation_add.md +++ b/docs/ficsit_installation_add.md @@ -17,12 +17,12 @@ ficsit installation add [profile] [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_installation_ls.md b/docs/ficsit_installation_ls.md index 4f65428..f6a5cc8 100644 --- a/docs/ficsit_installation_ls.md +++ b/docs/ficsit_installation_ls.md @@ -17,12 +17,12 @@ ficsit installation ls [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_installation_remove.md b/docs/ficsit_installation_remove.md index fed7e9a..d6e15e3 100644 --- a/docs/ficsit_installation_remove.md +++ b/docs/ficsit_installation_remove.md @@ -17,12 +17,12 @@ ficsit installation remove [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_installation_set-profile.md b/docs/ficsit_installation_set-profile.md index eda139d..0ef9ccf 100644 --- a/docs/ficsit_installation_set-profile.md +++ b/docs/ficsit_installation_set-profile.md @@ -17,12 +17,12 @@ ficsit installation set-profile [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile.md b/docs/ficsit_profile.md index 4cd53fa..93cdd69 100644 --- a/docs/ficsit_profile.md +++ b/docs/ficsit_profile.md @@ -13,12 +13,12 @@ Manage profiles ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile_delete.md b/docs/ficsit_profile_delete.md index 6f3dee6..32c940c 100644 --- a/docs/ficsit_profile_delete.md +++ b/docs/ficsit_profile_delete.md @@ -17,12 +17,12 @@ ficsit profile delete [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile_ls.md b/docs/ficsit_profile_ls.md index 34026ee..6d6f669 100644 --- a/docs/ficsit_profile_ls.md +++ b/docs/ficsit_profile_ls.md @@ -17,12 +17,12 @@ ficsit profile ls [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile_mods.md b/docs/ficsit_profile_mods.md index 9f992c1..1e9b780 100644 --- a/docs/ficsit_profile_mods.md +++ b/docs/ficsit_profile_mods.md @@ -17,12 +17,12 @@ ficsit profile mods [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile_new.md b/docs/ficsit_profile_new.md index 780343e..d3d2d34 100644 --- a/docs/ficsit_profile_new.md +++ b/docs/ficsit_profile_new.md @@ -17,12 +17,12 @@ ficsit profile new [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_profile_rename.md b/docs/ficsit_profile_rename.md index de4441b..60099c5 100644 --- a/docs/ficsit_profile_rename.md +++ b/docs/ficsit_profile_rename.md @@ -17,12 +17,12 @@ ficsit profile rename [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_search.md b/docs/ficsit_search.md index 3a10dcf..211cd48 100644 --- a/docs/ficsit_search.md +++ b/docs/ficsit_search.md @@ -22,12 +22,12 @@ ficsit search [query] [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_smr.md b/docs/ficsit_smr.md index 0591a43..4b14124 100644 --- a/docs/ficsit_smr.md +++ b/docs/ficsit_smr.md @@ -13,12 +13,12 @@ Manage mods on SMR ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_smr_upload.md b/docs/ficsit_smr_upload.md index e105949..4f71fae 100644 --- a/docs/ficsit_smr_upload.md +++ b/docs/ficsit_smr_upload.md @@ -19,12 +19,12 @@ ficsit smr upload [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/docs/ficsit_version.md b/docs/ficsit_version.md index 73da4d3..fbd5c1c 100644 --- a/docs/ficsit_version.md +++ b/docs/ficsit_version.md @@ -17,12 +17,12 @@ ficsit version [flags] ``` --api-base string URL for API (default "https://api.ficsit.app") --api-key string API key to use when sending requests - --cache-dir string The cache directory (default "/home/vilsol/.cache/ficsit") + --cache-dir string The cache directory (default "/home/{{Username}}/.cache/ficsit") --concurrent-downloads int Maximum number of concurrent downloads (default 5) --dry-run Dry-run. Do not save any changes --graphql-api string Path for GraphQL API (default "/v2/query") --installations-file string The installations file (default "installations.json") - --local-dir string The local directory (default "/home/vilsol/.local/share/ficsit") + --local-dir string The local directory (default "/home/{{Username}}/.local/share/ficsit") --log string The log level to output (default "info") --log-file string File to output logs to --offline Whether to only use local data diff --git a/ficsit/queries/mod.graphql b/ficsit/queries/mod.graphql index 1dec009..9605ace 100644 --- a/ficsit/queries/mod.graphql +++ b/ficsit/queries/mod.graphql @@ -11,6 +11,16 @@ query GetMod ($modId: String!) { username } } + compatibility { + EA { + note + state + } + EXP { + note + state + } + } full_description source_url created_at diff --git a/ficsit/types.go b/ficsit/types.go index f316cef..f0737c5 100644 --- a/ficsit/types.go +++ b/ficsit/types.go @@ -46,6 +46,14 @@ type CheckVersionUploadStateStateCreateVersionResponseVersion struct { // GetId returns CheckVersionUploadStateStateCreateVersionResponseVersion.Id, and is useful for accessing the field via an interface. func (v *CheckVersionUploadStateStateCreateVersionResponseVersion) GetId() string { return v.Id } +type CompatibilityState string + +const ( + CompatibilityStateWorks CompatibilityState = "Works" + CompatibilityStateDamaged CompatibilityState = "Damaged" + CompatibilityStateBroken CompatibilityState = "Broken" +) + // CreateVersionResponse is returned by CreateVersion on success. type CreateVersionResponse struct { VersionID string `json:"versionID"` @@ -64,15 +72,16 @@ func (v *FinalizeCreateVersionResponse) GetSuccess() bool { return v.Success } // GetModMod includes the requested fields of the GraphQL type Mod. type GetModMod struct { - Id string `json:"id"` - Mod_reference string `json:"mod_reference"` - Name string `json:"name"` - Views int `json:"views"` - Downloads int `json:"downloads"` - Authors []GetModModAuthorsUserMod `json:"authors"` - Full_description string `json:"full_description"` - Source_url string `json:"source_url"` - Created_at time.Time `json:"-"` + Id string `json:"id"` + Mod_reference string `json:"mod_reference"` + Name string `json:"name"` + Views int `json:"views"` + Downloads int `json:"downloads"` + Authors []GetModModAuthorsUserMod `json:"authors"` + Compatibility GetModModCompatibilityCompatibilityInfo `json:"compatibility"` + Full_description string `json:"full_description"` + Source_url string `json:"source_url"` + Created_at time.Time `json:"-"` } // GetId returns GetModMod.Id, and is useful for accessing the field via an interface. @@ -93,6 +102,11 @@ func (v *GetModMod) GetDownloads() int { return v.Downloads } // GetAuthors returns GetModMod.Authors, and is useful for accessing the field via an interface. func (v *GetModMod) GetAuthors() []GetModModAuthorsUserMod { return v.Authors } +// GetCompatibility returns GetModMod.Compatibility, and is useful for accessing the field via an interface. +func (v *GetModMod) GetCompatibility() GetModModCompatibilityCompatibilityInfo { + return v.Compatibility +} + // GetFull_description returns GetModMod.Full_description, and is useful for accessing the field via an interface. func (v *GetModMod) GetFull_description() string { return v.Full_description } @@ -128,7 +142,7 @@ func (v *GetModMod) UnmarshalJSON(b []byte) error { src, dst) if err != nil { return fmt.Errorf( - "Unable to unmarshal GetModMod.Created_at: %w", err) + "unable to unmarshal GetModMod.Created_at: %w", err) } } } @@ -148,6 +162,8 @@ type __premarshalGetModMod struct { Authors []GetModModAuthorsUserMod `json:"authors"` + Compatibility GetModModCompatibilityCompatibilityInfo `json:"compatibility"` + Full_description string `json:"full_description"` Source_url string `json:"source_url"` @@ -172,6 +188,7 @@ func (v *GetModMod) __premarshalJSON() (*__premarshalGetModMod, error) { retval.Views = v.Views retval.Downloads = v.Downloads retval.Authors = v.Authors + retval.Compatibility = v.Compatibility retval.Full_description = v.Full_description retval.Source_url = v.Source_url { @@ -183,7 +200,7 @@ func (v *GetModMod) __premarshalJSON() (*__premarshalGetModMod, error) { &src) if err != nil { return nil, fmt.Errorf( - "Unable to marshal GetModMod.Created_at: %w", err) + "unable to marshal GetModMod.Created_at: %w", err) } } return &retval, nil @@ -209,6 +226,50 @@ type GetModModAuthorsUserModUser struct { // GetUsername returns GetModModAuthorsUserModUser.Username, and is useful for accessing the field via an interface. func (v *GetModModAuthorsUserModUser) GetUsername() string { return v.Username } +// GetModModCompatibilityCompatibilityInfo includes the requested fields of the GraphQL type CompatibilityInfo. +type GetModModCompatibilityCompatibilityInfo struct { + EA GetModModCompatibilityCompatibilityInfoEACompatibility `json:"EA"` + EXP GetModModCompatibilityCompatibilityInfoEXPCompatibility `json:"EXP"` +} + +// GetEA returns GetModModCompatibilityCompatibilityInfo.EA, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfo) GetEA() GetModModCompatibilityCompatibilityInfoEACompatibility { + return v.EA +} + +// GetEXP returns GetModModCompatibilityCompatibilityInfo.EXP, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfo) GetEXP() GetModModCompatibilityCompatibilityInfoEXPCompatibility { + return v.EXP +} + +// GetModModCompatibilityCompatibilityInfoEACompatibility includes the requested fields of the GraphQL type Compatibility. +type GetModModCompatibilityCompatibilityInfoEACompatibility struct { + Note string `json:"note"` + State CompatibilityState `json:"state"` +} + +// GetNote returns GetModModCompatibilityCompatibilityInfoEACompatibility.Note, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfoEACompatibility) GetNote() string { return v.Note } + +// GetState returns GetModModCompatibilityCompatibilityInfoEACompatibility.State, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfoEACompatibility) GetState() CompatibilityState { + return v.State +} + +// GetModModCompatibilityCompatibilityInfoEXPCompatibility includes the requested fields of the GraphQL type Compatibility. +type GetModModCompatibilityCompatibilityInfoEXPCompatibility struct { + Note string `json:"note"` + State CompatibilityState `json:"state"` +} + +// GetNote returns GetModModCompatibilityCompatibilityInfoEXPCompatibility.Note, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfoEXPCompatibility) GetNote() string { return v.Note } + +// GetState returns GetModModCompatibilityCompatibilityInfoEXPCompatibility.State, and is useful for accessing the field via an interface. +func (v *GetModModCompatibilityCompatibilityInfoEXPCompatibility) GetState() CompatibilityState { + return v.State +} + // GetModNameMod includes the requested fields of the GraphQL type Mod. type GetModNameMod struct { Id string `json:"id"` @@ -245,51 +306,51 @@ type ModFields string const ( ModFieldsCreatedAt ModFields = "created_at" + ModFieldsUpdatedAt ModFields = "updated_at" + ModFieldsName ModFields = "name" + ModFieldsViews ModFields = "views" ModFieldsDownloads ModFields = "downloads" ModFieldsHotness ModFields = "hotness" - ModFieldsLastVersionDate ModFields = "last_version_date" - ModFieldsName ModFields = "name" ModFieldsPopularity ModFields = "popularity" + ModFieldsLastVersionDate ModFields = "last_version_date" ModFieldsSearch ModFields = "search" - ModFieldsUpdatedAt ModFields = "updated_at" - ModFieldsViews ModFields = "views" ) type ModFilter struct { - Hidden bool `json:"hidden,omitempty"` - Ids []string `json:"ids,omitempty"` Limit int `json:"limit,omitempty"` Offset int `json:"offset,omitempty"` - Order Order `json:"order,omitempty"` Order_by ModFields `json:"order_by,omitempty"` - References []string `json:"references,omitempty"` + Order Order `json:"order,omitempty"` Search string `json:"search,omitempty"` + Ids []string `json:"ids,omitempty"` + References []string `json:"references,omitempty"` + Hidden bool `json:"hidden,omitempty"` TagIDs []string `json:"tagIDs,omitempty"` } -// GetHidden returns ModFilter.Hidden, and is useful for accessing the field via an interface. -func (v *ModFilter) GetHidden() bool { return v.Hidden } - -// GetIds returns ModFilter.Ids, and is useful for accessing the field via an interface. -func (v *ModFilter) GetIds() []string { return v.Ids } - // GetLimit returns ModFilter.Limit, and is useful for accessing the field via an interface. func (v *ModFilter) GetLimit() int { return v.Limit } // GetOffset returns ModFilter.Offset, and is useful for accessing the field via an interface. func (v *ModFilter) GetOffset() int { return v.Offset } +// GetOrder_by returns ModFilter.Order_by, and is useful for accessing the field via an interface. +func (v *ModFilter) GetOrder_by() ModFields { return v.Order_by } + // GetOrder returns ModFilter.Order, and is useful for accessing the field via an interface. func (v *ModFilter) GetOrder() Order { return v.Order } -// GetOrder_by returns ModFilter.Order_by, and is useful for accessing the field via an interface. -func (v *ModFilter) GetOrder_by() ModFields { return v.Order_by } +// GetSearch returns ModFilter.Search, and is useful for accessing the field via an interface. +func (v *ModFilter) GetSearch() string { return v.Search } + +// GetIds returns ModFilter.Ids, and is useful for accessing the field via an interface. +func (v *ModFilter) GetIds() []string { return v.Ids } // GetReferences returns ModFilter.References, and is useful for accessing the field via an interface. func (v *ModFilter) GetReferences() []string { return v.References } -// GetSearch returns ModFilter.Search, and is useful for accessing the field via an interface. -func (v *ModFilter) GetSearch() string { return v.Search } +// GetHidden returns ModFilter.Hidden, and is useful for accessing the field via an interface. +func (v *ModFilter) GetHidden() bool { return v.Hidden } // GetTagIDs returns ModFilter.TagIDs, and is useful for accessing the field via an interface. func (v *ModFilter) GetTagIDs() []string { return v.TagIDs } @@ -503,7 +564,7 @@ func (v *ModsModsGetModsModsMod) UnmarshalJSON(b []byte) error { src, dst) if err != nil { return fmt.Errorf( - "Unable to unmarshal ModsModsGetModsModsMod.Last_version_date: %w", err) + "unable to unmarshal ModsModsGetModsModsMod.Last_version_date: %w", err) } } } @@ -516,7 +577,7 @@ func (v *ModsModsGetModsModsMod) UnmarshalJSON(b []byte) error { src, dst) if err != nil { return fmt.Errorf( - "Unable to unmarshal ModsModsGetModsModsMod.Created_at: %w", err) + "unable to unmarshal ModsModsGetModsModsMod.Created_at: %w", err) } } } @@ -566,7 +627,7 @@ func (v *ModsModsGetModsModsMod) __premarshalJSON() (*__premarshalModsModsGetMod &src) if err != nil { return nil, fmt.Errorf( - "Unable to marshal ModsModsGetModsModsMod.Last_version_date: %w", err) + "unable to marshal ModsModsGetModsModsMod.Last_version_date: %w", err) } } { @@ -578,7 +639,7 @@ func (v *ModsModsGetModsModsMod) __premarshalJSON() (*__premarshalModsModsGetMod &src) if err != nil { return nil, fmt.Errorf( - "Unable to marshal ModsModsGetModsModsMod.Created_at: %w", err) + "unable to marshal ModsModsGetModsModsMod.Created_at: %w", err) } } retval.Views = v.Views @@ -683,46 +744,46 @@ func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVer type TargetName string const ( - TargetNameLinuxserver TargetName = "LinuxServer" TargetNameWindows TargetName = "Windows" TargetNameWindowsserver TargetName = "WindowsServer" + TargetNameLinuxserver TargetName = "LinuxServer" ) type VersionFields string const ( VersionFieldsCreatedAt VersionFields = "created_at" - VersionFieldsDownloads VersionFields = "downloads" VersionFieldsUpdatedAt VersionFields = "updated_at" + VersionFieldsDownloads VersionFields = "downloads" ) type VersionFilter struct { - Ids []string `json:"ids,omitempty"` Limit int `json:"limit,omitempty"` Offset int `json:"offset,omitempty"` - Order Order `json:"order,omitempty"` Order_by VersionFields `json:"order_by,omitempty"` + Order Order `json:"order,omitempty"` Search string `json:"search,omitempty"` + Ids []string `json:"ids,omitempty"` } -// GetIds returns VersionFilter.Ids, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetIds() []string { return v.Ids } - // GetLimit returns VersionFilter.Limit, and is useful for accessing the field via an interface. func (v *VersionFilter) GetLimit() int { return v.Limit } // GetOffset returns VersionFilter.Offset, and is useful for accessing the field via an interface. func (v *VersionFilter) GetOffset() int { return v.Offset } -// GetOrder returns VersionFilter.Order, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetOrder() Order { return v.Order } - // GetOrder_by returns VersionFilter.Order_by, and is useful for accessing the field via an interface. func (v *VersionFilter) GetOrder_by() VersionFields { return v.Order_by } +// GetOrder returns VersionFilter.Order, and is useful for accessing the field via an interface. +func (v *VersionFilter) GetOrder() Order { return v.Order } + // GetSearch returns VersionFilter.Search, and is useful for accessing the field via an interface. func (v *VersionFilter) GetSearch() string { return v.Search } +// GetIds returns VersionFilter.Ids, and is useful for accessing the field via an interface. +func (v *VersionFilter) GetIds() []string { return v.Ids } + // VersionMod includes the requested fields of the GraphQL type Mod. type VersionMod struct { Id string `json:"id"` @@ -863,15 +924,8 @@ func (v *__VersionInput) GetModId() string { return v.ModId } // GetVersion returns __VersionInput.Version, and is useful for accessing the field via an interface. func (v *__VersionInput) GetVersion() string { return v.Version } -func CheckVersionUploadState( - ctx context.Context, - client graphql.Client, - modId string, - versionId string, -) (*CheckVersionUploadStateResponse, error) { - req := &graphql.Request{ - OpName: "CheckVersionUploadState", - Query: ` +// The query or mutation executed by CheckVersionUploadState. +const CheckVersionUploadState_Operation = ` query CheckVersionUploadState ($modId: ModID!, $versionId: VersionID!) { state: checkVersionUploadState(modId: $modId, versionId: $versionId) { auto_approved @@ -880,7 +934,17 @@ query CheckVersionUploadState ($modId: ModID!, $versionId: VersionID!) { } } } -`, +` + +func CheckVersionUploadState( + ctx context.Context, + client graphql.Client, + modId string, + versionId string, +) (*CheckVersionUploadStateResponse, error) { + req := &graphql.Request{ + OpName: "CheckVersionUploadState", + Query: CheckVersionUploadState_Operation, Variables: &__CheckVersionUploadStateInput{ ModId: modId, VersionId: versionId, @@ -900,6 +964,13 @@ query CheckVersionUploadState ($modId: ModID!, $versionId: VersionID!) { return &data, err } +// The query or mutation executed by CreateVersion. +const CreateVersion_Operation = ` +mutation CreateVersion ($modId: ModID!) { + versionID: createVersion(modId: $modId) +} +` + func CreateVersion( ctx context.Context, client graphql.Client, @@ -907,11 +978,7 @@ func CreateVersion( ) (*CreateVersionResponse, error) { req := &graphql.Request{ OpName: "CreateVersion", - Query: ` -mutation CreateVersion ($modId: ModID!) { - versionID: createVersion(modId: $modId) -} -`, + Query: CreateVersion_Operation, Variables: &__CreateVersionInput{ ModId: modId, }, @@ -930,6 +997,13 @@ mutation CreateVersion ($modId: ModID!) { return &data, err } +// The query or mutation executed by FinalizeCreateVersion. +const FinalizeCreateVersion_Operation = ` +mutation FinalizeCreateVersion ($modId: ModID!, $versionId: VersionID!, $version: NewVersion!) { + success: finalizeCreateVersion(modId: $modId, versionId: $versionId, version: $version) +} +` + func FinalizeCreateVersion( ctx context.Context, client graphql.Client, @@ -939,11 +1013,7 @@ func FinalizeCreateVersion( ) (*FinalizeCreateVersionResponse, error) { req := &graphql.Request{ OpName: "FinalizeCreateVersion", - Query: ` -mutation FinalizeCreateVersion ($modId: ModID!, $versionId: VersionID!, $version: NewVersion!) { - success: finalizeCreateVersion(modId: $modId, versionId: $versionId, version: $version) -} -`, + Query: FinalizeCreateVersion_Operation, Variables: &__FinalizeCreateVersionInput{ ModId: modId, VersionId: versionId, @@ -964,14 +1034,8 @@ mutation FinalizeCreateVersion ($modId: ModID!, $versionId: VersionID!, $version return &data, err } -func GetMod( - ctx context.Context, - client graphql.Client, - modId string, -) (*GetModResponse, error) { - req := &graphql.Request{ - OpName: "GetMod", - Query: ` +// The query or mutation executed by GetMod. +const GetMod_Operation = ` query GetMod ($modId: String!) { mod: getModByIdOrReference(modIdOrReference: $modId) { id @@ -985,12 +1049,31 @@ query GetMod ($modId: String!) { username } } + compatibility { + EA { + note + state + } + EXP { + note + state + } + } full_description source_url created_at } } -`, +` + +func GetMod( + ctx context.Context, + client graphql.Client, + modId string, +) (*GetModResponse, error) { + req := &graphql.Request{ + OpName: "GetMod", + Query: GetMod_Operation, Variables: &__GetModInput{ ModId: modId, }, @@ -1009,14 +1092,8 @@ query GetMod ($modId: String!) { return &data, err } -func GetModName( - ctx context.Context, - client graphql.Client, - modId string, -) (*GetModNameResponse, error) { - req := &graphql.Request{ - OpName: "GetModName", - Query: ` +// The query or mutation executed by GetModName. +const GetModName_Operation = ` query GetModName ($modId: String!) { mod: getModByIdOrReference(modIdOrReference: $modId) { id @@ -1024,7 +1101,16 @@ query GetModName ($modId: String!) { name } } -`, +` + +func GetModName( + ctx context.Context, + client graphql.Client, + modId string, +) (*GetModNameResponse, error) { + req := &graphql.Request{ + OpName: "GetModName", + Query: GetModName_Operation, Variables: &__GetModNameInput{ ModId: modId, }, @@ -1043,15 +1129,8 @@ query GetModName ($modId: String!) { return &data, err } -func ModVersions( - ctx context.Context, - client graphql.Client, - modId string, - filter VersionFilter, -) (*ModVersionsResponse, error) { - req := &graphql.Request{ - OpName: "ModVersions", - Query: ` +// The query or mutation executed by ModVersions. +const ModVersions_Operation = ` query ModVersions ($modId: String!, $filter: VersionFilter) { mod: getModByIdOrReference(modIdOrReference: $modId) { id @@ -1061,7 +1140,17 @@ query ModVersions ($modId: String!, $filter: VersionFilter) { } } } -`, +` + +func ModVersions( + ctx context.Context, + client graphql.Client, + modId string, + filter VersionFilter, +) (*ModVersionsResponse, error) { + req := &graphql.Request{ + OpName: "ModVersions", + Query: ModVersions_Operation, Variables: &__ModVersionsInput{ ModId: modId, Filter: filter, @@ -1081,14 +1170,8 @@ query ModVersions ($modId: String!, $filter: VersionFilter) { return &data, err } -func ModVersionsWithDependencies( - ctx context.Context, - client graphql.Client, - modId string, -) (*ModVersionsWithDependenciesResponse, error) { - req := &graphql.Request{ - OpName: "ModVersionsWithDependencies", - Query: ` +// The query or mutation executed by ModVersionsWithDependencies. +const ModVersionsWithDependencies_Operation = ` query ModVersionsWithDependencies ($modId: String!) { mod: getModByIdOrReference(modIdOrReference: $modId) { id @@ -1110,7 +1193,16 @@ query ModVersionsWithDependencies ($modId: String!) { } } } -`, +` + +func ModVersionsWithDependencies( + ctx context.Context, + client graphql.Client, + modId string, +) (*ModVersionsWithDependenciesResponse, error) { + req := &graphql.Request{ + OpName: "ModVersionsWithDependencies", + Query: ModVersionsWithDependencies_Operation, Variables: &__ModVersionsWithDependenciesInput{ ModId: modId, }, @@ -1129,14 +1221,8 @@ query ModVersionsWithDependencies ($modId: String!) { return &data, err } -func Mods( - ctx context.Context, - client graphql.Client, - filter ModFilter, -) (*ModsResponse, error) { - req := &graphql.Request{ - OpName: "Mods", - Query: ` +// The query or mutation executed by Mods. +const Mods_Operation = ` query Mods ($filter: ModFilter) { mods: getMods(filter: $filter) { count @@ -1153,7 +1239,16 @@ query Mods ($filter: ModFilter) { } } } -`, +` + +func Mods( + ctx context.Context, + client graphql.Client, + filter ModFilter, +) (*ModsResponse, error) { + req := &graphql.Request{ + OpName: "Mods", + Query: Mods_Operation, Variables: &__ModsInput{ Filter: filter, }, @@ -1172,13 +1267,8 @@ query Mods ($filter: ModFilter) { return &data, err } -func SMLVersions( - ctx context.Context, - client graphql.Client, -) (*SMLVersionsResponse, error) { - req := &graphql.Request{ - OpName: "SMLVersions", - Query: ` +// The query or mutation executed by SMLVersions. +const SMLVersions_Operation = ` query SMLVersions { smlVersions: getSMLVersions(filter: {limit:100}) { count @@ -1193,7 +1283,15 @@ query SMLVersions { } } } -`, +` + +func SMLVersions( + ctx context.Context, + client graphql.Client, +) (*SMLVersionsResponse, error) { + req := &graphql.Request{ + OpName: "SMLVersions", + Query: SMLVersions_Operation, } var err error @@ -1209,15 +1307,8 @@ query SMLVersions { return &data, err } -func Version( - ctx context.Context, - client graphql.Client, - modId string, - version string, -) (*VersionResponse, error) { - req := &graphql.Request{ - OpName: "Version", - Query: ` +// The query or mutation executed by Version. +const Version_Operation = ` query Version ($modId: String!, $version: String!) { mod: getModByIdOrReference(modIdOrReference: $modId) { id @@ -1229,7 +1320,17 @@ query Version ($modId: String!, $version: String!) { } } } -`, +` + +func Version( + ctx context.Context, + client graphql.Client, + modId string, + version string, +) (*VersionResponse, error) { + req := &graphql.Request{ + OpName: "Version", + Query: Version_Operation, Variables: &__VersionInput{ ModId: modId, Version: version, diff --git a/tea/scenes/mods/mod_info.go b/tea/scenes/mods/mod_info.go index aa6adb3..4676bab 100644 --- a/tea/scenes/mods/mod_info.go +++ b/tea/scenes/mods/mod_info.go @@ -1,5 +1,7 @@ package mods +// cspell:disable + import ( "context" "log/slog" @@ -23,40 +25,46 @@ import ( "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) +// cspell:enable + var _ tea.Model = (*modVersionMenu)(nil) type modInfo struct { - root components.RootModel - parent tea.Model - modData chan ficsit.GetModMod - modError chan string - error *components.ErrorComponent - help help.Model - keys modInfoKeyMap - viewport viewport.Model - spinner spinner.Model - ready bool + root components.RootModel + parent tea.Model + modData chan ficsit.GetModMod + modDataCache ficsit.GetModMod + modError chan string + error *components.ErrorComponent + help help.Model + keys modInfoKeyMap + viewport viewport.Model + spinner spinner.Model + ready bool + compatViewMode bool } type modInfoKeyMap struct { - Up key.Binding - UpHalf key.Binding - UpPage key.Binding - Down key.Binding - DownHalf key.Binding - DownPage key.Binding - Help key.Binding - Back key.Binding + Up key.Binding + UpHalf key.Binding + UpPage key.Binding + Down key.Binding + DownHalf key.Binding + DownPage key.Binding + Help key.Binding + Back key.Binding + CompatInfo key.Binding } func (k modInfoKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Help, k.Back} + return []key.Binding{k.Help, k.Back, k.Up, k.Down, k.CompatInfo} } func (k modInfoKeyMap) FullHelp() [][]key.Binding { return [][]key.Binding{ {k.Up, k.UpHalf, k.UpPage}, {k.Down, k.DownHalf, k.DownPage}, + {k.CompatInfo}, {k.Help, k.Back}, } } @@ -72,14 +80,15 @@ func NewModInfo(root components.RootModel, parent tea.Model, mod utils.Mod) tea. ready: false, help: help.New(), keys: modInfoKeyMap{ - Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "move up")), - UpHalf: key.NewBinding(key.WithKeys("u"), key.WithHelp("u", "up half page")), - UpPage: key.NewBinding(key.WithKeys("pgup", "b"), key.WithHelp("pgup/b", "page up")), - Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "move down")), - DownHalf: key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "down half page")), - DownPage: key.NewBinding(key.WithKeys("pgdn", "f"), key.WithHelp("pgdn/f", "page down")), - Help: key.NewBinding(key.WithKeys("?"), key.WithHelp("?", "toggle help")), - Back: key.NewBinding(key.WithKeys("q"), key.WithHelp("q", "back")), + Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "move up")), + UpHalf: key.NewBinding(key.WithKeys("u"), key.WithHelp("u", "up half page")), + UpPage: key.NewBinding(key.WithKeys("pgup", "b"), key.WithHelp("pgup/b", "page up")), + Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "move down")), + DownHalf: key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "down half page")), + DownPage: key.NewBinding(key.WithKeys("pgdn", "f"), key.WithHelp("pgdn/f", "page down")), + Help: key.NewBinding(key.WithKeys("?"), key.WithHelp("?", "toggle help")), + Back: key.NewBinding(key.WithKeys("q"), key.WithHelp("q", "back")), + CompatInfo: key.NewBinding(key.WithKeys("i"), key.WithHelp("i", "toggle compatibility info view")), }, } @@ -144,13 +153,19 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit case "?": m.help.ShowAll = !m.help.ShowAll - newModel, cmd := m.CalculateSizes(m.root.Size()) - return newModel, cmd + return m.CalculateSizes(m.root.Size()) + case "i": + m.compatViewMode = !m.compatViewMode + m.viewport = m.newViewport() + m.viewport.SetContent(m.renderModInfo()) + return m.CalculateSizes(m.root.Size()) default: - var cmd tea.Cmd - m.viewport, cmd = m.viewport.Update(msg) - return m, cmd + break } + + var cmd tea.Cmd + m.viewport, cmd = m.viewport.Update(msg) + return m, cmd case tea.WindowSizeMsg: return m.CalculateSizes(msg) case spinner.TickMsg: @@ -160,67 +175,121 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case utils.TickMsg: select { case mod := <-m.modData: - bottomPadding := 2 - if m.help.ShowAll { - bottomPadding = 4 - } - - top, right, bottom, left := lipgloss.NewStyle().Margin(m.root.Height(), 3, bottomPadding).GetMargin() - m.viewport = viewport.Model{Width: m.root.Size().Width - left - right, Height: m.root.Size().Height - top - bottom} - - title := lipgloss.NewStyle().Padding(0, 2).Render(utils.TitleStyle.Render(mod.Name)) + "\n" - - sidebar := "" - sidebar += utils.LabelStyle.Render("Views: ") + strconv.Itoa(mod.Views) + "\n" - sidebar += utils.LabelStyle.Render("Downloads: ") + strconv.Itoa(mod.Downloads) + "\n" - sidebar += "\n" - sidebar += utils.LabelStyle.Render("Authors:") + "\n" - - for _, author := range mod.Authors { - sidebar += "\n" - sidebar += utils.LabelStyle.Render(author.User.Username) + " - " + author.Role - } - - converter := md.NewConverter("", true, nil) - converter.AddRules(md.Rule{ - Filter: []string{"#text"}, - Replacement: func(content string, selec *goquery.Selection, options *md.Options) *string { - text := selec.Text() - return &text - }, - }) - - markdownDescription, err := converter.ConvertString(mod.Full_description) - if err != nil { - slog.Error("failed to convert html to markdown", slog.Any("err", err)) - markdownDescription = mod.Full_description - } - - description, err := glamour.Render(markdownDescription, "dark") - if err != nil { - slog.Error("failed to render markdown", slog.Any("err", err)) - description = mod.Full_description - } - - bottomPart := lipgloss.JoinHorizontal(lipgloss.Top, sidebar, strings.TrimSpace(description)) - - m.viewport.SetContent(lipgloss.JoinVertical(lipgloss.Left, title, bottomPart)) - - var cmd tea.Cmd - m.viewport, cmd = m.viewport.Update(msg) - return m, cmd + m.modDataCache = mod + m.viewport = m.newViewport() + m.viewport.SetContent(m.renderModInfo()) + break case err := <-m.modError: - errorComponent, cmd := components.NewErrorComponent(err, time.Second*5) + errorComponent, _ := components.NewErrorComponent(err, time.Second*5) m.error = errorComponent - return m, cmd + break default: - return m, utils.Ticker() + // skip + break } + return m, utils.Ticker() } return m, nil } +func (m modInfo) newViewport() viewport.Model { + bottomPadding := 2 + if m.help.ShowAll { + bottomPadding = 4 + } + + top, right, bottom, left := lipgloss.NewStyle().Margin(m.root.Height(), 3, bottomPadding).GetMargin() + return viewport.Model{Width: m.root.Size().Width - left - right, Height: m.root.Size().Height - top - bottom} +} + +func (m modInfo) renderModInfo() string { + mod := m.modDataCache + + title := lipgloss.NewStyle().Padding(0, 2).Render(utils.TitleStyle.Render(mod.Name)) + "\n" + title += lipgloss.NewStyle().Padding(0, 3).Render("("+string(mod.Mod_reference)+")") + "\n" + + sidebar := "" + sidebar += utils.LabelStyle.Render("Views: ") + strconv.Itoa(mod.Views) + "\n" + sidebar += utils.LabelStyle.Render("Downloads: ") + strconv.Itoa(mod.Downloads) + "\n" + sidebar += "\n" + sidebar += utils.LabelStyle.Render("EA Compat: ") + m.renderCompatInfo(mod.Compatibility.EA.State) + "\n" + sidebar += utils.LabelStyle.Render("EXP Compat: ") + m.renderCompatInfo(mod.Compatibility.EXP.State) + "\n" + sidebar += "\n" + sidebar += utils.LabelStyle.Render("Authors:") + "\n" + + converter := md.NewConverter("", true, nil) + converter.AddRules(md.Rule{ + Filter: []string{"#text"}, + Replacement: func(content string, selection *goquery.Selection, options *md.Options) *string { + text := selection.Text() + return &text + }, + }) + + for _, author := range mod.Authors { + sidebar += "\n" + sidebar += utils.LabelStyle.Render(author.User.Username) + " - " + author.Role + } + + description := "" + if m.compatViewMode { + a := "" + a += "Compatibility information is maintained by the community." + "\n" + a += "If you encounter issues with a mod, please report it on the Discord." + "\n" + a += "Learn more about what compatibility states mean on ficsit.app" + "\n\n" + + description = m.renderDescriptionText(a, converter) + + description += " " + utils.TitleStyle.Render("Early Access Branch Compatibility Note") + "\n" + description += m.renderDescriptionText(mod.Compatibility.EA.Note, converter) + description += "\n\n" + description += " " + utils.TitleStyle.Render("Experimental Branch Compatibility Note") + "\n" + description += m.renderDescriptionText(mod.Compatibility.EXP.Note, converter) + } else { + description += m.renderDescriptionText(mod.Full_description, converter) + } + + bottomPart := lipgloss.JoinHorizontal(lipgloss.Top, sidebar, strings.TrimSpace(description)) + + return lipgloss.JoinVertical(lipgloss.Left, title, bottomPart) +} + +func (m modInfo) renderDescriptionText(text string, converter *md.Converter) string { + text = strings.TrimSpace(text) + if text == "" { + text = "(No notes provided)" + } + + markdownDescription, err := converter.ConvertString(text) + if err != nil { + slog.Error("failed to convert html to markdown", slog.Any("err", err)) + markdownDescription = text + } + + description, err := glamour.Render(markdownDescription, "dark") + if err != nil { + slog.Error("failed to render markdown", slog.Any("err", err)) + description = text + } + + return description +} + +func (m modInfo) renderCompatInfo(state ficsit.CompatibilityState) string { + stateText := string(state) + switch state { + case ficsit.CompatibilityStateWorks: + return utils.CompatWorksStyle.Render(stateText) + case ficsit.CompatibilityStateDamaged: + return utils.CompatDamagedStyle.Render(stateText) + case ficsit.CompatibilityStateBroken: + return utils.CompatBrokenStyle.Render(stateText) + default: + return utils.CompatUntestedStyle.Render("Unknown") + } +} + func (m modInfo) View() string { if m.error != nil { helpBar := lipgloss.NewStyle().Padding(1, 2).Render(m.help.View(m.keys)) diff --git a/tea/utils/styles.go b/tea/utils/styles.go index 023601c..6882232 100644 --- a/tea/utils/styles.go +++ b/tea/utils/styles.go @@ -12,6 +12,13 @@ var ( NonListTitleStyle = TitleStyle.Copy().MarginLeft(2).Background(lipgloss.Color("#b34100")) ) +var ( + CompatWorksStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#00b12d")) + CompatDamagedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#e69000")) + CompatBrokenStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#e60000")) + CompatUntestedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#838383")) +) + var ( LogoForegroundStyles = []lipgloss.Style{ lipgloss.NewStyle().Foreground(lipgloss.Color("#ff5f00")).Background(lipgloss.Color("#ff5f00")), diff --git a/tools.go b/tools.go index 9de0d43..dc2b940 100644 --- a/tools.go +++ b/tools.go @@ -5,25 +5,80 @@ package main import ( "os" + "os/user" + "path/filepath" + "runtime" + "strings" - _ "github.com/Khan/genqlient/generate" "github.com/spf13/cobra/doc" "github.com/satisfactorymodding/ficsit-cli/cmd" + + _ "github.com/Khan/genqlient/generate" ) //go:generate go run github.com/Khan/genqlient //go:generate go run -tags tools tools.go func main() { + var err error _ = os.RemoveAll("./docs/") - if err := os.Mkdir("./docs/", 0777); err != nil { + if err = os.Mkdir("./docs/", 0o777); err != nil { panic(err) } - err := doc.GenMarkdownTree(cmd.RootCmd, "./docs/") + err = doc.GenMarkdownTree(cmd.RootCmd, "./docs/") if err != nil { panic(err) } + + // replace user dir information with generic username + baseCacheDir, err := os.UserCacheDir() + if err != nil { + panic(err) + } + + var baseLocalDir string + + switch runtime.GOOS { + case "windows": + baseLocalDir = os.Getenv("APPDATA") + case "linux": + baseLocalDir = filepath.Join(os.Getenv("HOME"), ".local", "share") + default: + panic("unsupported platform: " + runtime.GOOS) + } + + docFiles, err := os.ReadDir("./docs/") + if err != nil { + panic(err) + } + + user, err := user.Current() + if err != nil { + panic(err) + } + + for _, f := range docFiles { + fPath := "./docs/" + f.Name() + oldContents, err := os.ReadFile(fPath) + if err != nil { + panic(err) + } + + newContents := strings.ReplaceAll( + string(oldContents), + baseCacheDir, + strings.ReplaceAll(baseCacheDir, user.Username, "{{Username}}"), + ) + + newContents = strings.ReplaceAll( + newContents, + baseLocalDir, + strings.ReplaceAll(baseLocalDir, user.Username, "{{Username}}"), + ) + + os.WriteFile(fPath, []byte(newContents), 0o777) + } }