mirror of
https://github.com/sebastiandine/Card-Collection-Manager-2.git
synced 2026-08-28 17:00:55 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1346db06a6 | |||
| 01e9436a42 | |||
| 2f967a1626 | |||
| f03167a531 | |||
| 7be5f51376 | |||
| 0ce9b09979 | |||
| 9793a13646 | |||
| d6dd72bfb3 | |||
| ae79bae1d6 | |||
| 974b5fb30e | |||
| 5d7ef22cfd | |||
| d49330a4fc | |||
| 75fcba1b28 |
@@ -83,7 +83,7 @@ jobs:
|
||||
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_x64_en-US.msi
|
||||
asset_content_type: application/x-msi
|
||||
|
||||
- name: upload release asset (Linux/Debian)
|
||||
- name: upload release asset (Linux/.deb)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.deb
|
||||
asset_content_type: application/x-debian-package
|
||||
|
||||
- name: upload release asset (Linux/AppImage)
|
||||
- name: upload release asset (Linux/.AppImage)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -105,13 +105,25 @@ jobs:
|
||||
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.AppImage
|
||||
asset_content_type: application/x-executable
|
||||
|
||||
- name: upload release asset (MacOS)
|
||||
- name: upload release asset (MacOS/.dmg)
|
||||
if: matrix.platform == 'macos-latest'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.release_upload_url }}
|
||||
asset_path: card-collection-manager-2/src-tauri/target/release/bundle/dmg/card-collection-manager-2_${{ needs.create-release.outputs.version }}_x64.dmg
|
||||
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_x64.dmg
|
||||
asset_path: card-collection-manager-2/src-tauri/target/release/bundle/dmg/card-collection-manager-2_${{ needs.create-release.outputs.version }}_aarch64.dmg
|
||||
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_aarch64.dmg
|
||||
asset_content_type: application/x-apple-diskimage
|
||||
|
||||
#- name: upload release asset (MacOS/.app)
|
||||
# if: matrix.platform == 'macos-latest'
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ needs.create-release.outputs.release_upload_url }}
|
||||
# asset_path: card-collection-manager-2/src-tauri/target/release/bundle/macos/card-collection-manager-2.app
|
||||
# asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}.app
|
||||
# asset_content_type: application/x-mach-binary
|
||||
|
||||
|
||||
@@ -1,68 +1,90 @@
|
||||
# Card Collection Manager 2
|
||||

|
||||

