Add jobs and open dialog

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent d48cff688f
commit 3d5fcc8169

@ -1489,8 +1489,8 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "mediarepo-api" name = "mediarepo-api"
version = "0.24.1" version = "0.24.2"
source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=91d8182548bfdb19f2de9afd8c29d5c8ebd48993#91d8182548bfdb19f2de9afd8c29d5c8ebd48993" source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=076e9f344da034fc70c99cb1b9c4359374acc2ca#076e9f344da034fc70c99cb1b9c4359374acc2ca"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bromine", "bromine",

@ -25,7 +25,7 @@ features = [ "env-filter" ]
[dependencies.mediarepo-api] [dependencies.mediarepo-api]
git = "https://github.com/Trivernis/mediarepo-api.git" git = "https://github.com/Trivernis/mediarepo-api.git"
rev = "91d8182548bfdb19f2de9afd8c29d5c8ebd48993" rev = "076e9f344da034fc70c99cb1b9c4359374acc2ca"
features = [ "tauri-plugin" ] features = [ "tauri-plugin" ]
[features] [features]

@ -18,6 +18,7 @@ import {
ReadFileRequest, ReadFileRequest,
RemoveRepositoryRequest, RemoveRepositoryRequest,
ResolvePathsToFilesRequest, ResolvePathsToFilesRequest,
RunJobRequest,
SaveFileRequest, SaveFileRequest,
SelectRepositoryRequest, SelectRepositoryRequest,
SetFrontendStateRequest, SetFrontendStateRequest,
@ -31,7 +32,7 @@ import {
} from "./api-types/repo"; } from "./api-types/repo";
import {NamespaceData, TagData} from "./api-types/tags"; import {NamespaceData, TagData} from "./api-types/tags";
export class MediarepApi { export class MediarepoApi {
public static async hasExecutable(): Promise<boolean> { public static async hasExecutable(): Promise<boolean> {
return this.invokePlugin(ApiFunction.HasExecutable); return this.invokePlugin(ApiFunction.HasExecutable);
@ -157,6 +158,10 @@ export class MediarepApi {
return this.invokePlugin(ApiFunction.SetFrontendState, request); return this.invokePlugin(ApiFunction.SetFrontendState, request);
} }
public static async runJob(request: RunJobRequest): Promise<void> {
return this.invokePlugin(ApiFunction.RunJob, request);
}
private static async invokePlugin<T>(fn: ApiFunction, args?: any): Promise<T> { private static async invokePlugin<T>(fn: ApiFunction, args?: any): Promise<T> {
return invoke<T>(`plugin:mediarepo|${fn}`, args); return invoke<T>(`plugin:mediarepo|${fn}`, args);
} }

@ -35,4 +35,6 @@ export enum ApiFunction {
// state // state
GetFrontendState = "get_frontend_state", GetFrontendState = "get_frontend_state",
SetFrontendState = "set_frontend_state", SetFrontendState = "set_frontend_state",
// jobs
RunJob = "run_job",
} }

@ -0,0 +1,3 @@
export type JobType = "MigrateContentDescriptors"
| "CalculateSizes"
| "CheckIntegrity";

@ -1,5 +1,6 @@
import {FileOsMetadata, FilterExpression, SortKey} from "./files"; import {FileOsMetadata, FilterExpression, SortKey} from "./files";
import {RepositoryData, SizeType} from "./repo"; import {RepositoryData, SizeType} from "./repo";
import {JobType} from "./job";
type NameIdentifierRequest = { type NameIdentifierRequest = {
name: string name: string
@ -92,3 +93,7 @@ type AddFileOptions = {
export type SetFrontendStateRequest = { export type SetFrontendStateRequest = {
state: string state: string
}; };
export type RunJobRequest = {
jobType: JobType,
}

@ -4,7 +4,7 @@
</div> </div>
<div class="repository-list"> <div class="repository-list">
<div *ngFor="let repository of repositories" class="repository-container"> <div *ngFor="let repository of repositories" class="repository-container">
<app-repository-card [repository]="repository"></app-repository-card> <app-repository-card [repository]="repository" (openEvent)="this.onOpenRepository($event)"></app-repository-card>
</div> </div>
</div> </div>
</div> </div>

@ -3,13 +3,24 @@ import {Repository} from "../../../../api/models/Repository";
import { import {
RepositoryService RepositoryService
} from "../../../services/repository/repository.service"; } from "../../../services/repository/repository.service";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog, MatDialogRef} from "@angular/material/dialog";
import { import {
DownloadDaemonDialogComponent DownloadDaemonDialogComponent
} from "./download-daemon-dialog/download-daemon-dialog.component"; } from "./download-daemon-dialog/download-daemon-dialog.component";
import { import {
AddRepositoryDialogComponent AddRepositoryDialogComponent
} from "../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component"; } from "../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component";
import {
ErrorBrokerService
} from "../../../services/error-broker/error-broker.service";
import {BehaviorSubject} from "rxjs";
import {
BusyDialogComponent
} from "../../shared/app-common/busy-dialog/busy-dialog.component";
import {JobService} from "../../../services/job/job.service";
import {StateService} from "../../../services/state/state.service";
type BusyDialogContext = { message: BehaviorSubject<string>, dialog: MatDialogRef<BusyDialogComponent> };
@Component({ @Component({
selector: "app-repositories-tab", selector: "app-repositories-tab",
@ -21,7 +32,10 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
public selectedRepository?: Repository; public selectedRepository?: Repository;
constructor( constructor(
private errorBroker: ErrorBrokerService,
private repoService: RepositoryService, private repoService: RepositoryService,
private jobService: JobService,
private stateService: StateService,
public dialog: MatDialog public dialog: MatDialog
) { ) {
} }
@ -32,13 +46,95 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
this.repositories = repos; this.repositories = repos;
} }
}); });
this.repoService.selectedRepository.subscribe(repo => this.selectedRepository = repo); this.repoService.selectedRepository.subscribe(
repo => this.selectedRepository = repo);
} }
public async ngAfterViewInit() { public async ngAfterViewInit() {
await this.checkAndPromptDaemonExecutable(); await this.checkAndPromptDaemonExecutable();
} }
public async startDaemonAndSelectRepository(repository: Repository) {
try {
let dialogContext = this.openStartupDialog(repository);
let daemonRunning = await this.repoService.checkDaemonRunning(
repository.path!);
if (!daemonRunning) {
dialogContext.message.next("Starting repository daemon...");
await this.repoService.startDaemon(repository.path!);
await new Promise((res, _) => {
setTimeout(res, 2000); // wait for the daemon to start
});
}
await this.selectRepository(repository, dialogContext);
} catch (err) {
this.errorBroker.showError(err);
}
}
public async selectRepository(repository: Repository, dialogContext?: BusyDialogContext) {
dialogContext = dialogContext ?? this.openStartupDialog(repository);
try {
dialogContext.message.next("Opening repository...");
await this.repoService.setRepository(repository);
await this.runRepositoryStartupTasks(dialogContext);
dialogContext.message.next("Restoring previous tabs...");
await this.repoService.loadRepositories();
await this.stateService.loadState();
dialogContext.dialog.close(true);
} catch (err) {
this.errorBroker.showError(err);
dialogContext.message.next(
"Failed to open repository: " + err.toString());
await this.forceCloseRepository();
setTimeout(() => dialogContext!.dialog.close(true), 1000);
}
}
private async forceCloseRepository() {
try {
await this.repoService.closeSelectedRepository();
} catch {
}
try {
await this.repoService.disconnectSelectedRepository();
} catch {
}
}
private async runRepositoryStartupTasks(dialogContext: BusyDialogContext): Promise<void> {
dialogContext.message.next(
"Migrating content descriptors to new format...");
await this.jobService.runJob("MigrateContentDescriptors");
dialogContext.message.next("Calculating repository sizes...");
await this.jobService.runJob("CalculateSizes");
dialogContext.message.next("Checking integrity...");
await this.jobService.runJob("CheckIntegrity");
dialogContext.message.next("Finished repository startup");
}
private openStartupDialog(repository: Repository): BusyDialogContext {
let dialogMessage = new BehaviorSubject<string>(
"Opening repository...");
let dialog = this.dialog.open(BusyDialogComponent, {
data: {
title: `Opening repository ${repository.name}`,
message: dialogMessage,
allowCancel: true,
}, disableClose: true,
minWidth: "30%",
minHeight: "30%",
});
dialog.afterClosed().subscribe(async (result) => {
if (!result) {
await this.forceCloseRepository();
}
});
return {message: dialogMessage, dialog};
}
public openAddRepositoryDialog() { public openAddRepositoryDialog() {
this.dialog.open(AddRepositoryDialogComponent, { this.dialog.open(AddRepositoryDialogComponent, {
disableClose: true, disableClose: true,
@ -49,13 +145,23 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
private async checkAndPromptDaemonExecutable() { private async checkAndPromptDaemonExecutable() {
if (!await this.repoService.checkDameonConfigured()) { if (!await this.repoService.checkDameonConfigured()) {
const result = await this.dialog.open(DownloadDaemonDialogComponent, { const result = await this.dialog.open(DownloadDaemonDialogComponent,
disableClose: true, {
}).afterClosed().toPromise(); disableClose: true,
}).afterClosed().toPromise();
if (result) { if (result) {
// recursion avoidance // recursion avoidance
setTimeout(async () => await this.checkAndPromptDaemonExecutable(), 0); setTimeout(
async () => await this.checkAndPromptDaemonExecutable(), 0);
} }
} }
} }
public async onOpenRepository(repository: Repository) {
if (!repository.local) {
await this.selectRepository(repository);
} else {
await this.startDaemonAndSelectRepository(repository);
}
}
} }

@ -9,11 +9,11 @@
<p *ngIf="!repository.local" class="repository-address">{{repository.address}}</p> <p *ngIf="!repository.local" class="repository-address">{{repository.address}}</p>
</mat-card-content> </mat-card-content>
<mat-action-list> <mat-action-list>
<button (click)="startDaemonAndSelectRepository()" *ngIf="!this.isSelectedRepository() && repository.local" <button (click)="this.openEvent.next(repository)" *ngIf="!this.isSelectedRepository() && repository.local"
color="primary" color="primary"
mat-flat-button>Open mat-flat-button>Open
</button> </button>
<button (click)="selectRepository()" *ngIf="!this.isSelectedRepository() && !repository.local" <button (click)="this.openEvent.next(repository)" *ngIf="!this.isSelectedRepository() && !repository.local"
[disabled]="!this.daemonRunning" [disabled]="!this.daemonRunning"
color="primary" mat-flat-button>Connect color="primary" mat-flat-button>Connect
</button> </button>

@ -1,11 +1,15 @@
import {Component, Input, OnDestroy, OnInit, ViewChild} from "@angular/core"; import {
Component, EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
ViewChild
} from "@angular/core";
import {Repository} from "../../../../../api/models/Repository"; import {Repository} from "../../../../../api/models/Repository";
import { import {
RepositoryService RepositoryService
} from "../../../../services/repository/repository.service"; } from "../../../../services/repository/repository.service";
import {
ErrorBrokerService
} from "../../../../services/error-broker/error-broker.service";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import { import {
ConfirmDialogComponent ConfirmDialogComponent
@ -25,6 +29,8 @@ import {
export class RepositoryCardComponent implements OnInit, OnDestroy { export class RepositoryCardComponent implements OnInit, OnDestroy {
@Input() repository!: Repository; @Input() repository!: Repository;
@Output() openEvent = new EventEmitter<Repository>();
@ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent; @ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent;
public daemonRunning: boolean = false; public daemonRunning: boolean = false;
@ -33,7 +39,6 @@ export class RepositoryCardComponent implements OnInit, OnDestroy {
constructor( constructor(
public repoService: RepositoryService, public repoService: RepositoryService,
private errorBroker: ErrorBrokerService,
public dialog: MatDialog) { public dialog: MatDialog) {
} }
@ -118,31 +123,6 @@ export class RepositoryCardComponent implements OnInit, OnDestroy {
} }
} }
public async startDaemonAndSelectRepository() {
try {
if (!this.daemonRunning) {
await this.repoService.startDaemon(this.repository.path!);
this.daemonRunning = true;
await new Promise((res, _) => {
setTimeout(res, 2000); // wait for the daemon to start
});
}
await this.selectRepository();
} catch (err) {
this.errorBroker.showError(err);
}
}
public async selectRepository() {
this.busyIndicator.setBusy(true);
try {
await this.repoService.setRepository(this.repository);
} catch (err) {
this.errorBroker.showError(err);
}
this.busyIndicator.setBusy(false);
}
async checkRemoteRepositoryStatus() { async checkRemoteRepositoryStatus() {
this.daemonRunning = await this.repoService.checkDaemonRunning( this.daemonRunning = await this.repoService.checkDaemonRunning(
this.repository.address!); this.repository.address!);

@ -21,6 +21,7 @@ import {
import { import {
MetadataEntryComponent MetadataEntryComponent
} from "./metadata-entry/metadata-entry.component"; } from "./metadata-entry/metadata-entry.component";
import { BusyDialogComponent } from './busy-dialog/busy-dialog.component';
@NgModule({ @NgModule({
@ -31,6 +32,7 @@ import {
ContentAwareImageComponent, ContentAwareImageComponent,
InputReceiverDirective, InputReceiverDirective,
MetadataEntryComponent, MetadataEntryComponent,
BusyDialogComponent,
], ],
exports: [ exports: [
ConfirmDialogComponent, ConfirmDialogComponent,

@ -0,0 +1,12 @@
<h1 mat-dialog-title class="title">
{{title}}
</h1>
<div class="content" mat-dialog-content>
<mat-progress-spinner mode="indeterminate" color="primary"></mat-progress-spinner>
{{message}}
</div>
<div mat-dialog-actions *ngIf="this.allowCancel" class="busy-dialog-actions">
<button mat-flat-button (click)="this.dialogRef.close(false)">
Cancel
</button>
</div>

@ -0,0 +1,17 @@
mat-progress-spinner {
margin: auto;
padding-bottom: 1em;
}
.title, .content {
text-align: center;
}
.busy-dialog-actions {
display: block;
button {
float: right;
margin-left: 1em;
}
}

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BusyDialogComponent } from './busy-dialog.component';
describe('BusyDialogComponent', () => {
let component: BusyDialogComponent;
let fixture: ComponentFixture<BusyDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BusyDialogComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BusyDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,27 @@
import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {BehaviorSubject} from "rxjs";
export type BusyDialogData = {
title: string,
message: BehaviorSubject<string>,
allowCancel?: boolean,
}
@Component({
selector: "app-busy-dialog",
templateUrl: "./busy-dialog.component.html",
styleUrls: ["./busy-dialog.component.scss"]
})
export class BusyDialogComponent {
public title: string;
public message?: string;
public allowCancel: boolean;
constructor(public dialogRef: MatDialogRef<BusyDialogComponent>, @Inject(MAT_DIALOG_DATA) data: BusyDialogData) {
this.title = data.title;
data.message.subscribe(m => this.message = m);
this.allowCancel = data.allowCancel ?? false;
}
}

@ -32,14 +32,16 @@
</div> </div>
<div class="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill> <div class="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill>
<cdk-virtual-scroll-viewport itemSize="50" maxBufferPx="4000" minBufferPx="500">
<div (click)="addSearchTag(tag.getNormalizedOutput())" <app-busy-indicator [busy]="this.tagsLoading" [blurBackground]="true" [darkenBackground]="false">
(contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)" <cdk-virtual-scroll-viewport itemSize="50" maxBufferPx="4000" minBufferPx="500">
*cdkVirtualFor="let tag of contextTags" class="selectable-tag"> <div (click)="addSearchTag(tag.getNormalizedOutput())"
<app-tag-item [tag]="tag"></app-tag-item> (contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)"
</div> *cdkVirtualFor="let tag of contextTags" class="selectable-tag">
</cdk-virtual-scroll-viewport> <app-tag-item [tag]="tag"></app-tag-item>
<app-busy-indicator [busy]="this.tagsLoading" [blurBackground]="true" [darkenBackground]="false"></app-busy-indicator> </div>
</cdk-virtual-scroll-viewport>
</app-busy-indicator>
</div> </div>
</div> </div>

@ -123,12 +123,3 @@ mat-divider {
.file-tag-list { .file-tag-list {
position: relative; position: relative;
} }
app-busy-indicator {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 99;
}

@ -8,6 +8,7 @@ export class AppState {
private tabIdCounter = 0; private tabIdCounter = 0;
public tabs = new BehaviorSubject<TabState[]>([]); public tabs = new BehaviorSubject<TabState[]>([]);
public selectedTab = new BehaviorSubject<number | undefined>(undefined); public selectedTab = new BehaviorSubject<number | undefined>(undefined);
public repoName: string | undefined;
private readonly fileService: FileService private readonly fileService: FileService
@ -36,6 +37,7 @@ export class AppState {
appState.tabIdCounter = state.tabIdCounter; appState.tabIdCounter = state.tabIdCounter;
appState.selectedTab.next(state.selectedTab); appState.selectedTab.next(state.selectedTab);
appState.repoName = state.repoName;
return appState; return appState;
} }
@ -43,6 +45,7 @@ export class AppState {
public serializeJson(): string { public serializeJson(): string {
const tabDTOs = this.tabs.value.map(tab => tab.getDTO()); const tabDTOs = this.tabs.value.map(tab => tab.getDTO());
return JSON.stringify({ return JSON.stringify({
repoName: this.repoName,
tabs: tabDTOs, tabs: tabDTOs,
tabIdCounter: this.tabIdCounter, tabIdCounter: this.tabIdCounter,
selectedTab: this.selectedTab.value, selectedTab: this.selectedTab.value,

@ -3,7 +3,7 @@ import {File} from "../../../api/models/File";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser"; import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {SortKey} from "../../models/SortKey"; import {SortKey} from "../../models/SortKey";
import {GenericFilter} from "../../models/GenericFilter"; import {GenericFilter} from "../../models/GenericFilter";
import {MediarepApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
import {mapMany, mapNew} from "../../../api/models/adaptors"; import {mapMany, mapNew} from "../../../api/models/adaptors";
import {FileMetadata} from "../../../api/api-types/files"; import {FileMetadata} from "../../../api/api-types/files";
@ -19,20 +19,20 @@ export class FileService {
} }
public async getAllFiles(): Promise<File[]> { public async getAllFiles(): Promise<File[]> {
return MediarepApi.getAllFiles().then(mapMany(mapNew(File))); return MediarepoApi.getAllFiles().then(mapMany(mapNew(File)));
} }
public async findFiles(filters: GenericFilter[], sortBy: SortKey[]): Promise<File[]> { public async findFiles(filters: GenericFilter[], sortBy: SortKey[]): Promise<File[]> {
let backendFilters = filters.map(f => f.toBackendType()); let backendFilters = filters.map(f => f.toBackendType());
return MediarepApi.findFiles({filters: backendFilters, sortBy: sortBy.map(k => k.toBackendType())}).then(mapMany(mapNew(File))); return MediarepoApi.findFiles({filters: backendFilters, sortBy: sortBy.map(k => k.toBackendType())}).then(mapMany(mapNew(File)));
} }
public async getFileMetadata(id: number): Promise<FileMetadata> { public async getFileMetadata(id: number): Promise<FileMetadata> {
return MediarepApi.getFileMetadata({id}); return MediarepoApi.getFileMetadata({id});
} }
public async updateFileName(id: number, name: string): Promise<FileMetadata> { public async updateFileName(id: number, name: string): Promise<FileMetadata> {
return MediarepApi.updateFileName({id, name}); return MediarepoApi.updateFileName({id, name});
} }
/** /**
@ -64,7 +64,7 @@ export class FileService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async saveFile(file: File, targetPath: string) { public async saveFile(file: File, targetPath: string) {
await MediarepApi.saveFileLocally({id: file.id, path: targetPath}); await MediarepoApi.saveFileLocally({id: file.id, path: targetPath});
} }
/** /**
@ -73,7 +73,7 @@ export class FileService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async deleteThumbnails(file: File) { public async deleteThumbnails(file: File) {
await MediarepApi.deleteThumbnails({id: file.id}); await MediarepoApi.deleteThumbnails({id: file.id});
} }
/** /**
@ -82,7 +82,7 @@ export class FileService {
* @returns {Promise<SafeResourceUrl>} * @returns {Promise<SafeResourceUrl>}
*/ */
public async readFile(file: File): Promise<SafeResourceUrl> { public async readFile(file: File): Promise<SafeResourceUrl> {
const data = await MediarepApi.readFile({mimeType: file.mimeType, hash: file.cd}); const data = await MediarepoApi.readFile({mimeType: file.mimeType, hash: file.cd});
const blob = new Blob([new Uint8Array(data)], {type: file.mimeType}); const blob = new Blob([new Uint8Array(data)], {type: file.mimeType});
const url = URL?.createObjectURL(blob); const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url); return this.sanitizer.bypassSecurityTrustResourceUrl(url);

@ -1,7 +1,7 @@
import {Injectable} from "@angular/core"; import {Injectable} from "@angular/core";
import {AddFileOptions} from "../../models/AddFileOptions"; import {AddFileOptions} from "../../models/AddFileOptions";
import {File} from "../../../api/models/File"; import {File} from "../../../api/models/File";
import {MediarepApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
import {mapNew,} from "../../../api/models/adaptors"; import {mapNew,} from "../../../api/models/adaptors";
import {FileOsMetadata} from "../../../api/api-types/files"; import {FileOsMetadata} from "../../../api/api-types/files";
@ -19,7 +19,7 @@ export class ImportService {
* @returns {Promise<FileOsMetadata[]>} * @returns {Promise<FileOsMetadata[]>}
*/ */
public async resolvePathsToFiles(paths: string[]): Promise<FileOsMetadata[]> { public async resolvePathsToFiles(paths: string[]): Promise<FileOsMetadata[]> {
return MediarepApi.resolvePathsToFiles({paths}); return MediarepoApi.resolvePathsToFiles({paths});
} }
/** /**
@ -29,6 +29,6 @@ export class ImportService {
* @returns {Promise<File>} * @returns {Promise<File>}
*/ */
public async addLocalFile(metadata: FileOsMetadata, options: AddFileOptions): Promise<File> { public async addLocalFile(metadata: FileOsMetadata, options: AddFileOptions): Promise<File> {
return MediarepApi.addLocalFile({metadata, options}).then(mapNew(File)); return MediarepoApi.addLocalFile({metadata, options}).then(mapNew(File));
} }
} }

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { JobService } from './job.service';
describe('JobService', () => {
let service: JobService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(JobService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

@ -0,0 +1,16 @@
import {Injectable} from "@angular/core";
import {MediarepoApi} from "../../../api/Api";
import {JobType} from "../../../api/api-types/job";
@Injectable({
providedIn: "root"
})
export class JobService {
constructor() {
}
public async runJob(jobType: JobType): Promise<void> {
return MediarepoApi.runJob({jobType});
}
}

@ -5,7 +5,7 @@ import {listen} from "@tauri-apps/api/event";
import {Info} from "../../models/Info"; import {Info} from "../../models/Info";
import {ErrorBrokerService} from "../error-broker/error-broker.service"; import {ErrorBrokerService} from "../error-broker/error-broker.service";
import {RepositoryMetadata} from "../../models/RepositoryMetadata"; import {RepositoryMetadata} from "../../models/RepositoryMetadata";
import {MediarepApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
import {mapMany, mapNew, mapOptional,} from "../../../api/models/adaptors"; import {mapMany, mapNew, mapOptional,} from "../../../api/models/adaptors";
import {SizeMetadata, SizeType} from "../../../api/api-types/repo"; import {SizeMetadata, SizeType} from "../../../api/api-types/repo";
@ -34,7 +34,7 @@ export class RepositoryService {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
public async checkDameonConfigured(): Promise<boolean> { public async checkDameonConfigured(): Promise<boolean> {
return MediarepApi.hasExecutable(); return MediarepoApi.hasExecutable();
} }
/** /**
@ -43,7 +43,7 @@ export class RepositoryService {
*/ */
public async loadRepositories() { public async loadRepositories() {
await this.loadSelectedRepository(); await this.loadSelectedRepository();
let repos = await MediarepApi.getRepositories().then(mapMany(mapNew(Repository))); let repos = await MediarepoApi.getRepositories().then(mapMany(mapNew(Repository)));
this.repositories.next(repos); this.repositories.next(repos);
} }
@ -67,10 +67,8 @@ export class RepositoryService {
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);
} }
} }
await MediarepApi.selectRepository({name: repo.name}); await MediarepoApi.selectRepository({name: repo.name});
await this.loadRepositories();
} }
/** /**
@ -78,7 +76,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async disconnectSelectedRepository() { public async disconnectSelectedRepository() {
await MediarepApi.disconnectRepository(); await MediarepoApi.disconnectRepository();
await this.loadRepositories(); await this.loadRepositories();
} }
@ -87,7 +85,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async closeSelectedRepository() { public async closeSelectedRepository() {
await MediarepApi.closeLocalRepository(); await MediarepoApi.closeLocalRepository();
await this.loadRepositories(); await this.loadRepositories();
} }
@ -100,7 +98,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async addRepository(name: string, path: string | undefined, address: string | undefined, local: boolean) { public async addRepository(name: string, path: string | undefined, address: string | undefined, local: boolean) {
let repos = await MediarepApi.addRepository({name, path, address, local}).then(mapMany(mapNew(Repository))); let repos = await MediarepoApi.addRepository({name, path, address, local}).then(mapMany(mapNew(Repository)));
this.repositories.next(repos); this.repositories.next(repos);
} }
@ -110,7 +108,7 @@ export class RepositoryService {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
public async checkDaemonRunning(address: string): Promise<boolean> { public async checkDaemonRunning(address: string): Promise<boolean> {
return MediarepApi.checkDaemonRunning({address}); return MediarepoApi.checkDaemonRunning({address});
} }
/** /**
@ -119,7 +117,7 @@ export class RepositoryService {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
public async checkLocalRepositoryExists(path: string): Promise<boolean> { public async checkLocalRepositoryExists(path: string): Promise<boolean> {
return await MediarepApi.checkLocalRepositoryExists({path}); return await MediarepoApi.checkLocalRepositoryExists({path});
} }
/** /**
@ -128,7 +126,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async removeRepository(name: string): Promise<void> { public async removeRepository(name: string): Promise<void> {
await MediarepApi.removeRepository({name}); await MediarepoApi.removeRepository({name});
await this.loadRepositories(); await this.loadRepositories();
} }
@ -138,7 +136,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async deleteRepository(name: string): Promise<void> { public async deleteRepository(name: string): Promise<void> {
await MediarepApi.deleteRepository({name}); await MediarepoApi.deleteRepository({name});
await this.removeRepository(name); await this.removeRepository(name);
} }
@ -148,7 +146,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async startDaemon(repoPath: string): Promise<void> { public async startDaemon(repoPath: string): Promise<void> {
return MediarepApi.startDaemon({repoPath}); return MediarepoApi.startDaemon({repoPath});
} }
/** /**
@ -157,7 +155,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async initRepository(repoPath: string): Promise<void> { public async initRepository(repoPath: string): Promise<void> {
return MediarepApi.initRepository({repoPath}); return MediarepoApi.initRepository({repoPath});
} }
/** /**
@ -165,7 +163,7 @@ export class RepositoryService {
* @returns {Promise<RepositoryMetadata>} * @returns {Promise<RepositoryMetadata>}
*/ */
public async getRepositoryMetadata(): Promise<RepositoryMetadata> { public async getRepositoryMetadata(): Promise<RepositoryMetadata> {
return MediarepApi.getRepositoryMetadata(); return MediarepoApi.getRepositoryMetadata();
} }
/** /**
@ -174,11 +172,11 @@ export class RepositoryService {
* @param sizeType * @param sizeType
*/ */
public async getSize(sizeType: SizeType): Promise<SizeMetadata> { public async getSize(sizeType: SizeType): Promise<SizeMetadata> {
return MediarepApi.getSize({sizeType}); return MediarepoApi.getSize({sizeType});
} }
async loadSelectedRepository() { async loadSelectedRepository() {
let active_repo = await MediarepApi.getActiveRepository().then(mapOptional(mapNew(Repository))); let active_repo = await MediarepoApi.getActiveRepository().then(mapOptional(mapNew(Repository)));
this.selectedRepository.next(active_repo); this.selectedRepository.next(active_repo);
} }
} }

@ -5,7 +5,7 @@ import {FileService} from "../file/file.service";
import {RepositoryService} from "../repository/repository.service"; import {RepositoryService} from "../repository/repository.service";
import {TabState} from "../../models/TabState"; import {TabState} from "../../models/TabState";
import {debounceTime} from "rxjs/operators"; import {debounceTime} from "rxjs/operators";
import {MediarepApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
@Injectable({ @Injectable({
providedIn: "root" providedIn: "root"
@ -21,7 +21,7 @@ export class StateService {
constructor(private fileService: FileService, private repoService: RepositoryService) { constructor(private fileService: FileService, private repoService: RepositoryService) {
this.state = new BehaviorSubject(new AppState(fileService)); this.state = new BehaviorSubject(new AppState(fileService));
this.repoService.selectedRepository.subscribe(async (repo) => { this.repoService.selectedRepository.subscribe(async (repo) => {
if (repo) { if (repo && (!this.state.value.repoName || this.state.value.repoName !== repo.name)) {
await this.loadState(); await this.loadState();
} else { } else {
const state = new AppState(this.fileService); const state = new AppState(this.fileService);
@ -38,7 +38,7 @@ export class StateService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async loadState() { public async loadState() {
let stateString = await MediarepApi.getFrontendState(); let stateString = await MediarepoApi.getFrontendState();
let state; let state;
if (stateString) { if (stateString) {
@ -46,6 +46,10 @@ export class StateService {
} else { } else {
state = new AppState(this.fileService); state = new AppState(this.fileService);
} }
let selectedRepo = this.repoService.selectedRepository.value;
if (selectedRepo) {
state.repoName = selectedRepo.name;
}
this.subscribeToState(state); this.subscribeToState(state);
this.state.next(state); this.state.next(state);
} }
@ -75,6 +79,8 @@ export class StateService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async saveState(): Promise<void> { public async saveState(): Promise<void> {
await MediarepApi.setFrontendState({state: this.state.value.serializeJson()}); if (this.repoService.selectedRepository.value) {
await MediarepoApi.setFrontendState({state: this.state.value.serializeJson()});
}
} }
} }

@ -3,7 +3,7 @@ import {Tag} from "../../../api/models/Tag";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {Namespace} from "../../../api/models/Namespace"; import {Namespace} from "../../../api/models/Namespace";
import {mapMany, mapNew} from "../../../api/models/adaptors"; import {mapMany, mapNew} from "../../../api/models/adaptors";
import {MediarepApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
@Injectable({ @Injectable({
providedIn: "root" providedIn: "root"
@ -17,28 +17,28 @@ export class TagService {
} }
public async loadTags() { public async loadTags() {
const tags = await MediarepApi.getAllTags().then(mapMany(mapNew(Tag))); const tags = await MediarepoApi.getAllTags().then(mapMany(mapNew(Tag)));
this.tags.next(tags); this.tags.next(tags);
} }
public async loadNamespaces() { public async loadNamespaces() {
const namespaces = await MediarepApi.getAllNamespaces().then(mapMany(mapNew(Namespace))); const namespaces = await MediarepoApi.getAllNamespaces().then(mapMany(mapNew(Namespace)));
this.namespaces.next(namespaces); this.namespaces.next(namespaces);
} }
public async getTagsForFiles(cds: string[]): Promise<Tag[]> { public async getTagsForFiles(cds: string[]): Promise<Tag[]> {
let tags: Tag[] = []; let tags: Tag[] = [];
if (cds.length > 0) { if (cds.length > 0) {
tags = await MediarepApi.getTagsForFiles({cds}).then(mapMany(mapNew(Tag))); tags = await MediarepoApi.getTagsForFiles({cds}).then(mapMany(mapNew(Tag)));
} }
return tags; return tags;
} }
public async createTags(tags: string[]): Promise<Tag[]> { public async createTags(tags: string[]): Promise<Tag[]> {
return MediarepApi.createTags({tags}).then(mapMany(mapNew(Tag))); return MediarepoApi.createTags({tags}).then(mapMany(mapNew(Tag)));
} }
public async changeFileTags(fileId: number, addedTags: number[], removedTags: number[]): Promise<Tag[]> { public async changeFileTags(fileId: number, addedTags: number[], removedTags: number[]): Promise<Tag[]> {
return MediarepApi.changeFileTags({id: fileId, addedTags, removedTags}).then(mapMany(mapNew(Tag))); return MediarepoApi.changeFileTags({id: fileId, addedTags, removedTags}).then(mapMany(mapNew(Tag)));
} }
} }

Loading…
Cancel
Save