From 95a69df695d5704337aa94a1472488e10526d0df Mon Sep 17 00:00:00 2001 From: isark Date: Mon, 7 Aug 2023 22:42:57 +0200 Subject: [PATCH] Lots of plan management, loading and so on --- src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 23 ++++- src-tauri/src/plan.rs | 45 ++++++++- src-tauri/src/storage.rs | 94 +++++++++++++------ src/app/app.component.html | 6 +- src/app/app.component.ts | 22 ++++- src/app/carousel/carousel.component.ts | 29 +++--- src/app/models/plan.ts | 7 +- .../plan-display/plan-display.component.html | 5 +- .../plan-display/plan-display.component.ts | 20 ++-- src/app/services/overlay.service.ts | 4 +- src/app/services/plan.service.ts | 21 ++++- 12 files changed, 204 insertions(+), 73 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9fb5650..d8e94d2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -39,3 +39,4 @@ directories = "5.0.1" # this feature is used for production builds or when `devPath` points to the filesystem # DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] + diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c7c45b1..f97538b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,12 +1,13 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::sync::Mutex; +use std::{path::PathBuf, sync::Mutex}; use config::Config; use crossbeam::channel::Sender; use overlay::{Event, LockedOverlayData, Overlay}; +use plan::Plan; use poe_data::world_area::WorldAreasMap; use simple_logger::SimpleLogger; use storage::Storage; @@ -57,6 +58,24 @@ fn set_config(config: Config, state: tauri::State>) { } } +#[tauri::command] +fn load_plan(path: PathBuf, state: tauri::State>) -> Option { + if let Ok(mut storage) = state.lock() { + if let Some(path_string) = path.to_str() { + storage.last_plan = Some(path_string.to_string()); + let plan = Storage::load_plan(storage.last_plan.as_ref()?); + log::info!("got plan: {plan:?}"); + return plan; + } + } + None +} + +#[tauri::command] +fn load_stored_plans() -> Vec { + Storage::enumerate_plans() +} + fn main() { SimpleLogger::new() .with_module_level("underlayer", log::LevelFilter::Info) @@ -86,6 +105,8 @@ fn main() { load_world_areas, load_config, set_config, + load_plan, + load_stored_plans ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/plan.rs b/src-tauri/src/plan.rs index fe9cbfa..093f1e5 100644 --- a/src-tauri/src/plan.rs +++ b/src-tauri/src/plan.rs @@ -1,3 +1,9 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use poe_data::world_area::WorldArea; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] @@ -10,4 +16,41 @@ pub struct Plan { pub struct PlanElement { area_key: String, notes: String, -} \ No newline at end of file +} + +#[derive(Debug, Deserialize)] +struct OldPlanElement { + area: OldArea, + note: String, +} + +#[derive(Debug, Deserialize)] +struct OldPlan { + elements: Vec, + position: usize, +} + +#[derive(Debug, Deserialize)] +struct OldArea { + _rid: usize, +} + +pub fn convert_old(path: &str) -> Option { + let plan: OldPlan = serde_json::from_str(&std::fs::read_to_string(path).expect("Could not convert old")).expect("could not convert old"); + + let map = poe_data::world_area::load_world_areas_map(include_str!( + "../../data/processed_world_areas.json" + )); + let map = map + .iter() + .map(|(k, v)| (v.key_id, k)) + .collect::>(); + + Some(Plan { + current: 0, + plan: plan.elements.into_iter().map(|plan_element| PlanElement { + area_key: map[&plan_element.area._rid].to_owned(), + notes: plan_element.note, + }).collect::>(), + }) +} diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index 82f79e0..1f5db5a 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -1,17 +1,15 @@ -use std::error::Error; +use std::path::{Path, PathBuf}; use directories::ProjectDirs; use serde::{Deserialize, Serialize}; use crate::config::Config; -use crate::plan::Plan; +use crate::plan::{convert_old, Plan}; #[derive(Serialize, Deserialize, Debug)] pub struct Storage { pub config: Config, - #[serde(skip)] - pub plan: Option, - pub plan_name: Option, + pub last_plan: Option, } const QUALIFIER: &'static str = "me"; @@ -26,13 +24,12 @@ fn proj_dir() -> Option { impl Default for Storage { fn default() -> Self { - let config = Self::load(); - match config { - Some(config) => config, + let storage = Self::load(); + match storage { + Some(storage) => storage, None => Self { config: Default::default(), - plan: Default::default(), - plan_name: None, + last_plan: None, }, } } @@ -40,35 +37,16 @@ impl Default for Storage { impl Storage { fn load() -> Option { - let mut storage: Option = serde_json::from_str( + let storage: Option = serde_json::from_str( &std::fs::read_to_string(proj_dir()?.data_dir().with_file_name(CONFIG_FILE)).ok()?, ) .ok(); - if let Some(storage) = &mut storage { - if let Some(plan_name) = &storage.plan_name { - storage.plan = Self::load_plan(plan_name); - } - } - log::info!("Loaded storage: {:?}", storage); storage } - fn load_plan>(file_name: T) -> Option { - serde_json::from_str( - &std::fs::read_to_string( - proj_dir()? - .data_dir() - .join(SAVED_PLANS) - .with_file_name(file_name.into()), - ) - .ok()?, - ) - .ok() - } - pub fn save(&self) { if let Ok(content) = serde_json::to_string_pretty(&self) { if let Some(dir) = proj_dir() { @@ -83,4 +61,60 @@ impl Storage { } } } + + pub fn load_plan>(file: T) -> Option { + let mut file = file.into(); + let plan_file = Path::new(&file); + //basically copy to our own dir to store if for reuse without contaminating or depending on the original file + if let Some(path) = plan_file.parent() { + if !path.ends_with(SAVED_PLANS) { + if let Some(proj_dir) = proj_dir() { + if let Some(file_name) = plan_file.file_name() { + let copy_result = std::fs::copy( + &file, + proj_dir + .data_dir() + .join(SAVED_PLANS) + .with_file_name(file_name), + ); + match copy_result { + Ok(_) => { + file = proj_dir + .data_dir() + .join(SAVED_PLANS) + .with_file_name(file_name) + .to_str() + .unwrap() + .to_string() + } + Err(e) => log::error!("Could not store plan file {e:?}"), + } + } + } + } + } + + log::info!("Loading plan: {file:?}"); + if let Some(plan) = serde_json::from_str(&std::fs::read_to_string(&file).ok()?).ok() + { + plan + } else { + log::info!("Attempting to convert old"); + let plan = convert_old(&file)?; + std::fs::write(&file, serde_json::to_string(&plan).ok()?).ok(); + Some(plan) + } + } + + pub fn enumerate_plans() -> Vec { + if let Some(proj_dir) = proj_dir() { + if let Ok(read_dir) = proj_dir.data_dir().join(SAVED_PLANS).read_dir() { + return read_dir + .filter_map(|entry| Some(entry.ok()?.path().to_str()?.to_string())) + .collect::>(); + } + } + + return vec![]; + } } diff --git a/src/app/app.component.html b/src/app/app.component.html index d57b413..9b63dd7 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,8 +1,8 @@
- - - matched init + Click me for color picker! + +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5aa8618..2b8517a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -6,6 +6,9 @@ import { WorldAreaService } from "./services/world-area.service"; import { Color } from "./color-picker/color-picker.component"; import { OverlayService } from "./services/overlay.service"; import { ConfigService } from "./services/config.service"; +import { PlanService } from "./services/plan.service"; +import { open } from '@tauri-apps/api/dialog'; +import { from } from "rxjs"; @Component({ selector: "app-root", @@ -21,11 +24,26 @@ export class AppComponent implements OnInit { constructor( public overlayService: OverlayService, public worldAreas: WorldAreaService, - private shortcuts: ShortcutService, - private events: EventsService, + public planService: PlanService, public configService: ConfigService, ) { } + openDialog() { + from(open({ + multiple: false, + filters: [ + // { + // name: "Plan Files", + // extensions: ['PPF'] + // } + ] + })).subscribe(file => { + if (file) { + this.planService.loadPlan(file as string); + } + }); + } + ngOnInit(): void { diff --git a/src/app/carousel/carousel.component.ts b/src/app/carousel/carousel.component.ts index fd4b18d..014e7bc 100644 --- a/src/app/carousel/carousel.component.ts +++ b/src/app/carousel/carousel.component.ts @@ -14,27 +14,27 @@ import { Subject, debounceTime } from 'rxjs'; ], animations: [ trigger('direction', [ - state('vertical', style({ - 'grid-auto-flow': 'row', - })), - state('horizontal', style({ - 'grid-auto-flow': 'column', - })), + + state('vertical', style({})), + state('horizontal', style({})), + state('hidden', style({ opacity: 0, })), state('unhidden', style({ opacity: 1, })), + transition('* => hidden', animate(`{{directionTime}}ms`)), transition('hidden => unhidden', animate(`{{directionTime}}ms`)), - transition('unhidden => *', animate(0)), + transition('unhidden => *', animate('10ms')), ]) ] }) export class CarouselComponent implements OnInit, AfterViewInit { @Output() afterInitSelf: EventEmitter = new EventEmitter(); + @Output() changedIndex: EventEmitter = new EventEmitter(); @Input() slides?: any[]; @ContentChild(TemplateRef) template?: TemplateRef; @@ -124,6 +124,7 @@ export class CarouselComponent implements OnInit, AfterViewInit { this.increasing = true; if (this.slides && this.current + 1 < this.slides?.length) { this.current += 1; + this.changedIndex.emit(this.current); if (this.current + 1 < this.slides.length) { if (!this.visibleSlides?.find(e => e.index == this.current + 1)) { @@ -142,6 +143,7 @@ export class CarouselComponent implements OnInit, AfterViewInit { this.increasing = false; if (this.current - 1 >= 0) { this.current -= 1; + this.changedIndex.emit(this.current); if (this.current - 1 >= 0) { if (!this.visibleSlides?.find(e => e.index == this.current - 1)) { @@ -189,15 +191,7 @@ export class CarouselComponent implements OnInit, AfterViewInit { const len = this.slides?.length; return `repeat(${len}, minmax(calc(100% / 3), 1fr))`; } - // .window { - // grid-template-columns: repeat(3, 1fr); - // grid-auto-flow: column; - // } - - // .window-vertical { - // grid-template-rows: repeat(3, 1fr); - // grid-auto-flow: row; - // } + style() { let style: any = { @@ -205,8 +199,11 @@ export class CarouselComponent implements OnInit, AfterViewInit { if (this.vertical) { style['grid-template-rows'] = this.templateValue(); + style['grid-auto-flow'] = 'column'; + } else { style['grid-template-columns'] = this.templateValue(); + style['grid-auto-flow'] = 'row'; } if (!this.angularAnimating) { diff --git a/src/app/models/plan.ts b/src/app/models/plan.ts index 9239efe..ea73641 100644 --- a/src/app/models/plan.ts +++ b/src/app/models/plan.ts @@ -1,13 +1,10 @@ import { WorldArea } from "./world-area"; -export class Plan { - +export interface Plan { + elements: PlanElement[]; } interface PlanElement { area: WorldArea; note: string; - prev: boolean; - current: boolean; - next: boolean; } \ No newline at end of file diff --git a/src/app/plan-display/plan-display.component.html b/src/app/plan-display/plan-display.component.html index 0a29856..268558f 100644 --- a/src/app/plan-display/plan-display.component.html +++ b/src/app/plan-display/plan-display.component.html @@ -4,11 +4,14 @@ #targetRef> - + {{slide}} +
+ Notes:{{plan.elements[slideIndex].note}} +
diff --git a/src/app/plan-display/plan-display.component.ts b/src/app/plan-display/plan-display.component.ts index c02c56e..a06c2f2 100644 --- a/src/app/plan-display/plan-display.component.ts +++ b/src/app/plan-display/plan-display.component.ts @@ -6,6 +6,8 @@ import { Rect } from '../models/generated/Rect'; import { Color } from '../color-picker/color-picker.component'; import { ShortcutService } from '../services/shortcut.service'; import { CarouselComponent } from '../carousel/carousel.component'; +import { PlanService } from '../services/plan.service'; +import { Plan } from '../models/plan'; @Component({ selector: 'plan-display', @@ -13,24 +15,24 @@ import { CarouselComponent } from '../carousel/carousel.component'; styleUrls: ['./plan-display.component.scss'] }) export class PlanDisplayComponent implements AfterViewInit, OnInit { - @Input() plan: boolean = false; @Input() backgroundColor?: Color; draggable: boolean = true; rect?: Rect; - slides!: number[]; + // slides!: number[]; + plan?: Plan; slideIndex: number = 0; carouselComponent?: CarouselComponent; - constructor(private configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService) { - this.slides = []; - for(let i = 0; i < 100; i++) { - this.slides.push(i); - } + constructor(private configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, private planService: PlanService) { + // for(let i = 0; i < 100; i++) { + // this.slides.push(i); + // } } ngOnInit() { + this.plan = this.planService.currentPlan; } abs(v: number) { @@ -51,7 +53,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { ngAfterViewInit(): void { const cfgRect = this.configService.config.initialPlanWindowPosition; - console.log(window.innerWidth); this.rect = { x: cfgRect.x * window.innerWidth, y: cfgRect.y * window.innerHeight, @@ -75,8 +76,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { this.rect!.width = e.width; this.rect!.height = e.height; this.onDrag(e.drag); - - console.log(e.style); } onResizeEnd(e: OnResizeEnd) { @@ -103,7 +102,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { this.carouselComponent?.next(); }); } else { - console.log("no carousel", this.carouselComponent); } } } diff --git a/src/app/services/overlay.service.ts b/src/app/services/overlay.service.ts index 54f023b..797febb 100644 --- a/src/app/services/overlay.service.ts +++ b/src/app/services/overlay.service.ts @@ -4,6 +4,7 @@ import { EventsService } from './events.service'; import { Event } from "@tauri-apps/api/event"; import { invoke } from '@tauri-apps/api'; import { ConfigService } from './config.service'; +import { WorldAreaService } from './world-area.service'; class StateEvent { Visible?: any; @@ -20,7 +21,7 @@ export class OverlayService { visible: boolean = false; - constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService, private zone: NgZone) { + constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService) { this.shortcuts.register(this.configService.config.toggleOverlay, this.onToggleOverlay.bind(this)); this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); } @@ -28,7 +29,6 @@ export class OverlayService { onOverlayStateChange(event: Event) { this.interactable = event.payload.Interactable != null; if (event.payload.Hidden) {this.visible = false} else {this.visible = true}; - console.log("visible: ", this.visible); invoke("set_interactable", { interactable: true }).then(); } diff --git a/src/app/services/plan.service.ts b/src/app/services/plan.service.ts index 5d353e9..8f21ae3 100644 --- a/src/app/services/plan.service.ts +++ b/src/app/services/plan.service.ts @@ -1,9 +1,28 @@ import { Injectable } from '@angular/core'; +import { invoke } from '@tauri-apps/api'; +import { from } from 'rxjs'; +import { Plan } from '../models/plan'; @Injectable({ providedIn: 'root' }) export class PlanService { + currentPlan?: Plan; + planStore: string[] = []; - constructor() { } + constructor() { + this.getPreviousPlans(); + } + + loadPlan(path: string) { + console.log("loading path: ", path); + from(invoke('load_plan', {path})).subscribe(plan => { + console.log("got plan: ", plan); + this.currentPlan = plan; + }); + } + + getPreviousPlans() { + from(invoke('load_stored_plans')).subscribe(plans => this.planStore = plans); + } }