Lots of tweaks, bugfixes and improvements! Also supports storing and loading current position in a plan you've imported before. Can scroll on the plan window to quickly move forward/backward

merge-notes
isark 2 years ago
parent 534dde670e
commit 5bcaa47008

@ -2202,6 +2202,7 @@ dependencies = [
"Underlayer",
"crossbeam",
"directories",
"lazy_static",
"log",
"notify",
"poe_data",

@ -37,6 +37,7 @@ poe_data = { path = "poe_data" }
directories = "5.0.1"
notify = "6.0.1"
regex = "1.9.3"
lazy_static = "1.4.0"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem

@ -195,7 +195,6 @@ impl Matcher {
}
}
//TODO: Multimap
#[allow(dead_code)]
pub fn repack(content: &str, out_path: &str) {
let areas_json = serde_json::from_str::<UnprocessedAreas>(content);

@ -10,11 +10,11 @@ fn main() {
poe_data::world_area::repack(&include_str!("../../data/WorldAreas.json"), OUT_PROCESSED);
//Let's go with uppercase names, allows automatic import to work...
config::Rect::export_to("../src/app/models/generated/Rect.ts").expect("Could not generate config struct");
config::Config::export_to("../src/app/models/generated/Config.ts").expect("Could not generate config struct");
config::Rect::export_to("../src/app/_models/generated/Rect.ts").expect("Could not generate config struct");
config::Config::export_to("../src/app/_models/generated/Config.ts").expect("Could not generate config struct");
println!("cargo:rerun-if-changed=src/config.rs");
println!("cargo:rerun-if-changed=../src/app/models/generated/Rect.ts");
println!("cargo:rerun-if-changed=../src/app/models/generated/Config.ts");
println!("cargo:rerun-if-changed=../src/app/_models/generated/Rect.ts");
println!("cargo:rerun-if-changed=../src/app/_models/generated/Config.ts");
println!("cargo:rustc-cfg=build_only");
}

@ -7,16 +7,14 @@ use std::{path::PathBuf, sync::Mutex};
use config::Config;
use crossbeam::channel::Sender;
use notify::PollWatcher;
use overlay::{Event, LockedOverlayData, Overlay};
use overlay::{Event, Overlay};
use plan::Plan;
use poe_data::world_area::Matcher;
use poe_data::world_area::WorldAreasMap;
use poe_reader::filter_func;
use poe_reader::{blocking_area_filtered_rx, poe_client_log_receiver};
use simple_logger::SimpleLogger;
use storage::Storage;
use tauri::AppHandle;
use tauri::CustomMenuItem;
use tauri::Manager;
@ -26,6 +24,8 @@ use tauri::SystemTrayMenu;
use tauri::SystemTrayMenuItem;
use tauri::Window;
use lazy_static::lazy_static;
mod config;
mod overlay;
mod plan;
@ -41,12 +41,16 @@ fn set_interactable(interactable: bool, state: tauri::State<Sender<Event>>) {
}
}
lazy_static! {
static ref WORLD_AREAS_MAP: WorldAreasMap = poe_data::world_area::load_world_areas_map(include_str!(
"../../data/processed_world_areas.json"
));
}
#[tauri::command]
fn load_world_areas() -> WorldAreasMap {
log::info!("Loading world areas");
poe_data::world_area::load_world_areas_map(include_str!(
"../../data/processed_world_areas.json"
))
WORLD_AREAS_MAP.clone()
}
#[tauri::command]
@ -58,8 +62,6 @@ fn load_config(state: tauri::State<Mutex<Storage>>) -> Option<Config> {
fn set_config(
config: Config,
state: tauri::State<Mutex<Storage>>,
window: tauri::Window,
app: AppHandle,
) {
log::info!("Saved config: {:?}", config);
if let Ok(mut storage) = state.lock() {
@ -96,15 +98,21 @@ fn load_stored_plans() -> Vec<String> {
}
#[tauri::command]
fn load_previous(name: String, state: tauri::State<Mutex<Storage>>) -> Option<Plan> {
if let Ok(mut storage) = state.lock() {
fn load_previous(name: String) -> Option<Plan> {
let plan = Storage::load_by_name(name);
log::info!("got plan: {plan:?}");
return plan;
}
None
}
#[tauri::command]
fn path_for_previous(prev: String) -> Option<String> {
let path = Storage::path_for_name(prev);
log::info!("got path: {path:?}");
return path;
}
#[tauri::command]
fn base_plan() -> Plan {
const BASE_PLAN_STRING: &str = include_str!("../../data/base_plan.json");
@ -161,7 +169,8 @@ fn main() {
load_stored_plans,
save_plan,
base_plan,
load_previous
load_previous,
path_for_previous,
])
.system_tray(system_tray)
.on_system_tray_event(|app, event| match event {

@ -6,6 +6,12 @@ use serde::{Deserialize, Serialize};
pub struct Plan {
plan: Vec<PlanElement>,
current: usize,
stored_path: Option<String>,
}
impl Plan {
pub fn set_stored_path(&mut self, file: String) {
self.stored_path = Some(file);
}
}
#[derive(Debug, Deserialize, Serialize)]
@ -54,5 +60,6 @@ pub fn convert_old(path: &str) -> Option<Plan> {
notes: plan_element.note,
})
.collect::<Vec<PlanElement>>(),
stored_path: None
})
}

@ -100,7 +100,7 @@ impl Storage {
}
log::info!("Loading plan: {file:?}");
if let Some(plan) = serde_json::from_str(&std::fs::read_to_string(&file).ok()?).ok() {
let mut plan = if let Some(plan) = serde_json::from_str(&std::fs::read_to_string(&file).ok()?).ok() {
plan
} else {
log::info!("Attempting to convert old");
@ -114,7 +114,13 @@ impl Storage {
.map_err(|e| "Could not write converted plan to storage")
.ok();
Some(plan)
};
if let Some(plan) = &mut plan {
plan.set_stored_path(file);
}
plan
}
pub fn load_by_name<T: Into<String>>(file: T) -> Option<Plan> {
@ -137,5 +143,11 @@ impl Storage {
return vec![];
}
pub fn path_for_name<T: Into<String>>(file: T) -> Option<String> {
let file = file.into();
let file = proj_dir()?.data_dir().join(SAVED_PLANS).join(file);
Some(file.to_str()?.to_string())
}
}

@ -0,0 +1,71 @@
import { invoke } from "@tauri-apps/api/tauri";
import { Subject, debounceTime, from } from "rxjs";
export interface PlanInterface {
plan: PlanElement[];
current: number;
stored_path?: string;
}
export class Plan {
plan: PlanElement[];
current: number;
private path?: string;
private saveSubject: Subject<void> = new Subject<void>();
constructor(plan: PlanInterface) {
this.plan = plan.plan;
this.current = plan.current;
if(plan.stored_path) {
this.path = plan.stored_path;
}
this.saveSubject.pipe(debounceTime(500)).subscribe(() => this.underlyingSave());
}
setPath(path: string) {
this.path = path;
}
next() {
if (this.current + 1 < this.plan!.length) {
this.current++;
this.save();
}
}
prev() {
if (this.current - 1 >= 0) {
this.current--;
this.save();
}
}
setPrevious(prev: string) {
from(invoke<string>('path_for_previous', { prev })).subscribe(path => {
if (path) {
this.setPath(path);
}
});
}
private save() {
if (this.path) {
this.saveSubject.next();
}
}
private underlyingSave() {
console.log("Underlying save");
invoke('save_plan', {
plan: {
plan: this.plan,
current: this.current
}, path: this.path
});
}
}
export interface PlanElement {
area_key: string;
notes?: string;
}

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Config } from '../models/generated/Config';
import { Config } from '../_models/generated/Config';
import { app, invoke } from '@tauri-apps/api';
import { Subject, debounceTime, from } from 'rxjs';
import { EventsService } from './events.service';
@ -9,19 +9,13 @@ import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
providedIn: 'root'
})
export class ConfigService {
//We're making sure this is loaded initially so let's not indicate it being nullable.
private cfg!: Config;
private proxied!: Config;
private syncSubject: Subject<void> = new Subject<void>();
constructor(private events: EventsService) {
// Let's do some premature optimization! Now we can do lots of config changes quickly from FE without as many sync calls :D
constructor(events: EventsService) {
this.syncSubject.pipe(debounceTime(100)).subscribe(() => this.underlyingSync());
events.listen<Config>("config").subscribe(cfg => {
Object.assign(this.cfg, cfg.payload);
// this.cfg = cfg.payload;
}
);
events.listen<Config>("config").subscribe(cfg => Object.assign(this.cfg, cfg.payload));
}
get config() {
@ -34,6 +28,7 @@ export class ConfigService {
console.error("Could not load config, should generally not happen :')");
return;
}
const _this = this;
this.cfg = cfg;
// Mostly to wrap setters so we can run the (debounced) sync function on any config changes!
@ -54,7 +49,7 @@ export class ConfigService {
sync() {
let target = "Normal"
if(appWindow.label == "Normal") {
if (appWindow.label == "Normal") {
target = "Overlay";
}

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { invoke } from '@tauri-apps/api';
import { from, map } from 'rxjs';
import { Plan } from '../models/plan';
import { from, map, tap } from 'rxjs';
import { Plan, PlanInterface } from '../_models/plan';
@Injectable({
providedIn: 'root'
@ -16,27 +16,33 @@ export class PlanService {
}
loadPlan(path: string) {
return from(invoke<Plan>('load_plan', { path })).pipe(
return from(invoke<PlanInterface>('load_plan', { path })).pipe(
map(plan => {
this.currentPlan = plan;
this.currentPlan = new Plan(plan);
return plan
})
);
}
loadPlanNoSave(path: string) {
return from(invoke<Plan>('load_plan', { path })).pipe(
return from(invoke<PlanInterface>('load_plan', { path })).pipe(
map(plan => {
this.currentPlan = new Plan(plan);
this.currentPlan.setPath(path);
return plan
})
);
}
loadBasePlan() {
return from(invoke<Plan>('base_plan'));
return from(invoke<PlanInterface>('base_plan'));
}
savePlan(path: string, plan: Plan) {
plan.plan.forEach(elem => {
if (!elem.notes) { elem.notes = "" }
})
});
return from(invoke<boolean>('save_plan', { path, plan })).subscribe(status => {
});
}
@ -46,6 +52,10 @@ export class PlanService {
}
loadPrevious(name: string) {
from(invoke<Plan>('load_previous', {name})).subscribe(plan => this.currentPlan = plan);
return from(invoke<PlanInterface>('load_previous', { name })).pipe(tap(plan => {
console.log("previous loaded: ", plan);
this.currentPlan = new Plan(plan);
this.currentPlan.setPrevious(name);
}));
}
}

@ -1,5 +1,5 @@
import { Injectable, NgZone } from '@angular/core';
import { WorldArea } from '../models/world-area';
import { WorldArea } from '../_models/world-area';
import { invoke } from '@tauri-apps/api';
import { Observable, ReplaySubject, Subject, filter, from, map } from 'rxjs';
import naturalCompare from 'natural-compare';

@ -1,12 +1,12 @@
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { invoke } from "@tauri-apps/api/tauri";
import { ShortcutService } from "./services/shortcut.service";
import { EventsService } from "./services/events.service";
import { WorldAreaService } from "./services/world-area.service";
import { ShortcutService } from "./_services/shortcut.service";
import { EventsService } from "./_services/events.service";
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 { OverlayService } from "./_services/overlay.service";
import { ConfigService } from "./_services/config.service";
import { PlanService } from "./_services/plan.service";
import { from } from "rxjs";
import { appWindow } from "@tauri-apps/api/window"
import { FormControl } from "@angular/forms";

@ -6,7 +6,7 @@ import { AppComponent } from "./app.component";
import { FormsModule } from "@angular/forms";
import { RecordKeyChord } from "./directives/record-key-chord.directive";
import { PlanDisplayModule } from "./plan-display/plan-display.module";
import { ConfigService } from "./services/config.service";
import { ConfigService } from "./_services/config.service";
import { MatButtonModule } from "@angular/material/button";
import { EditorComponent } from "./editor/editor.component";
import { AngularResizeEventModule } from "angular-resize-event";

@ -32,7 +32,6 @@ import { Subject, debounceTime } from 'rxjs';
]
})
export class CarouselComponent<T> implements OnInit, AfterViewInit {
@Output() afterInitSelf: EventEmitter<CarouselComponent<T>> = new EventEmitter<CarouselComponent<T>>();
@Output() changedIndex: EventEmitter<number> = new EventEmitter<number>();
@Input() slides?: T[];
@ -40,6 +39,8 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
@ViewChild('carouselWindow') window!: ElementRef;
@ViewChildren('slideElement') slideElements!: QueryList<ElementRef>;
@Input() initIndex?: number;
current: number = 0;
intersectionObserver?: IntersectionObserver;
visibleSlides?: IntersectingSlide[];
@ -56,6 +57,9 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
constructor(private cdr: ChangeDetectorRef) {
this.visibleSlides = [];
this.debouncedOnchange.pipe(debounceTime(500)).subscribe(() => this.realOnChange());
if(this.initIndex) {
this.current = this.initIndex;
}
}
ngOnInit(): void {
@ -124,6 +128,21 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
this.intersectionObserver?.observe(ref.nativeElement);
}
setIndex(slideIndex: number) {
this.current = slideIndex;
for(let i = -this.numExtraPrev(); i <= this.numExtraNext(); i++) {
console.log("adding slide:", this.current + i);
this.visibleSlides?.push({
index: this.current + i,
hasBeenVisible: false,
currentlyIntersecting: false,
});
}
this.onChange();
}
next(): void {
this.increasing = true;
if (this.slides && this.current + 1 < this.slides?.length) {

@ -9,14 +9,14 @@ import {
} from '@angular/cdk/drag-drop';
import { CommonModule } from '@angular/common';
import { WorldArea } from '../models/world-area';
import { Plan, PlanElement } from '../models/plan';
import { WorldAreaService } from '../services/world-area.service';
import { WorldArea } from '../_models/world-area';
import { Plan, PlanElement } from '../_models/plan';
import { WorldAreaService } from '../_services/world-area.service';
import { FormsModule } from '@angular/forms';
import { Fuzzr } from '../fuzzr/fuzzr';
import { from } from 'rxjs';
import { save } from '@tauri-apps/api/dialog';
import { PlanService } from '../services/plan.service';
import { PlanService } from '../_services/plan.service';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { EditNotesComponentDialog } from './notes/notes.component';
import { open } from '@tauri-apps/api/dialog';
@ -59,10 +59,10 @@ export class EditorComponent implements OnInit {
constructor(public worldAreaService: WorldAreaService, private cdr: ChangeDetectorRef, private planService: PlanService, public dialog: MatDialog) {
this.plan = {
this.plan = new Plan({
plan: [],
current: 0,
}
current: 0
});
this.disabledPlanDD = false;
this.autoScrollToEnd = false;

@ -5,7 +5,7 @@ import { MatButton, MatButtonModule } from '@angular/material/button';
import { FormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MarkdownService } from 'src/app/services/markdown.service';
import { MarkdownService } from 'src/app/_services/markdown.service';
interface DialogData {

@ -1,11 +0,0 @@
import { WorldArea } from "./world-area";
export interface Plan {
plan: PlanElement[];
current: number;
}
export interface PlanElement {
area_key: string;
notes?: string;
}

@ -3,17 +3,17 @@
<ng-container *ngIf="rect && planService.currentPlan">
<div class="target waypoint" [style.background-color]="backgroundColor ? backgroundColor : 'rgba(0, 0, 0, 0.1)'"
[style.transform]="transform()" [style.width]="rect.width + 'px'" [style.height]="rect.height + 'px'"
[class]="hasWaypoint() ? 'active' : ''"
[class]="hasWaypoint() ? 'active' : ''" (wheel)="onScroll($event)"
#targetRef>
<ng-container *ngIf="planService.currentPlan">
<carousel [numVisible]="3" [slides]="planService.currentPlan.plan"
<span hidden >{{planService.currentPlan.current}}</span>
<carousel [initIndex]="planService.currentPlan.current" [numVisible]="3" [slides]="planService.currentPlan.plan"
(afterInitSelf)="registerZoneSlides($event)">
<ng-template let-slide let-index="index">
<div class="zone-slide" [style.color]="configService.config.noteDefaultFg" [style.border]="index == planService.currentPlan.current ? '1px white solid' : 'none'">{{worldAreaMap!.get(slide.area_key)!.name}}<div class="waypoint-text" *ngIf="hasWaypoint(slide.area_key)">(W)</div></div>
</ng-template>
</carousel>
<carousel [slides]="planService.currentPlan.plan" (afterInitSelf)="registerCurrentSlides($event)">
<carousel [initIndex]="planService.currentPlan.current" [slides]="planService.currentPlan.plan" (afterInitSelf)="registerCurrentSlides($event)">
<ng-template let-slide>
<notes [note]="slide.notes" [style.color]="configService.config.noteDefaultFg" #noteSlide (resized)="onResizeNote(noteSlide)"></notes>
</ng-template>
@ -39,7 +39,7 @@
</div>
<div class="enumerated">
<mat-list role="list">
<mat-list-item role="listitem" *ngFor="let plan of planService.planStore"><button (click)="planService.loadPrevious(plan)">{{plan}}</button></mat-list-item>
<mat-list-item role="listitem" *ngFor="let plan of planService.planStore"><button (click)="setPrevious(plan)">{{plan}}</button></mat-list-item>
</mat-list>
</div>

@ -1,23 +1,23 @@
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, NgZone, OnInit, ViewChild } from '@angular/core';
import { NgxMoveableComponent, OnDragEnd, OnResize, OnResizeEnd } from 'ngx-moveable';
import { OnDrag } from 'ngx-moveable';
import { ConfigService } from '../services/config.service';
import { Rect } from '../models/generated/Rect';
import { ConfigService } from '../_services/config.service';
import { Rect } from '../_models/generated/Rect';
import { Color } from '../color-picker/color-picker.component';
import { ShortcutService } from '../services/shortcut.service';
import { ShortcutService } from '../_services/shortcut.service';
import { CarouselComponent } from '../carousel/carousel.component';
import { PlanService } from '../services/plan.service';
import { Plan, PlanElement } from '../models/plan';
import { WorldAreaService } from '../services/world-area.service';
import { WorldArea } from '../models/world-area';
import { PlanService } from '../_services/plan.service';
import { Plan, PlanElement } from '../_models/plan';
import { WorldAreaService } from '../_services/world-area.service';
import { WorldArea } from '../_models/world-area';
import { NotesComponent } from '../editor/notes/notes.component';
import { ResizedEvent } from 'angular-resize-event';
import { from } from 'rxjs';
import { open } from '@tauri-apps/api/dialog';
import { OverlayRef } from '@angular/cdk/overlay';
import { OverlayService, StateEvent } from '../services/overlay.service';
import { OverlayService, StateEvent } from '../_services/overlay.service';
import { appWindow } from '@tauri-apps/api/window';
import { EventsService } from '../services/events.service';
import { EventsService } from '../_services/events.service';
@Component({
selector: 'plan-display',
@ -25,6 +25,7 @@ import { EventsService } from '../services/events.service';
styleUrls: ['./plan-display.component.scss']
})
export class PlanDisplayComponent implements AfterViewInit, OnInit {
@Input() backgroundColor?: String;
draggable: boolean = true;
rect?: Rect;
@ -102,7 +103,7 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
}
hasWaypoint(key?: string): boolean {
if(!key) {
if (!key) {
key = this.planService.currentPlan!.plan[this.planService.currentPlan!.current].area_key;
}
const world_area = this.worldAreaMap?.get(key);
@ -158,9 +159,11 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
registerZoneSlides(carousel: CarouselComponent<PlanElement>) {
this.zoneSlides = carousel;
this.zoneSlides.setIndex(this.slideIndex);
}
registerCurrentSlides(carousel: CarouselComponent<PlanElement>) {
this.currentSlides = carousel;
this.currentSlides.setIndex(this.slideIndex);
if (this.currentSlides) {
this.shortcut.register(this.configService.config.prev, this.prev.bind(this));
@ -169,21 +172,36 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
}
next() {
if (this.planService.currentPlan!.current + 1 < this.planService.currentPlan!.plan.length) {
this.planService.currentPlan!.current++;
}
console.log("next");
this.planService.currentPlan!.next();
this.currentSlides?.next();
this.zoneSlides?.next();
}
prev() {
if (this.planService.currentPlan!.current - 1 >= 0) {
this.planService.currentPlan!.current--;
}
console.log("prev");
this.planService.currentPlan!.prev();
this.currentSlides?.prev();
this.zoneSlides?.prev();
}
setIndex(index: number) {
this.slideIndex = index;
if (this.currentSlides) {
this.currentSlides.current = index;
}
if (this.zoneSlides) {
this.zoneSlides.current = index;
}
}
setPrevious(plan: string) {
this.planService.loadPrevious(plan).subscribe(plan => {
this.setIndex(plan.current);
});
}
onResizeNote(noteSlide: NotesComponent) {
if (!noteSlide.ref) { return; }
@ -236,11 +254,20 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
loadBasePlan() {
this.planService.loadBasePlan().subscribe(plan => {
this.planService.currentPlan = plan;
this.planService.currentPlan = new Plan(plan);
})
}
enumeratedPlans() {
}
onScroll(event: WheelEvent) {
console.log("event", event );
if(event.deltaY < 0) {
this.prev();
} else {
this.next();
}
}
}

@ -1,18 +1,18 @@
import { Component, Inject, Input, NgZone } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ConfigService } from '../services/config.service';
import { ConfigService } from '../_services/config.service';
import { Color, ColorPickerComponent } from '../color-picker/color-picker.component';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import { OverlayService } from '../services/overlay.service';
import { PlanService } from '../services/plan.service';
import { OverlayService } from '../_services/overlay.service';
import { PlanService } from '../_services/plan.service';
import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { ShortcutService } from '../services/shortcut.service';
import { ShortcutService } from '../_services/shortcut.service';
import { RecordKeyChord } from '../directives/record-key-chord.directive';
import { Config } from '../models/generated/Config';
import { Config } from '../_models/generated/Config';
@Component({
selector: 'settings',

Loading…
Cancel
Save