13 Commits

Author SHA1 Message Date
Sebastian Dine 1346db06a6 deprecation note 2026-05-09 11:13:10 +02:00
Sebastian Dine 01e9436a42 macos .app removed from pipeline 2024-12-19 18:29:00 +00:00
Sebastian Dine 2f967a1626 macos support for release pipeline adjusted 2024-12-19 17:34:46 +00:00
Sebastian Dine f03167a531 version numbering fix 2024-12-19 17:22:15 +00:00
Sebastian Dine 7be5f51376 version 1.0.4 2024-12-19 17:33:04 +01:00
Sebastian Dine 0ce9b09979 bugfix
Card names with an à led to crashes when images were handled
2024-12-19 17:32:00 +01:00
Sebastian Dine 9793a13646 version 1.0.3 2023-09-05 11:56:04 +00:00
Sebastian Dine d6dd72bfb3 Update fs.rs 2023-09-05 11:48:06 +02:00
Sebastian Dine ae79bae1d6 precommit version adjustments 2023-03-27 09:36:20 +00:00
Sebastian Dine 974b5fb30e Update tauri.conf.json 2023-03-24 17:08:25 +01:00
Sebastian Dine 5d7ef22cfd Update version 2023-03-24 16:47:58 +01:00
Sebastian Dine d49330a4fc Minor adjustment/refactoring
resolves #1
resolves #2
2023-03-18 12:13:50 +01:00
Sebastian Dine 75fcba1b28 Update README.md 2022-12-05 15:33:12 +01:00
17 changed files with 392 additions and 260 deletions
+17 -5
View File
@@ -83,7 +83,7 @@ jobs:
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_x64_en-US.msi asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_x64_en-US.msi
asset_content_type: application/x-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' if: matrix.platform == 'ubuntu-20.04'
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
@@ -94,7 +94,7 @@ jobs:
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.deb asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.deb
asset_content_type: application/x-debian-package 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' if: matrix.platform == 'ubuntu-20.04'
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
@@ -105,13 +105,25 @@ jobs:
asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.AppImage asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_amd64.AppImage
asset_content_type: application/x-executable asset_content_type: application/x-executable
- name: upload release asset (MacOS) - name: upload release asset (MacOS/.dmg)
if: matrix.platform == 'macos-latest' if: matrix.platform == 'macos-latest'
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ needs.create-release.outputs.release_upload_url }} 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_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 }}_x64.dmg asset_name: card-collection-manager-2_${{ needs.create-release.outputs.version }}_aarch64.dmg
asset_content_type: application/x-apple-diskimage 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
Regular → Executable
+24 -2
View File
@@ -1,4 +1,11 @@
# Card Collection Manager 2 # Card Collection Manager 2
[![Deprecated: Continued as CCM3](https://img.shields.io/badge/%E2%84%B9%EF%B8%8F%20Project%20Deprecated-Continued%20as%20CCM3-yellow?style=for-the-badge)](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
![img1](img1.PNG) ![img1](img1.PNG)
![img2](img2.PNG) ![img2](img2.PNG)
@@ -19,10 +26,20 @@ After performing these steps, the next time you open CCM2, you will see your col
## How to build/ contribute ## 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 ### 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. 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. 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: 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:
@@ -38,7 +55,7 @@ Now, you can start a GUI app in your container that will be displayed via the X-
### Local Run & Building ### 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` 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 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. * 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 ### Remote Building
@@ -66,3 +83,8 @@ Actual versions are automatically built via GitHub Action pipelines defined at [
### Additional Resources ### Additional Resources
* [Scryfall API](https://scryfall.com/docs/api) for fetching card and set data of Magic the Gathering, including card preview images. * [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. * [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)
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "card-collection-manager-2", "name": "card-collection-manager-2",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.4",
"scripts": { "scripts": {
"dev": "next dev -p 1420", "dev": "next dev -p 1420",
"build": "next build && next export -o dist", "build": "next build && next export -o dist",
+1 -1
View File
@@ -203,7 +203,7 @@ dependencies = [
[[package]] [[package]]
name = "card-collection-manager-2" name = "card-collection-manager-2"
version = "1.0.0" version = "1.0.3"
dependencies = [ dependencies = [
"base64", "base64",
"image", "image",
+2 -2
View File
@@ -1,8 +1,8 @@
[package] [package]
name = "card-collection-manager-2" name = "card-collection-manager-2"
version = "1.0.0" version = "1.0.4"
description = "A Tauri App" description = "A Tauri App"
authors = ["you"] authors = ["Sebastian Dine"]
license = "" license = ""
repository = "" repository = ""
edition = "2021" edition = "2021"
@@ -1 +0,0 @@
{"dataStorage":"/home/dev/cards","defaultGame":"Magic"}
@@ -14,7 +14,12 @@ pub fn format_text_for_fs(text: &String) -> String {
.replace(":", "-") .replace(":", "-")
.replace("&", "And") .replace("&", "And")
.replace("|", "Or") .replace("|", "Or")
.replace("á", "a")
.replace("é", "e") .replace("é", "e")
.replace("í", "i")
.replace("ó", "o")
.replace("ú", "u")
.replace("û", "u")
} }
/// Parse an index from the end of a filename. This function supports indices with /// Parse an index from the end of a filename. This function supports indices with
+1 -1
View File
@@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "card-collection-manager-2", "productName": "card-collection-manager-2",
"version": "1.0.0" "version": "1.0.4"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {
@@ -1,11 +1,9 @@
import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
import { invoke } from "@tauri-apps/api/tauri";
import { GrNext, GrPrevious } from "react-icons/gr"; import { GrNext, GrPrevious } from "react-icons/gr";
import { RotatingLines } from "react-loader-spinner";
import ModalTemplate from "../templates/ModalTemplate"; import ModalTemplate from "../templates/ModalTemplate";
import Image from "next/image"; import CardImageWithLoader from "../templates/CardImageWithLoader";
/** /**
* Modal to display a set of images. * Modal to display a set of images.
@@ -25,43 +23,24 @@ const ImageModal: React.FC<{
startIndex?: number; startIndex?: number;
}> = (props) => { }> = (props) => {
// current image as base-64 string
const [imageB64, setImageB64] = useState<string>("");
// index of current image in `props.images` // index of current image in `props.images`
const [imageIndex, setImageIndex] = useState<number>(0); const [imageIndex, setImageIndex] = useState<number>(0);
// flag to display the loading image
const [loaderVisible, setLoaderVisible] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
if (props.visible && props.images) { if (props.visible && props.images) {
setLoaderVisible(true);
setImageIndex(props.startIndex | 0); setImageIndex(props.startIndex | 0);
loadImage(props.startIndex | 0);
} }
}, [props.visible]); }, [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 = () => { const loadNextImage = () => {
if(imageIndex < props.images.length -1){ if(imageIndex < props.images.length -1){
setLoaderVisible(true);
setImageIndex(imageIndex + 1); setImageIndex(imageIndex + 1);
loadImage(imageIndex + 1);
} }
} }
const loadPreviousImage = () => { const loadPreviousImage = () => {
if(imageIndex > 0){ if(imageIndex > 0){
setLoaderVisible(true);
setImageIndex(imageIndex - 1); setImageIndex(imageIndex - 1);
loadImage(imageIndex - 1);
} }
} }
@@ -74,19 +53,7 @@ const ImageModal: React.FC<{
modalStyle=" w-[80%] h-[80%]" modalStyle=" w-[80%] h-[80%]"
> >
<div className="relative h-[90%] mx-8"> <div className="relative h-[90%] mx-8">
{loaderVisible <CardImageWithLoader images={props.images} index={imageIndex} game={props.game} />
?
<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" />
}
{/* next/previous image icons */} {/* next/previous image icons */}
{props.images.length > 1 ? ( {props.images.length > 1 ? (
@@ -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> </select>
<label className="text-sm col-span-1">Set No.</label> <label className="text-sm col-span-1">Set No.</label>
<input type="number" <input type="text"
className="text-sm col-span-2 border-2" className="text-sm col-span-2 border-2"
defaultValue={""} defaultValue={""}
ref={setNoRef} ref={setNoRef}
@@ -1,8 +1,37 @@
import React, { forwardRef } from "react"; 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. * 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>) => { 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
View File
@@ -1 +0,0 @@
1.0.0
+10 -7
View File
@@ -1,26 +1,29 @@
#!/bin/sh #!/bin/sh
PROJECT_ROOT=$(pwd) 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 cd $PROJECT_ROOT && cd card-collection-manager-2/src-tauri
CARGO_VERSION=$(cargo read-manifest | jq -r .version) CARGO_VERSION=$(cargo read-manifest | jq -r .version)
CARGO_NAME=$(cargo read-manifest | jq -r .name) 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 cd $PROJECT_ROOT && cd card-collection-manager-2
YARN_VERSION=$(cat package.json | jq -r .version) YARN_VERSION=$(cat package.json | jq -r .version)
YARN_NAME=$(cat package.json | jq -r .name) YARN_NAME=$(cat package.json | jq -r .name)
if [ $CARGO_VERSION != $YARN_VERSION ]; then if [ $CARGO_VERSION != $YARN_VERSION ] || [ $CARGO_VERSION != $TAURI_VERSION ] || [ $YARN_VERSION != $TAURI_VERSION ]; then
echo "[PRE-COMMIT] Project version in 'package.json' and 'Cargo.toml' does not match." echo "[PRE-COMMIT] Project version in manifests does not match."
exit 1 exit 1
fi fi
if [ $CARGO_NAME != $YARN_NAME ]; then if [ $CARGO_NAME != $YARN_NAME ] || [ $CARGO_NAME != $TAURI_NAME ] || [ $YARN_NAME != $TAURI_NAME ]; then
echo "[PRE-COMMIT] Project name in 'package.json' and 'Cargo.toml' does not match." echo "[PRE-COMMIT] Project name in manifests does not match."
exit 1 exit 1
fi fi
cd $PROJECT_ROOT cd $PROJECT_ROOT
echo $CARGO_VERSION > version echo $TAURI_VERSION > version
git add version git add version
exit 0 exit 0
Regular → Executable
+1 -1
View File
@@ -1 +1 @@
1.0.0 1.0.4