|
||||
|
||||
## Project Description
|
||||
This projects provides a management application for trading card game collections. Its aim is to be a simple solution to keep track of collections. The main reason why I wrote this application is because I required a tool, which can manage single card images of a large collection, besides common card collection management features.
|
||||
|
||||
This project is the successor of my previous project [Card Collection Manager](https://github.com/sebastiandine/Card-Collection-Manager). The main benefit over the first version of the project is that this version can support multiple card games at once. Right now, it supports [Magic the Gathering](https://magic.wizards.com/) and the [Pokemon TCG](https://tcg.pokemon.com). The project was designed with extensibility in mind, so adding support for additional games is quite easy (about 2 hours of work per game).
|
||||
|
||||
In addition to these functional enhancements, the application was completely rewritten from scratch. While the previous project was Java-based, this version is based on the [Tauri framework](https://tauri.app/), using Rust and Typescript. Therefore, the application is now available as native binary for all three common platform: Windows, Linux and MacOS.
|
||||
|
||||
## Migrate from Card Collection Manager 1
|
||||
You can migrate your Magic the Gathering collection from CCM1 to CCM2 by following these steps:
|
||||
1. Create a JSON export of your collection in CCM1 (`Export -> to JSON (.json)`)
|
||||
2. Copy this file to `<cc2_collection_dir>/magic/collection.json`, where `<cc2_collection_dir>` is the root collection directory of your CCM2 instance. By default, this will be the directory where CCM2 is installed, but you can also specify an arbitrary directory via the CCM2 settings dialog (`File -> Settings`).
|
||||
3. Copy all image files of your collection from `<cc1_collection_dir>/images/` to `<cc2_collection_dir>/magic/images/`, where `<cc1_collection_dir>` is the root collection directory of your CCM1 instance.
|
||||
|
||||
After performing these steps, the next time you open CCM2, you will see your collection in CCM2. Now, you can delete CCM1.
|
||||
|
||||
|
||||
## How to build/ contribute
|
||||
### Contribute
|
||||
The project includes configuration for [VSCode development containers](https://code.visualstudio.com/docs/remote/containers) which should be the preffered environment to develop new features of the app. The container automatically sets up a whole Tauri development environment including Typescript & Rust plugins for VSCode.
|
||||
|
||||
Also, in directory `hooks/` you find some helpful git hooks that automate/standardize some work. You can activate these hooks by executing the script `activate_hooks.sh` from within the `hooks/` directory.
|
||||
|
||||
Additionally, if you want to run the GUI out of the container, you need to use a X11 tool. I will briefly explain how to run them in order to display the GUI from the container:
|
||||
|
||||
**Windows**<br>
|
||||
I recommend [Xming](https://sourceforge.net/projects/xming/) if your host system is Windows ([VcXSrv](https://sourceforge.net/projects/vcxsrv/), which I recommended for the Java-based CCM1 did not work here). Once you have it installed, start it via `Xming.exe` and enable the option `Disable access control` before you start the server. Now, you can start a GUI app in your container that will be displayed via the X-server on the host system.
|
||||
|
||||
**MacOS**<br>
|
||||
Install [XQuartz](https://www.xquartz.org/) and run it via the following command:
|
||||
```
|
||||
xhost +localhost
|
||||
```
|
||||
Now, you can start a GUI app in your container that will be displayed via the X-server on the host system.
|
||||
|
||||
### Local Run & Building
|
||||
* Execute `yarn` to install all NodeJS dependencies when you initially check out the project. Make sure you are in the Tauri project directory.
|
||||
* Execute `yarn tauri dev` to run the application in development mode. Make sure you are in the Tauri project directory. If you run this for the first time, it will quite long since it needs to fetch all Rust-based dependencies and build corresponding binaries. If you make changes to the Rust code of the project, it will also take a while (but not as long as the initial run), since it has to recompile binaries.
|
||||
* Execute `yarn tauri build` to build the application. Right now, Tauri only supports building for the local architecture. Since the development container is based on Linux, this means you will build Linux packages via this command.
|
||||
|
||||
### Remote Building
|
||||
Actual versions are automatically built via GitHub Action pipelines defined at [`.github/workflows`](./.github/workflows/).
|
||||
|
||||
## Toolkit
|
||||
### Base
|
||||
* [Tauri](https://tauri.app/) framework with [Rust](https://www.rust-lang.org/) for backend/internal logic and [Typescript](https://www.typescriptlang.org/) for the UI.
|
||||
|
||||
### Frontend (TS)
|
||||
* [Next.JS](https://nextjs.org/) as UI framework.
|
||||
* [Tailwind CSS](https://tailwindcss.com/) for UI component styling.
|
||||
* [React Icons](https://react-icons.github.io/react-icons/)
|
||||
* [React Spinner](https://mhnpd.github.io/react-loader-spinner/docs/intro)
|
||||
|
||||
### Backend (Rust)
|
||||
* [serde](https://crates.io/crates/serde) for data serialization/deserialization.
|
||||
* [serde_json](https://crates.io/crates/serde_json) for data serialization/deserialization to and from JSON.
|
||||
* [image](https://crates.io/crates/image) to work with image files.
|
||||
* [base64](https://crates.io/crates/base64) to convert image files to base64 encoded strings to send them from backend to frontend.
|
||||
* [reqwest](https://crates.io/crates/reqwest) for REST calls to game-specific APIs to fetch set and card data.
|
||||
* [strum](https://crates.io/crates/strum) for additional macros for enums and strings.
|
||||
* [nom](https://crates.io/crates/nom) for type checks.
|
||||
|
||||
### Additional Resources
|
||||
* [Scryfall API](https://scryfall.com/docs/api) for fetching card and set data of Magic the Gathering, including card preview images.
|
||||
* [Pokemon TCG API](https://docs.pokemontcg.io/) for fetching card and set data of the Pokemon TCG, including card preview images.
|
||||
# Card Collection Manager 2
|
||||
|
||||
[](https://github.com/sebastiandine/Card-Collection-Manager-3)
|
||||
|
||||
> [!WARNING]
|
||||
> This project (**CCM2**) is deprecated and no longer actively developed.
|
||||
> Development continues in **CCM3**: https://github.com/sebastiandine/Card-Collection-Manager-3
|
||||
|
||||

|
||||

|
||||
|
||||
## Project Description
|
||||
This projects provides a management application for trading card game collections. Its aim is to be a simple solution to keep track of collections. The main reason why I wrote this application is because I required a tool, which can manage single card images of a large collection, besides common card collection management features.
|
||||
|
||||
This project is the successor of my previous project [Card Collection Manager](https://github.com/sebastiandine/Card-Collection-Manager). The main benefit over the first version of the project is that this version can support multiple card games at once. Right now, it supports [Magic the Gathering](https://magic.wizards.com/) and the [Pokemon TCG](https://tcg.pokemon.com). The project was designed with extensibility in mind, so adding support for additional games is quite easy (about 2 hours of work per game).
|
||||
|
||||
In addition to these functional enhancements, the application was completely rewritten from scratch. While the previous project was Java-based, this version is based on the [Tauri framework](https://tauri.app/), using Rust and Typescript. Therefore, the application is now available as native binary for all three common platform: Windows, Linux and MacOS.
|
||||
|
||||
## Migrate from Card Collection Manager 1
|
||||
You can migrate your Magic the Gathering collection from CCM1 to CCM2 by following these steps:
|
||||
1. Create a JSON export of your collection in CCM1 (`Export -> to JSON (.json)`)
|
||||
2. Copy this file to `<cc2_collection_dir>/magic/collection.json`, where `<cc2_collection_dir>` is the root collection directory of your CCM2 instance. By default, this will be the directory where CCM2 is installed, but you can also specify an arbitrary directory via the CCM2 settings dialog (`File -> Settings`).
|
||||
3. Copy all image files of your collection from `<cc1_collection_dir>/images/` to `<cc2_collection_dir>/magic/images/`, where `<cc1_collection_dir>` is the root collection directory of your CCM1 instance.
|
||||
|
||||
After performing these steps, the next time you open CCM2, you will see your collection in CCM2. Now, you can delete CCM1.
|
||||
|
||||
|
||||
## How to build/ contribute
|
||||
|
||||
### Creating a new version
|
||||
To create a new version that is properly picked up by the pipeline to create a new release, adjust the version in the following files:
|
||||
```
|
||||
version
|
||||
card-collection-manager-2/package.json
|
||||
card-collection-manager-2/src-tauri/tauri.conf.json
|
||||
card-collection-manager-2/src-tauri/Cargo.toml
|
||||
```
|
||||
|
||||
### Contribute
|
||||
The project includes configuration for [VSCode development containers](https://code.visualstudio.com/docs/remote/containers) which should be the preffered environment to develop new features of the app. The container automatically sets up a whole Tauri development environment including Typescript & Rust plugins for VSCode.
|
||||
|
||||
Also, in directory `hooks/` you find some helpful git hooks that automate/standardize some work. You can activate these hooks by executing the script `activate_hooks.sh` from within the `hooks/` directory. If you are receiving errors like `fatal: cannot run .git/hooks/pre-commit: No such file or directory`, check the line-end sequence of the hook scripts, adjust them according to your system and run `activate_hooks.sh` again. The hooks are especially useful for versioning. They ensure that the version number in each of the 3 manifest files are the same and create a `version` file with the current version at the project's root level, which can be picked up easily by automated CI/CD tasks.
|
||||
|
||||
Additionally, if you want to run the GUI out of the container, you need to use a X11 tool. I will briefly explain how to run them in order to display the GUI from the container:
|
||||
|
||||
**Windows**<br>
|
||||
I recommend [Xming](https://sourceforge.net/projects/xming/) if your host system is Windows ([VcXSrv](https://sourceforge.net/projects/vcxsrv/), which I recommended for the Java-based CCM1 did not work here). Once you have it installed, start it via `Xming.exe` and enable the option `Disable access control` before you start the server. Now, you can start a GUI app in your container that will be displayed via the X-server on the host system.
|
||||
|
||||
**MacOS**<br>
|
||||
Install [XQuartz](https://www.xquartz.org/) and run it via the following command:
|
||||
```
|
||||
xhost +localhost
|
||||
```
|
||||
Now, you can start a GUI app in your container that will be displayed via the X-server on the host system.
|
||||
|
||||
### Local Run & Building
|
||||
* Execute `yarn` to install all NodeJS dependencies when you initially check out the project. Make sure you are in the Tauri project directory.
|
||||
* Execute `yarn tauri dev` to run the application in development mode. Make sure you are in the Tauri project directory. If you run this for the first time, this will take quite a long time, since it needs to fetch all Rust-based dependencies and build corresponding binaries. If you make changes to the Rust code of the project, it will also take a while (but not as long as the initial run), since it has to recompile binaries.
|
||||
* Execute `yarn tauri build` to build the application. Right now, Tauri only supports building for the local architecture. Since the development container is based on Linux, this means you will build Linux packages via this command.
|
||||
|
||||
### Remote Building
|
||||
Actual versions are automatically built via GitHub Action pipelines defined at [`.github/workflows`](./.github/workflows/).
|
||||
|
||||
## Toolkit
|
||||
### Base
|
||||
* [Tauri](https://tauri.app/) framework with [Rust](https://www.rust-lang.org/) for backend/internal logic and [Typescript](https://www.typescriptlang.org/) for the UI.
|
||||
|
||||
### Frontend (TS)
|
||||
* [Next.JS](https://nextjs.org/) as UI framework.
|
||||
* [Tailwind CSS](https://tailwindcss.com/) for UI component styling.
|
||||
* [React Icons](https://react-icons.github.io/react-icons/)
|
||||
* [React Spinner](https://mhnpd.github.io/react-loader-spinner/docs/intro)
|
||||
|
||||
### Backend (Rust)
|
||||
* [serde](https://crates.io/crates/serde) for data serialization/deserialization.
|
||||
* [serde_json](https://crates.io/crates/serde_json) for data serialization/deserialization to and from JSON.
|
||||
* [image](https://crates.io/crates/image) to work with image files.
|
||||
* [base64](https://crates.io/crates/base64) to convert image files to base64 encoded strings to send them from backend to frontend.
|
||||
* [reqwest](https://crates.io/crates/reqwest) for REST calls to game-specific APIs to fetch set and card data.
|
||||
* [strum](https://crates.io/crates/strum) for additional macros for enums and strings.
|
||||
* [nom](https://crates.io/crates/nom) for type checks.
|
||||
|
||||
### Additional Resources
|
||||
* [Scryfall API](https://scryfall.com/docs/api) for fetching card and set data of Magic the Gathering, including card preview images.
|
||||
* [Pokemon TCG API](https://docs.pokemontcg.io/) for fetching card and set data of the Pokemon TCG, including card preview images.
|
||||
|
||||
### Further Reading
|
||||
* [Tauri GitHub Actions Templates](https://github.com/tauri-apps/tauri-action)
|
||||
* [Rust Serialization Framework](https://github.com/serde-rs/serde)
|
||||
* [Tauri Command Docs](https://tauri.app/v1/guides/features/command)
|
||||
|
||||
Regular → Executable
+28
-28
@@ -1,28 +1,28 @@
|
||||
{
|
||||
"name": "card-collection-manager-2",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev -p 1420",
|
||||
"build": "next build && next export -o dist",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.1.0",
|
||||
"next": "^12.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.6.0",
|
||||
"react-loader-spinner": "^5.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.1.0",
|
||||
"@types/node": "^18.7.11",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.18",
|
||||
"tailwindcss": "^3.2.2",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "card-collection-manager-2",
|
||||
"private": true,
|
||||
"version": "1.0.4",
|
||||
"scripts": {
|
||||
"dev": "next dev -p 1420",
|
||||
"build": "next build && next export -o dist",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.1.0",
|
||||
"next": "^12.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.6.0",
|
||||
"react-loader-spinner": "^5.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.1.0",
|
||||
"@types/node": "^18.7.11",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.18",
|
||||
"tailwindcss": "^3.2.2",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -203,7 +203,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "card-collection-manager-2"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"image",
|
||||
|
||||
Regular → Executable
+42
-42
@@ -1,42 +1,42 @@
|
||||
[package]
|
||||
name = "card-collection-manager-2"
|
||||
version = "1.0.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.1", features = ["api-all"] }
|
||||
|
||||
# b64 image encoding
|
||||
base64 = "0.13"
|
||||
image = "0.24"
|
||||
|
||||
# rest calls
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
|
||||
# iterator for enums
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
|
||||
# data type checker, e.g. is_digit
|
||||
nom = "7.1.1"
|
||||
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
default = [ "custom-protocol" ]
|
||||
# this feature is used used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
[package]
|
||||
name = "card-collection-manager-2"
|
||||
version = "1.0.4"
|
||||
description = "A Tauri App"
|
||||
authors = ["Sebastian Dine"]
|
||||
license = ""
|
||||
repository = ""
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.1", features = ["api-all"] }
|
||||
|
||||
# b64 image encoding
|
||||
base64 = "0.13"
|
||||
image = "0.24"
|
||||
|
||||
# rest calls
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
|
||||
# iterator for enums
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
|
||||
# data type checker, e.g. is_digit
|
||||
nom = "7.1.1"
|
||||
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
default = [ "custom-protocol" ]
|
||||
# this feature is used used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"dataStorage":"/home/dev/cards","defaultGame":"Magic"}
|
||||
@@ -14,7 +14,12 @@ pub fn format_text_for_fs(text: &String) -> String {
|
||||
.replace(":", "-")
|
||||
.replace("&", "And")
|
||||
.replace("|", "Or")
|
||||
.replace("á", "a")
|
||||
.replace("é", "e")
|
||||
.replace("í", "i")
|
||||
.replace("ó", "o")
|
||||
.replace("ú", "u")
|
||||
.replace("û", "u")
|
||||
}
|
||||
|
||||
/// Parse an index from the end of a filename. This function supports indices with
|
||||
|
||||
Regular → Executable
+67
-67
@@ -1,67 +1,67 @@
|
||||
{
|
||||
"build": {
|
||||
"beforeDevCommand": "yarn dev",
|
||||
"beforeBuildCommand": "yarn build",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../dist",
|
||||
"withGlobalTauri": false
|
||||
},
|
||||
"package": {
|
||||
"productName": "card-collection-manager-2",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.sdine.dev",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"title": "Card Collection Manager 2",
|
||||
"minWidth": 1300,
|
||||
"minHeight": 900,
|
||||
"width": 1300,
|
||||
"height": 900
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"build": {
|
||||
"beforeDevCommand": "yarn dev",
|
||||
"beforeBuildCommand": "yarn build",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../dist",
|
||||
"withGlobalTauri": false
|
||||
},
|
||||
"package": {
|
||||
"productName": "card-collection-manager-2",
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.sdine.dev",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"title": "Card Collection Manager 2",
|
||||
"minWidth": 1300,
|
||||
"minHeight": 900,
|
||||
"width": 1300,
|
||||
"height": 900
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
|
||||
import { GrNext, GrPrevious } from "react-icons/gr";
|
||||
import { RotatingLines } from "react-loader-spinner";
|
||||
|
||||
import ModalTemplate from "../templates/ModalTemplate";
|
||||
import Image from "next/image";
|
||||
import CardImageWithLoader from "../templates/CardImageWithLoader";
|
||||
|
||||
/**
|
||||
* Modal to display a set of images.
|
||||
@@ -25,43 +23,24 @@ const ImageModal: React.FC<{
|
||||
startIndex?: number;
|
||||
}> = (props) => {
|
||||
|
||||
// current image as base-64 string
|
||||
const [imageB64, setImageB64] = useState<string>("");
|
||||
// index of current image in `props.images`
|
||||
const [imageIndex, setImageIndex] = useState<number>(0);
|
||||
// flag to display the loading image
|
||||
const [loaderVisible, setLoaderVisible] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.visible && props.images) {
|
||||
setLoaderVisible(true);
|
||||
setImageIndex(props.startIndex | 0);
|
||||
loadImage(props.startIndex | 0);
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
const loadImage = async (id: number) => {
|
||||
invoke("get_image_b64", { image: props.images[id], game: props.game }).then(
|
||||
(result) => {
|
||||
setImageB64(result as string);
|
||||
setLoaderVisible(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const loadNextImage = () => {
|
||||
if(imageIndex < props.images.length -1){
|
||||
setLoaderVisible(true);
|
||||
setImageIndex(imageIndex + 1);
|
||||
loadImage(imageIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const loadPreviousImage = () => {
|
||||
if(imageIndex > 0){
|
||||
setLoaderVisible(true);
|
||||
setImageIndex(imageIndex - 1);
|
||||
loadImage(imageIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,20 +53,8 @@ const ImageModal: React.FC<{
|
||||
modalStyle=" w-[80%] h-[80%]"
|
||||
>
|
||||
<div className="relative h-[90%] mx-8">
|
||||
{loaderVisible
|
||||
?
|
||||
<div className="w-full h-[90%] flex items-center justify-center">
|
||||
<RotatingLines
|
||||
strokeColor="grey"
|
||||
strokeWidth="5"
|
||||
animationDuration="0.75"
|
||||
width="96"
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<Image src={imageB64} layout="fill" objectFit="contain" />
|
||||
}
|
||||
|
||||
<CardImageWithLoader images={props.images} index={imageIndex} game={props.game} />
|
||||
|
||||
{/* next/previous image icons */}
|
||||
{props.images.length > 1 ? (
|
||||
<div className="absolute flex justify-between z-20 top-[50%] w-full">
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import React, { Suspense } from "react";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import Image from "next/image";
|
||||
|
||||
import SpinnerLoader from "./SpinnerLoader";
|
||||
|
||||
// Promise wrapper that acts as an interface for a component to work with React.Suspense.
|
||||
const wrapPromise = <T,>(promise: Promise<T>) => {
|
||||
let status = 'pending'; // set initial status
|
||||
let result: T;
|
||||
let error: any;
|
||||
|
||||
// resolve promise
|
||||
let suspender = promise.then(
|
||||
(res) => {
|
||||
status = 'success';
|
||||
result = res;
|
||||
},
|
||||
(err) => {
|
||||
status = 'error';
|
||||
error = err;
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
read() {
|
||||
switch (status) {
|
||||
case 'pending': throw suspender;
|
||||
case 'error': throw error;
|
||||
default: return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Get the promise that returns the image data as base-64 string once it resolves.
|
||||
const loadImagePromise = (images: string[], index: number, game: string) => {
|
||||
return invoke("get_image_b64", { image: images[index], game: game})
|
||||
.then(res => res as string)
|
||||
.catch(err => err)
|
||||
}
|
||||
|
||||
// wrap image data promise inside the "Suspense interface"
|
||||
const loadImageWrapped = (images: string[], index: number, game: string) => {
|
||||
const imagePromise = loadImagePromise(images, index, game);
|
||||
return wrapPromise(imagePromise);
|
||||
};
|
||||
|
||||
// Image component that can be used with React.Suspense
|
||||
const CardImageSuspendable: React.FC<{ resource: any }> = (props) => {
|
||||
const imageB64: string = props.resource.read();
|
||||
return (
|
||||
<Image src={imageB64} layout="fill" objectFit="contain" />
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* This component loads an image from the backend and displays it. It uses React.Suspense in order
|
||||
* to display a loading animation until the image data is loaded.
|
||||
*
|
||||
* # Props:
|
||||
* * game - Name of the game for which images should be displayed.
|
||||
* * images - List of image names. From this list, one image is loaded and displayed at a time.
|
||||
* * index - index that indicates which element from `props.images` should be displayed.
|
||||
*/
|
||||
const CardImageWithLoader: React.FC<{images: string[], index: number, game: string}> = (props) => {
|
||||
|
||||
return (
|
||||
<Suspense fallback={<SpinnerLoader/>}>
|
||||
<CardImageSuspendable resource={loadImageWrapped(props.images, props.index, props.game)} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardImageWithLoader;
|
||||
@@ -300,7 +300,7 @@ const CreateEditModalTemplate: React.FC<{
|
||||
</select>
|
||||
|
||||
<label className="text-sm col-span-1">Set No.</label>
|
||||
<input type="number"
|
||||
<input type="text"
|
||||
className="text-sm col-span-2 border-2"
|
||||
defaultValue={""}
|
||||
ref={setNoRef}
|
||||
|
||||
@@ -1,8 +1,37 @@
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
/**
|
||||
* HTMLInput Element for numeric values with additional validation so that only positive integer
|
||||
* Input element for numeric values with additional validation so that only positive integer
|
||||
* values can be entered.
|
||||
*
|
||||
* Optional Props:
|
||||
* ===============
|
||||
* - min (number) minimal value of field's value (default: 1)
|
||||
* - step (number) step/Distance between successive values (default: 1)
|
||||
* - defaultValue (number) default value of the field (default: 1)
|
||||
* - className (string) component styling (default: "")
|
||||
* - required (boolean) indicator, wheter this field is mandatory (default: false)
|
||||
* - ref (React.MutableRefObject) useRef hook to perist the value between rendering (default: null)
|
||||
*
|
||||
* Usage example:
|
||||
* ==============
|
||||
* ```
|
||||
* import React, { useRef }from "react";
|
||||
*
|
||||
* const UsageExample: React.FC<{}> = () => {
|
||||
* const inputRef = useRef<HTMLInputElement>(null);
|
||||
*
|
||||
* const printValue = () => {
|
||||
* console.log(Number.parseInt(inputRef.current!.value))
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <IntegerInput ref={inputRef} />
|
||||
* <button onClick(printValue)>Click</button>
|
||||
* <div>
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
|
||||
const IntegerInput = forwardRef((props: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, ref: React.MutableRefObject<HTMLInputElement>) => {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import { RotatingLines } from "react-loader-spinner";
|
||||
|
||||
/**
|
||||
* Simple loading animation with rotating lines.
|
||||
* see also: https://mhnpd.github.io/react-loader-spinner/docs/components/rotating-lines
|
||||
*/
|
||||
const SpinnerLoader: React.FC<{}> = () => {
|
||||
return (
|
||||
<div className="w-full h-[90%] flex items-center justify-center">
|
||||
<RotatingLines
|
||||
strokeColor="grey"
|
||||
strokeWidth="5"
|
||||
animationDuration="0.75"
|
||||
width="96"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpinnerLoader;
|
||||
@@ -1 +0,0 @@
|
||||
1.0.0
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/sh
|
||||
#!/bin/sh
|
||||
cp ./pre-commit ../.git/hooks
|
||||
+10
-7
@@ -1,26 +1,29 @@
|
||||
#!/bin/sh
|
||||
PROJECT_ROOT=$(pwd)
|
||||
|
||||
# check if version and project name are the same in frontend and backend manifest
|
||||
# Check if version and project name are the same in all manifest files.
|
||||
# TAURI_VERSION is the version that is actually used by the tauri build command,
|
||||
# but to avoid any misconception, versions in all manifests should be the same.
|
||||
cd $PROJECT_ROOT && cd card-collection-manager-2/src-tauri
|
||||
CARGO_VERSION=$(cargo read-manifest | jq -r .version)
|
||||
CARGO_NAME=$(cargo read-manifest | jq -r .name)
|
||||
TAURI_VERSION=$(cat tauri.conf.json | jq -r .package.version)
|
||||
TAURI_NAME=$(cat tauri.conf.json | jq -r .package.productName)
|
||||
cd $PROJECT_ROOT && cd card-collection-manager-2
|
||||
YARN_VERSION=$(cat package.json | jq -r .version)
|
||||
YARN_NAME=$(cat package.json | jq -r .name)
|
||||
|
||||
if [ $CARGO_VERSION != $YARN_VERSION ]; then
|
||||
echo "[PRE-COMMIT] Project version in 'package.json' and 'Cargo.toml' does not match."
|
||||
if [ $CARGO_VERSION != $YARN_VERSION ] || [ $CARGO_VERSION != $TAURI_VERSION ] || [ $YARN_VERSION != $TAURI_VERSION ]; then
|
||||
echo "[PRE-COMMIT] Project version in manifests does not match."
|
||||
exit 1
|
||||
fi
|
||||
if [ $CARGO_NAME != $YARN_NAME ]; then
|
||||
echo "[PRE-COMMIT] Project name in 'package.json' and 'Cargo.toml' does not match."
|
||||
if [ $CARGO_NAME != $YARN_NAME ] || [ $CARGO_NAME != $TAURI_NAME ] || [ $YARN_NAME != $TAURI_NAME ]; then
|
||||
echo "[PRE-COMMIT] Project name in manifests does not match."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
cd $PROJECT_ROOT
|
||||
echo $CARGO_VERSION > version
|
||||
echo $TAURI_VERSION > version
|
||||
git add version
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user