Add somehow working file content retrieval

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 5e4d6e098f
commit 2940f8089a

@ -186,17 +186,6 @@ dependencies = [
"wildmatch",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
@ -951,19 +940,6 @@ dependencies = [
"syn",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fastrand"
version = "1.5.0"
@ -1541,12 +1517,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee9694f83d9b7c09682fdb32213682939507884e5bcf227be9aff5d644b90dc"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "ico"
version = "0.1.0"
@ -1820,9 +1790,8 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mediarepo"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=edc5877582cde744fe23c22f31075da792821e77#edc5877582cde744fe23c22f31075da792821e77"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
dependencies = [
"env_logger",
"log",
"mediarepo-core",
"mediarepo-socket",
@ -1832,7 +1801,7 @@ dependencies = [
[[package]]
name = "mediarepo-core"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=edc5877582cde744fe23c22f31075da792821e77#edc5877582cde744fe23c22f31075da792821e77"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
dependencies = [
"base64",
"multibase",
@ -1850,7 +1819,7 @@ dependencies = [
[[package]]
name = "mediarepo-database"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=edc5877582cde744fe23c22f31075da792821e77#edc5877582cde744fe23c22f31075da792821e77"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
dependencies = [
"chrono",
"mediarepo-core",
@ -1861,11 +1830,13 @@ dependencies = [
[[package]]
name = "mediarepo-model"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=edc5877582cde744fe23c22f31075da792821e77#edc5877582cde744fe23c22f31075da792821e77"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
dependencies = [
"chrono",
"mediarepo-core",
"mediarepo-database",
"mime",
"mime_guess",
"sea-orm",
"serde",
"tokio",
@ -1875,7 +1846,7 @@ dependencies = [
[[package]]
name = "mediarepo-socket"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=edc5877582cde744fe23c22f31075da792821e77#edc5877582cde744fe23c22f31075da792821e77"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
dependencies = [
"chrono",
"mediarepo-core",
@ -1900,6 +1871,22 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.1.4"
@ -3769,15 +3756,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thin-slice"
version = "0.1.1"
@ -3990,6 +3968,15 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.7"

@ -30,7 +30,7 @@ features = ["fs", "io-std", "io-util"]
[dependencies.mediarepo]
git = "https://github.com/Trivernis/mediarepo-daemon"
rev = "edc5877582cde744fe23c22f31075da792821e77"
rev = "a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
features = ["library"]
default-features=false

@ -0,0 +1,29 @@
use mediarepo::requests::ReadFileRequest;
use mediarepo::responses::FileResponse;
use crate::context::Context;
use crate::error::{AppError, AppResult};
#[tauri::command]
pub async fn get_all_files(context: tauri::State<'_, Context>) -> AppResult<Vec<FileResponse>> {
let ipc = context.ipc.read().await;
if let Some(ipc) = &*ipc {
let response = ipc.emitter.emit_to("files", "all_files", ()).await?.await_reply(&ipc).await?;
Ok(response.data::<Vec<FileResponse>>()?)
} else {
Err(AppError::new("No ipc connection."))
}
}
#[tauri::command]
pub async fn read_file_by_hash(hash: String, context: tauri::State<'_, Context>) -> AppResult<Vec<u8>> {
let ipc = context.ipc.read().await;
if let Some(ipc) = &*ipc {
let response = ipc.emitter.emit_to("files", "read_file", ReadFileRequest::Hash(hash)).await?.await_reply(&ipc).await?;
Ok(response.data::<Vec<u8>>()?)
} else {
Err(AppError::new("No ipc connection."))
}
}

@ -2,6 +2,7 @@ use crate::context::Context;
use crate::error::AppResult;
pub mod repo;
pub mod files;
#[tauri::command]
pub async fn emit_info(context: tauri::State<'_, Context>) -> AppResult<()> {

@ -1,5 +1,4 @@
use std::path::PathBuf;
use rmp_ipc::IPCBuilder;
use serde::{Serialize, Deserialize};
use crate::context::Context;
use crate::error::{AppError, AppResult};

@ -4,6 +4,7 @@
)]
use crate::commands::repo::{get_repositories, add_repository, select_repository, get_active_repository};
use crate::commands::files::{get_all_files, read_file_by_hash};
use crate::commands::emit_info;
use crate::context::Context;
use crate::settings::load_settings;
@ -20,7 +21,7 @@ fn main() {
tauri::Builder::default()
.manage(context)
.invoke_handler(tauri::generate_handler![get_repositories, add_repository, select_repository, get_active_repository, emit_info])
.invoke_handler(tauri::generate_handler![get_repositories, add_repository, select_repository, get_active_repository, emit_info, get_all_files, read_file_by_hash])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

@ -23,10 +23,8 @@ export class AppComponent implements OnInit{
}
async ngOnInit() {
this.errorBroker.errorCb = (err: { message: string }) => {
console.error(err);
this.showError(err)
};
this.errorBroker.errorCb = (err: { message: string }) => this.showError(err);
this.errorBroker.infoCb = (info: string) => this.showInfo(info);
await this.dataloaderService.loadData();
if (this.repoService.selectedRepository.getValue() == undefined) {
await this.router.navigate(["repositories"])
@ -39,4 +37,11 @@ export class AppComponent implements OnInit{
duration: 2000,
});
}
private showInfo(info: string) {
this.snackBar.open(info, undefined, {
panelClass: "primary",
duration: 2000,
});
}
}

@ -11,11 +11,15 @@ import {MatCardModule} from "@angular/material/card";
import {MatListModule} from "@angular/material/list";
import {MatButtonModule} from "@angular/material/button";
import {MatToolbarModule} from "@angular/material/toolbar";
import {MatSnackBar, MatSnackBarModule} from "@angular/material/snack-bar";
import {MatSnackBarModule} from "@angular/material/snack-bar";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInputModule} from "@angular/material/input";
import {ReactiveFormsModule} from "@angular/forms";
import { RepoFormComponent } from './pages/repositories/repo-form/repo-form.component';
import { FileGridComponent } from './components/file-grid/file-grid.component';
import {MatSidenavModule} from "@angular/material/sidenav";
import {MatGridListModule} from "@angular/material/grid-list";
import { FileGridEntryComponent } from './components/file-grid/file-grid-entry/file-grid-entry.component';
@NgModule({
declarations: [
@ -23,7 +27,9 @@ import { RepoFormComponent } from './pages/repositories/repo-form/repo-form.comp
RepositoriesComponent,
HomeComponent,
RepositoryCardComponent,
RepoFormComponent
RepoFormComponent,
FileGridComponent,
FileGridEntryComponent,
],
imports: [
BrowserModule,
@ -37,6 +43,8 @@ import { RepoFormComponent } from './pages/repositories/repo-form/repo-form.comp
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule,
MatSidenavModule,
MatGridListModule,
],
providers: [],
bootstrap: [AppComponent]

@ -0,0 +1,7 @@
<mat-card>
<mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title>
<mat-card-content>
<img class="entry-image" decoding="async" [src]="contentUrl" alt="image">
</mat-card-content>
<mat-card-footer>{{file?.mime_type}}, {{file?.hash}}</mat-card-footer>
</mat-card>

@ -0,0 +1,15 @@
mat-card, mat-card-content {
height: 100%;
width: 100%;
}
mat-card-content {
justify-content: center;
text-align: center;
}
.entry-image {
max-width: 100%;
max-height: 100%;
margin: auto;
}

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

@ -0,0 +1,28 @@
import {Component, Input, OnInit} from '@angular/core';
import {File} from "../../../models/File";
import {FileService} from "../../../services/file/file.service";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
@Component({
selector: 'app-file-grid-entry',
templateUrl: './file-grid-entry.component.html',
styleUrls: ['./file-grid-entry.component.scss']
})
export class FileGridEntryComponent implements OnInit {
@Input() file: File | undefined;
contentUrl: string | undefined;
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService) { }
async ngOnInit(): Promise<void> {
if (this.file) {
console.log(this.file);
try {
this.contentUrl = await this.fileService.readFile(this.file.hash, this.file.mime_type ?? "image/png");
} catch (err) {
this.errorBroker.showError(err);
}
}
}
}

@ -0,0 +1,5 @@
<mat-grid-list cols="5" rowHeight="1:1">
<mat-grid-tile *ngFor="let file of files">
<app-file-grid-entry [file]="file"></app-file-grid-entry>
</mat-grid-tile>
</mat-grid-list>

@ -0,0 +1,4 @@
app-file-grid-entry {
height: 100%;
width: 100%
}

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

@ -0,0 +1,20 @@
import { Component, OnInit } from '@angular/core';
import {File} from "../../models/File";
import {FileService} from "../../services/file/file.service";
@Component({
selector: 'app-file-grid',
templateUrl: './file-grid.component.html',
styleUrls: ['./file-grid.component.scss']
})
export class FileGridComponent implements OnInit {
files: File[] = [];
constructor(private fileService: FileService) { }
ngOnInit(): void {
this.fileService.displayedFiles.subscribe((files) => this.files = files);
}
}

@ -0,0 +1,12 @@
export class File {
constructor(
public name: string | undefined,
public comment: string | undefined,
public hash: string,
public file_type: number,
public mime_type: string | undefined,
public creation_time: Date,
public change_time: Date,
public import_time: Date,
) {}
}

@ -0,0 +1,4 @@
export type Info = {
name: string;
version: string;
}

@ -1 +1,14 @@
<p>home works!</p>
<div id="content">
<mat-toolbar color="primary">
<h1>Files</h1>
</mat-toolbar>
<mat-drawer-container>
<mat-drawer mode="side" opened>
<p>Drawer</p>
</mat-drawer>
<mat-drawer-content>
<app-file-grid></app-file-grid>
</mat-drawer-content>
</mat-drawer-container>
</div>

@ -0,0 +1,23 @@
#content {
height: 100vh;
width: 100vw;
position: absolute;
left: 0;
top: 0;
margin: 0;
overflow: hidden
}
mat-drawer {
height: 100%;
}
mat-drawer-content {
overflow-x: hidden;
overflow-y: auto;
height: 100%;
}
mat-drawer-container {
height: 100%;
}

@ -1,15 +1,17 @@
import { Component, OnInit } from '@angular/core';
import {FileService} from "../../services/file/file.service";
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.sass']
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor() { }
constructor(private fileService: FileService) { }
ngOnInit(): void {
async ngOnInit() {
await this.fileService.getFiles();
}
}

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import {Injectable, OnInit} from '@angular/core';
import {BehaviorSubject} from "rxjs";
import {listen} from "@tauri-apps/api/event";
@Injectable({
providedIn: 'root'
@ -7,10 +8,32 @@ import {BehaviorSubject} from "rxjs";
export class ErrorBrokerService {
errorCb: Function | undefined;
infoCb: Function | undefined;
constructor() { }
constructor() {
this.registerListener();
}
async registerListener() {
const _unlisten = await listen("error", event => {
const payload: any = event.payload;
if (payload.message) {
this.showError(payload)
} else {
this.showError(payload.toString())
}
})
}
showInfo(info: string) {
console.log(info);
if (this.infoCb) {
this.infoCb(info);
}
}
showError(error: {message: String}) {
showError(error: {message: string}) {
console.error(error);
if (this.errorCb) {
if (!error.message) {
this.errorCb({message: error});

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

@ -0,0 +1,39 @@
import {Inject, Injectable} from '@angular/core';
import {BehaviorSubject} from "rxjs";
import {File} from "../../models/File";
import {invoke} from "@tauri-apps/api/tauri";
import {DOCUMENT} from "@angular/common";
@Injectable({
providedIn: 'root'
})
export class FileService {
displayedFiles = new BehaviorSubject<File[]>([]);
constructor(@Inject(DOCUMENT) private document: Document) {
}
public async getFiles() {
let all_files = await invoke<File[]>("get_all_files");
this.displayedFiles.next(all_files.slice(0, 50));
}
public async readFile(hash: string, mime_type: string): Promise<string | undefined> {
const data = await invoke<number[]>("read_file_by_hash", {hash});
const blob = new Blob([new Uint8Array(data)], {type: mime_type});
return new Promise<string | undefined>((res, rej) => {
const reader = new FileReader();
reader.onload = (e) => {
const url = e.target?.result
if (url === null) {
res(undefined);
} else {
res(url as string)
}
};
reader.readAsDataURL(blob);
})
}
}

@ -2,6 +2,9 @@ import { Injectable } from '@angular/core';
import {Repository} from "../../models/Repository";
import {BehaviorSubject, Observable} from "rxjs";
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";
@Injectable({
providedIn: 'root'
@ -10,15 +13,28 @@ export class RepositoryService {
repositories = new BehaviorSubject<Repository[]>([]);
public selectedRepository = new BehaviorSubject<Repository | undefined>(undefined);
constructor() {}
constructor(private errorBroker: ErrorBrokerService) {
this.registerListener()
}
async registerListener() {
await listen("info", (event: { payload: Info }) => {
const message = `Connected to ${event.payload.name}, Version: ${event.payload.version}`;
this.errorBroker.showInfo(message);
});
}
public async loadRepositories() {
let active_repo = await invoke<Repository | undefined>("get_active_repository");
this.selectedRepository.next(active_repo);
let repos = await invoke<Repository[]>("get_repositories");
this.repositories.next(repos);
}
public async setRepository(repo: Repository) {
await invoke("select_repository", {name: repo.name});
await invoke("emit_info");
this.selectedRepository.next(repo);
}

Loading…
Cancel
Save