Skip to content

Commit

Permalink
finish create default pet config, read app configs from user config dir.
Browse files Browse the repository at this point in the history
  • Loading branch information
SeakMengs committed Aug 15, 2023
1 parent 954e590 commit e69c375
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 109 deletions.
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.4", features = [] }

[dependencies]
tauri = { version = "1.4", features = [ "fs-all", "path-all", "macos-private-api", "system-tray", "window-start-dragging", "shell-open"] }
tauri = { version = "1.4", features = [ "fs-all", "path-all", "macos-private-api", "system-tray", "shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
Expand Down
File renamed without changes.
17 changes: 14 additions & 3 deletions src-tauri/src/app/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@ pub fn convert_path(path_str: &str) -> String {
}
}

pub fn if_app_setting_does_not_exist_create_default(app: &mut App) {
let binding = path::config_dir().unwrap().join("WindowPet\\settings.json");
pub fn if_app_config_does_not_exist_create_default(app: &mut App, config_name: &str) {
let binding = path::config_dir()
.unwrap()
.join("WindowPet\\".to_owned() + config_name);
let setting_config_path = convert_path(binding.to_str().unwrap());
let is_config_already_exist = Path::new(&setting_config_path).exists();

if !is_config_already_exist {
let data = include_str!("default/settings.json");
// might rewrite how we read content inside file if this function is frequently used
let data;
if config_name == "settings.json" {
data = include_str!("default/settings.json");
} else if config_name == "pets.json" {
data = include_str!("default/pets.json");
} else {
return;
}

let json_data: serde_json::Value = serde_json::from_str(&data).unwrap();

let mut store = StoreBuilder::new(app.handle(), PathBuf::from(setting_config_path)).build();
Expand Down
5 changes: 3 additions & 2 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
mod app;
use app::cmd::{change_current_app_position, change_current_app_size};
use app::tray::{handle_tray_event, init_system_tray};
use app::utils::{get_os, if_app_setting_does_not_exist_create_default};
use app::utils::{get_os, if_app_config_does_not_exist_create_default};
use tauri_plugin_autostart::MacosLauncher;

fn main() {
Expand All @@ -21,7 +21,8 @@ fn main() {
.set_ignore_cursor_events(true)
.unwrap_or_else(|err| println!("{:?}", err));

if_app_setting_does_not_exist_create_default(app);
if_app_config_does_not_exist_create_default(app, "settings.json");
if_app_config_does_not_exist_create_default(app, "pets.json");

Ok(())
})
Expand Down
3 changes: 0 additions & 3 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
"all": false,
"open": true
},
"window": {
"startDragging": true
},
"fs": {
"all": true,
"scope": [
Expand Down
22 changes: 8 additions & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { useEffect } from "react";
import SettingWindow from "./SettingWindow";
import Canvas from "./Canvas";
import { getAppSettings } from "./utils/settingsHelper";
import { useSettingStore } from "./hooks/useSettingStore";
import { isEnabled } from "tauri-plugin-autostart-api";
import useInit from "./hooks/useInit";

function App() {

const { setLanguage, setTheme, setIsAutoStartUp } = useSettingStore();

// initialize settings
useEffect(() => {
getAppSettings({}).then((settings) => {
setLanguage(settings.language);
setTheme(settings.theme);
});

isEnabled().then((enabled) => {
setIsAutoStartUp(enabled);
})
}, []);
const { setLanguage, setTheme, setIsAutoStartUp, setPetConfig } = useSettingStore();
useInit(async () => {
const settings = await getAppSettings({ path: "settings.json" });
setLanguage(settings.language);
setTheme(settings.theme);
setIsAutoStartUp(await isEnabled());
});

return (
<Router>
Expand Down
102 changes: 52 additions & 50 deletions src/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,80 @@
import { useEffect, useRef } from "react";
import Pet from "./Class/Pet";
import petConfig from "./default/pets.json"
import { usePetStore } from "./hooks/usePetStore";
import { getAppSettings } from "./utils/settingsHelper";

function Canvas() {
// credit: https://stackoverflow.com/questions/16277383/javascript-screen-height-and-screen-width-returns-incorrect-values
const DPR: number = window.devicePixelRatio;
const currentScreenHeight: number = Math.round(DPR * window.screen.height);
const canvasRef = useRef<HTMLCanvasElement>(null);
const FPS = 60;

const { pets, addPet, clearPets, isPetsInitialized, setIsPetsInitialized } = usePetStore((state) => ({
pets: state.pets,
addPet: state.addPet,
clearPets: state.clearPets,
isPetsInitialized: state.isPetsInitialized,
setIsPetsInitialized: state.setIsPetsInitialized
}));
// disable right click (context menu) for build version only. uncomment for development
// credit: https://github.com/tauri-apps/wry/issues/30
document.addEventListener('contextmenu', event => event.preventDefault());

//* credit: https://medium.com/@pdx.lucasm/canvas-with-react-js-32e133c05258
useEffect(() => {
// deep clone pet config to prevent reference error
const deepClonePetConfig = JSON.parse(JSON.stringify(petConfig))
let pets: any = [];
// register cat object
if (deepClonePetConfig.length > 0) {
for (let i = 0; i < deepClonePetConfig.length; i++) {
pets[i] = new Pet(deepClonePetConfig[i]);
}
}

// const handleDrag = document.getElementById('allowCanvasDrag');
// handleDrag.addEventListener('mousedown', function (event) {
// event.preventDefault()
// appWindow.startDragging();
// });
getAppSettings({ path: "pets.json" }).then((petConfig) => {
const petConf = JSON.parse(JSON.stringify(petConfig));

const canvas = canvasRef.current;
const context = canvas?.getContext("2d");

canvas!.width = window.innerWidth;;
canvas!.height = currentScreenHeight;
// prevent pets initialize more than once when pets.json is updated
if (petConf.length < pets.length) return () => {
clearPets();
setIsPetsInitialized(false);
}

function animate() {
context!.save();
context!.imageSmoothingEnabled = false;

// credit: https://stackoverflow.com/questions/4815166/how-do-i-make-a-transparent-canvas-in-html5
context!.clearRect(0, 0, canvas!.width, canvas!.height);

// Debug purposes
// context.fillStyle = 'black'
// context.fillRect(0, 0, canvas.width, canvas.height)

if (pets.length > 0) {
for (let i = 0; i < pets.length; i++) {
pets[i].animateBehavior();
pets[i].update(context);
if (petConf.length > 0 && !isPetsInitialized) {
for (let i = 0; i < petConf.length; i++) {
addPet(new Pet(petConf[i]));
}
setIsPetsInitialized(true);
// console.log("pets initialized");
}

context!.restore();

setTimeout(() => {
window.requestAnimationFrame(animate)
}, 1000 / FPS);
}
if (pets.length === 0 && isPetsInitialized) return;

animate();
// console.log(pets);
const canvas = canvasRef.current;
const context = canvas?.getContext("2d");
canvas!.width = window.innerWidth;;
canvas!.height = currentScreenHeight;

return () => {
// cleanup
pets = [];
}
function animate() {
context!.save();
context!.imageSmoothingEnabled = false;
// credit: https://stackoverflow.com/questions/4815166/how-do-i-make-a-transparent-canvas-in-html5
context!.clearRect(0, 0, canvas!.width, canvas!.height);

}, [petConfig])
// Debug purposes
// context.fillStyle = 'black'
// context.fillRect(0, 0, canvas.width, canvas.height)

if (pets.length > 0) {
for (let i = 0; i < pets.length; i++) {
pets[i].animateBehavior();
pets[i].update(context as CanvasRenderingContext2D);
}
}

context!.restore();
setTimeout(() => {
window.requestAnimationFrame(animate)
}, 1000 / FPS);
}
animate();
});
}, [pets]);

return (
<div id="allowCanvasDrag" data-tauri-drag-region>
<div>
<canvas ref={canvasRef} />
</div>
)
Expand Down
34 changes: 1 addition & 33 deletions src/Class/Pet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,7 @@
* A good resource to learn about canvas, the project is based on this tutorial.
* credit: https://www.youtube.com/watch?v=vyqbNFMDRGQ&t=8593s&ab_channel=ChrisCourses
*/
type PetState = {
imageSrc: string;
framesMax: number;
stateHold: number;
framesHold: number;
image?: HTMLImageElement;
};

type States = {
[key: string]: PetState | undefined;
}

type CurrentPetState = {
state: string;
index?: number;
stateHold: number;
};

interface PetParams {
position: { x: number; y: number };
name: string;
currentState: CurrentPetState;
velocity: { x: number; y: number };
offset: { x: number; y: number };
scale?: number;
framesMax?: number;
framesCurrent?: number;
framesElapsed?: number;
framesHold?: number;
states: States;
walkSpeed?: number;
runSpeed?: number;
}
import { States, CurrentPetState, PetParams } from "./type";

export default class Pet {
position: { x: number; y: number };
Expand Down
33 changes: 33 additions & 0 deletions src/Class/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export type PetState = {
imageSrc: string;
framesMax: number;
stateHold: number;
framesHold: number;
image?: HTMLImageElement;
};

export type States = {
[key: string]: PetState | undefined;
}

export type CurrentPetState = {
state: string;
index?: number;
stateHold: number;
};

export interface PetParams {
position: { x: number; y: number };
name: string;
currentState: CurrentPetState;
velocity: { x: number; y: number };
offset: { x: number; y: number };
scale?: number;
framesMax?: number;
framesCurrent?: number;
framesElapsed?: number;
framesHold?: number;
states: States;
walkSpeed?: number;
runSpeed?: number;
}
6 changes: 4 additions & 2 deletions src/SettingWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {
Box,
rem,
Text,
ActionIcon
ActionIcon,
MantineProvider,
ColorSchemeProvider,
ColorScheme
} from '@mantine/core';
import { IconSun, IconMoonStars } from '@tabler/icons-react';
import Logo from './components/shell/Logo';
import { MantineProvider, ColorSchemeProvider, ColorScheme } from "@mantine/core";
import { SettingTabs } from './components/shell/SettingTabs';
import AddPet from './components/setting_tabs/AddPet';
import EditPet from './components/setting_tabs/EditPet';
Expand Down
13 changes: 13 additions & 0 deletions src/components/setting_tabs/EditPet.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { Text } from "@mantine/core";
import { usePetStore } from "../../hooks/usePetStore";
import { useEffect } from "react";

function EditPet() {
const { pets, isPetsInitialized } = usePetStore((state) => ({
pets: state.pets,
isPetsInitialized: state.isPetsInitialized,
}));

useEffect(() => {
console.log(pets);
console.log(isPetsInitialized);
}, [pets, isPetsInitialized]);

return (
<>
<button onClick={() => console.log(pets, isPetsInitialized)}>b</button>
<Text>Edit Pet</Text>
</>
)
Expand Down
13 changes: 13 additions & 0 deletions src/hooks/useInit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// credit: https://github.com/lencx/ChatGPT/blob/main/src/hooks/useInit.ts
import { useRef, useEffect } from 'react';

// fix: Two interface requests will be made in development mode
export default function useInit(callback: () => void) {
const isInit = useRef(true);
useEffect(() => {
if (isInit.current) {
callback();
isInit.current = false;
}
});
}
Loading

0 comments on commit e69c375

Please sign in to comment.