diff --git a/mediarepo-ui/src-tauri/Cargo.lock b/mediarepo-ui/src-tauri/Cargo.lock index f7679e2..e891714 100644 --- a/mediarepo-ui/src-tauri/Cargo.lock +++ b/mediarepo-ui/src-tauri/Cargo.lock @@ -1491,8 +1491,8 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" -version = "0.14.0" -source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=5991f5d3038386c921a901a05c4a72e4ce00b418#5991f5d3038386c921a901a05c4a72e4ce00b418" +version = "0.16.0" +source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=9599020726eb9db47b9be7a6c41c2942a477ba09#9599020726eb9db47b9be7a6c41c2942a477ba09" dependencies = [ "async-trait", "bromine", diff --git a/mediarepo-ui/src-tauri/Cargo.toml b/mediarepo-ui/src-tauri/Cargo.toml index 6deb78b..add2843 100644 --- a/mediarepo-ui/src-tauri/Cargo.toml +++ b/mediarepo-ui/src-tauri/Cargo.toml @@ -25,7 +25,7 @@ features = [ "env-filter" ] [dependencies.mediarepo-api] git = "https://github.com/Trivernis/mediarepo-api.git" -rev = "5991f5d3038386c921a901a05c4a72e4ce00b418" +rev = "9599020726eb9db47b9be7a6c41c2942a477ba09" features = [ "tauri-plugin" ] [features] diff --git a/mediarepo-ui/src/app/components/core/core.component.ts b/mediarepo-ui/src/app/components/core/core.component.ts index 4d60e61..d1812e8 100644 --- a/mediarepo-ui/src/app/components/core/core.component.ts +++ b/mediarepo-ui/src/app/components/core/core.component.ts @@ -6,6 +6,7 @@ import {TagService} from "../../services/tag/tag.service"; import {TabService} from "../../services/tab/tab.service"; import {TabCategory} from "../../models/TabCategory"; import {TabState} from "../../models/TabState.rs"; +import {AppState} from "../../models/AppState"; @Component({ selector: "app-core", @@ -16,6 +17,8 @@ export class CoreComponent implements OnInit { public selectedRepository: Repository | undefined; public tabs: TabState[] = []; + public appState: AppState = new AppState(); + private stateInterval?: number; @ViewChild("tabGroup") tabGroup!: MatTabGroup; public newTab = false; @@ -33,10 +36,14 @@ export class CoreComponent implements OnInit { if (this.selectedRepository) { await this.loadRepoData(); - this.addTab(); + if (this.appState.tabs.length === 0) { + this.addTab(); + } } else { + clearInterval(this.stateInterval); this.newTab = false; this.tabService.closeAllTabs(); + this.appState = new AppState(); } }); this.tabService.tabs.subscribe(tabs => { @@ -46,6 +53,9 @@ export class CoreComponent implements OnInit { async loadRepoData() { await this.tagService.loadTags(); + this.appState = await this.repoService.getFrontendState(); + this.tabService.restoreFromState(this.appState); + this.startStateSaveRoutine(); } public onTabSelectionChange(event: MatTabChangeEvent): void { @@ -90,4 +100,13 @@ export class CoreComponent implements OnInit { this.closeTab(tab); } } + + private startStateSaveRoutine() { + clearInterval(this.stateInterval); + this.stateInterval = setInterval(async () => this.saveState(), 10000); + } + + private async saveState() { + await this.repoService.setFrontendState(this.appState); + } } diff --git a/mediarepo-ui/src/app/models/AppState.ts b/mediarepo-ui/src/app/models/AppState.ts new file mode 100644 index 0000000..c6d69e3 --- /dev/null +++ b/mediarepo-ui/src/app/models/AppState.ts @@ -0,0 +1,27 @@ +import {TabState} from "./TabState.rs"; +import {FileService} from "../services/file/file.service"; + +export class AppState { + + public tabs: TabState[] = []; + + constructor() { + } + + public static deserializeJson(stateString: string, fileService: FileService): AppState { + let state = JSON.parse(stateString); + let appState = new AppState(); + for (let tab of state.tabs) { + appState.tabs.push(TabState.fromDTO(tab, fileService)); + } + + return appState + } + + public serializeJson(): string { + const tabDTOs = this.tabs.map(tab => tab.getDTO()); + return JSON.stringify({ + tabs: tabDTOs + }); + } +} diff --git a/mediarepo-ui/src/app/models/TabState.rs.ts b/mediarepo-ui/src/app/models/TabState.rs.ts index b8e679b..0105ddf 100644 --- a/mediarepo-ui/src/app/models/TabState.rs.ts +++ b/mediarepo-ui/src/app/models/TabState.rs.ts @@ -2,9 +2,14 @@ import {BehaviorSubject} from "rxjs"; import {TabCategory} from "./TabCategory"; import {FileService} from "../services/file/file.service"; import {File} from "./File"; -import {FilterExpression} from "./FilterExpression"; +import { + FilterExpression, + OrFilterExpression, + SingleFilterExpression +} from "./FilterExpression"; import {SortKey} from "./SortKey"; import {debounceTime} from "rxjs/operators"; +import {TagQuery} from "./TagQuery"; export class TabState { public uuid: number; @@ -40,4 +45,31 @@ export class TabState { public setSortKeys(keys: SortKey[]) { this.sortKeys.next(keys) } + + public static fromDTO(dto: any, fileService: FileService): TabState { + const state = new TabState(dto.uuid, dto.category, fileService); + const filters = dto.filters.map((f: {filter: any, filter_type: any}) => { + if (f.filter_type === "OrExpression") { + return new OrFilterExpression(f.filter.map((f: any) => new TagQuery(f.tag, f.negate))) + } else { + return new SingleFilterExpression(new TagQuery(f.filter.tag, f.filter.negate)) + } + }) + const sortKeys = dto.sortKeys.map((s: {sortType: any, sortDirection: any, namespaceName: any}) => + new SortKey(s.sortType, s.sortDirection, s.namespaceName) + ); + state.filters.next(filters); + state.sortKeys.next(sortKeys); + + return state + } + + public getDTO(): any { + return { + uuid: this.uuid, + category: this.category, + filters: this.filters.value, + sortKeys: this.sortKeys.value, + }; + } } diff --git a/mediarepo-ui/src/app/services/file/file.service.ts b/mediarepo-ui/src/app/services/file/file.service.ts index fd650cc..5858047 100644 --- a/mediarepo-ui/src/app/services/file/file.service.ts +++ b/mediarepo-ui/src/app/services/file/file.service.ts @@ -13,19 +13,10 @@ import {FilterExpression} from "../../models/FilterExpression"; }) export class FileService { - thumbnailCache: { [key: number]: Thumbnail[] } = {}; - constructor( @Inject(DomSanitizer) private sanitizer: DomSanitizer, - private repoService: RepositoryService, ) { - repoService.selectedRepository.subscribe(_ => this.clearCache()); - } - - public clearCache() { - this.thumbnailCache = {}; } - public async getAllFiles(): Promise { return await invoke("plugin:mediarepo|get_all_files"); } diff --git a/mediarepo-ui/src/app/services/repository/repository.service.ts b/mediarepo-ui/src/app/services/repository/repository.service.ts index 1dc22ab..b348b5f 100644 --- a/mediarepo-ui/src/app/services/repository/repository.service.ts +++ b/mediarepo-ui/src/app/services/repository/repository.service.ts @@ -5,6 +5,8 @@ import {invoke} from "@tauri-apps/api/tauri"; import {listen} from "@tauri-apps/api/event"; import {Info} from "../../models/Info"; import {ErrorBrokerService} from "../error-broker/error-broker.service"; +import {AppState} from "../../models/AppState"; +import {FileService} from "../file/file.service"; @Injectable({ providedIn: "root" @@ -14,7 +16,7 @@ export class RepositoryService { public selectedRepository = new BehaviorSubject( undefined); - constructor(private errorBroker: ErrorBrokerService) { + constructor(private errorBroker: ErrorBrokerService, private fileService: FileService) { this.registerListener() } @@ -145,10 +147,37 @@ export class RepositoryService { await invoke("plugin:mediarepo|start_daemon", {repoPath}) } + /** + * Initializes a folder as a repository + * @param {string} repoPath + * @returns {Promise} + */ public async initRepository(repoPath: string): Promise { await invoke("plugin:mediarepo|init_repository", {repoPath}); } + /** + * Returns the state of the frontend + * @returns {Promise} + */ + public async getFrontendState(): Promise { + let stateString = await invoke("plugin:mediarepo|get_frontend_state"); + if (stateString) { + return AppState.deserializeJson(stateString, this.fileService) + } else { + return new AppState(); + } + } + + /** + * Sets the state of the frontend + * @param {AppState} state + * @returns {Promise} + */ + public async setFrontendState(state: AppState): Promise { + await invoke("plugin:mediarepo|set_frontend_state", {state: state.serializeJson()}) + } + async loadSelectedRepository() { let active_repo = await invoke( "plugin:mediarepo|get_active_repository"); diff --git a/mediarepo-ui/src/app/services/tab/tab.service.ts b/mediarepo-ui/src/app/services/tab/tab.service.ts index f28d47b..53f9f39 100644 --- a/mediarepo-ui/src/app/services/tab/tab.service.ts +++ b/mediarepo-ui/src/app/services/tab/tab.service.ts @@ -3,6 +3,7 @@ import {BehaviorSubject} from "rxjs"; import {TabState} from "../../models/TabState.rs"; import {TabCategory} from "../../models/TabCategory"; import {FileService} from "../file/file.service"; +import {AppState} from "../../models/AppState"; @Injectable({ providedIn: "root" @@ -12,10 +13,16 @@ export class TabService { private tabIdCounter = 0; public selectedTab = new BehaviorSubject(0); public tabs = new BehaviorSubject([]); + private appState?: AppState; constructor(private fileService: FileService) { } + public restoreFromState(appState: AppState) { + this.tabs.next(appState.tabs); + this.appState = appState; + } + public setSelectedTab(index: number) { this.selectedTab.next(index); } @@ -23,6 +30,7 @@ export class TabService { public addTab(category: TabCategory): TabState { const state = new TabState(this.tabIdCounter++, category, this.fileService); this.tabs.next([...this.tabs.value, state]); + this.saveState(); return state; } @@ -30,10 +38,18 @@ export class TabService { const index = this.tabs.value.findIndex(t => t.uuid === uuid); const tabs = this.tabs.value; tabs.splice(index, 1) + this.saveState(); this.tabs.next(tabs); } public closeAllTabs() { this.tabs.next([]); + this.saveState(); + } + + private saveState() { + if (this.appState) { + this.appState.tabs = this.tabs.value; + } } }