From f400f00c2847e2ef9988a9ecad4a2ec352812678 Mon Sep 17 00:00:00 2001 From: fsteff Date: Fri, 26 Nov 2021 17:33:28 +0100 Subject: [PATCH] spaces WIP --- filemanager-ui/src/app/app.module.ts | 6 ++-- filemanager-ui/src/app/drive.service.ts | 9 +++--- .../src/app/file-list/file-list.component.ts | 19 +++++++++--- .../share-dialog/share-dialog.component.css | 8 +++++ .../share-dialog/share-dialog.component.html | 16 ++++++---- .../share-dialog/share-dialog.component.ts | 14 +++++++++ src/ContactsEventHandler.ts | 4 +-- src/DriveEventHandler.ts | 29 ++++++++++++++++--- src/EventInterfaces.ts | 8 +++-- 9 files changed, 90 insertions(+), 23 deletions(-) diff --git a/filemanager-ui/src/app/app.module.ts b/filemanager-ui/src/app/app.module.ts index eae7154..8f835a9 100644 --- a/filemanager-ui/src/app/app.module.ts +++ b/filemanager-ui/src/app/app.module.ts @@ -18,7 +18,8 @@ import { MatListModule} from '@angular/material/list'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import { MatFormFieldModule } from '@angular/material/form-field'; import {MatExpansionModule} from '@angular/material/expansion'; -import {MatSidenavModule} from '@angular/material/sidenav'; +import {MatSidenavModule} from '@angular/material/sidenav'; +import {MatTooltipModule} from '@angular/material/tooltip'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -72,7 +73,8 @@ import { ShareOverviewComponent } from './share-overview/share-overview.componen MatFormFieldModule, MatProgressSpinnerModule, MatExpansionModule, - MatSidenavModule + MatSidenavModule, + MatTooltipModule ], providers: [], bootstrap: [AppComponent] diff --git a/filemanager-ui/src/app/drive.service.ts b/filemanager-ui/src/app/drive.service.ts index 92cfe4a..b3f5492 100644 --- a/filemanager-ui/src/app/drive.service.ts +++ b/filemanager-ui/src/app/drive.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core' import globals from '../../../src/globals' import { Observable, from } from 'rxjs' -import { readdirResult, FileDownload, Stat, Share } from '../../../src/EventInterfaces' +import { readdirResult, FileDownload, Stat, Share, Space } from '../../../src/EventInterfaces' import { ActivatedRoute } from '@angular/router' @Injectable({ @@ -75,13 +75,14 @@ export class DriveService { } async mountShare(url: string, path: string): Promise { - //console.log('waiting for peers') - //const firstPeer = await globals.drive.lookupPeers(url) - //console.log('first peer: ' + firstPeer) return globals.drive.mountShare(url, path) } async getFileUrl(path: string): Promise { return globals.drive.getFileUrl(path) } + + async addWriterToSpace(path: string, user: string): Promise { + return globals.drive.addWriterToSpace(path, user) + } } diff --git a/filemanager-ui/src/app/file-list/file-list.component.ts b/filemanager-ui/src/app/file-list/file-list.component.ts index 1cc0015..dcd19b9 100644 --- a/filemanager-ui/src/app/file-list/file-list.component.ts +++ b/filemanager-ui/src/app/file-list/file-list.component.ts @@ -5,6 +5,7 @@ import { MatDialog } from '@angular/material/dialog' import { ActivatedRoute, Router } from '@angular/router' import { DriveService } from '../drive.service' import { ShareDialogComponent } from '../share-dialog/share-dialog.component' +import { Space } from '../../../../src/EventInterfaces' export interface FileData { name: string, @@ -13,7 +14,8 @@ export interface FileData { icon?: string, size: string, lastChanged: string, - path: string + path: string, + space?: Space } @Component({ @@ -73,14 +75,23 @@ export class FileListComponent implements OnInit, AfterViewInit { files.map(r => { let name = window.decodeURIComponent(r.name) if(name.length > 64) name = name.substr(0, 32) + '...' + let icon = 'description' + if(r.stat?.isDirectory) { + if(r.space) { + icon = 'folder_shared' + } else { + icon = 'folder' + } + } return { name: name, path: r.path, isFile: r.stat?.isFile, - icon: r.stat?.isDirectory ? 'folder' : 'description', - link: r.stat?.isDirectory ? '/explorer/' + window.encodeURIComponent(r.path) : undefined, + icon: icon, + link: r.stat?.isDirectory ? '/explorer/' + window.encodeURIComponent(r.path) : undefined, // TODO: download link? size: formatSize(r.stat?.size), - lastChanged: toDate(r.stat?.mtime) + lastChanged: toDate(r.stat?.mtime), + space: r.space } }) }) diff --git a/filemanager-ui/src/app/share-dialog/share-dialog.component.css b/filemanager-ui/src/app/share-dialog/share-dialog.component.css index d279886..70f9ee8 100644 --- a/filemanager-ui/src/app/share-dialog/share-dialog.component.css +++ b/filemanager-ui/src/app/share-dialog/share-dialog.component.css @@ -39,4 +39,12 @@ table, .contacts-list{ mat-expansion-panel { padding-top: 0.5em; padding-bottom: 0.5em; +} + +.icon-green { + color: #a5ca65 +} + +.icon-red { + color: #5c3939 } \ No newline at end of file diff --git a/filemanager-ui/src/app/share-dialog/share-dialog.component.html b/filemanager-ui/src/app/share-dialog/share-dialog.component.html index bf739f3..795ed24 100644 --- a/filemanager-ui/src/app/share-dialog/share-dialog.component.html +++ b/filemanager-ui/src/app/share-dialog/share-dialog.component.html @@ -18,11 +18,17 @@

Share

Name {{user.username}} - - - + + Write Access + + + - + + no contacts selected @@ -41,7 +47,7 @@

Share

{{user.username}}
- + Share diff --git a/filemanager-ui/src/app/share-dialog/share-dialog.component.ts b/filemanager-ui/src/app/share-dialog/share-dialog.component.ts index 745e576..5cc0107 100644 --- a/filemanager-ui/src/app/share-dialog/share-dialog.component.ts +++ b/filemanager-ui/src/app/share-dialog/share-dialog.component.ts @@ -51,6 +51,10 @@ export class ShareDialogComponent implements OnInit { this.allContacts = allContacts } + isWriter(user: string) { + return this.fileData.space?.writers.includes(user) + } + onCopy() { const input = this.urlInput.nativeElement input.select() @@ -58,6 +62,16 @@ export class ShareDialogComponent implements OnInit { this.snackBarRef.open('Share URL copied to clipboard!', 'dismiss', {duration: 2000}) } + async toggleWriter(user: Contact) { + if(this.isWriter(user.publicUrl)) { + this.snackBarRef.open('Revoking write access is not implemented', 'dismiss', {duration: 2000}) + } else { + this.fileData.space = await this.drive.addWriterToSpace(this.fileData.path, user.publicUrl) + console.log('converted directory ' + this.fileData.path + ' to collaboration space') + this.sharedWithTable?.renderRows() + } + } + async onAdd(user: Contact) { await this.contacts.sendShare(user.publicUrl, this.url) this.allContacts.splice(this.allContacts.indexOf(user), 1) diff --git a/src/ContactsEventHandler.ts b/src/ContactsEventHandler.ts index cf0f102..620cdba 100644 --- a/src/ContactsEventHandler.ts +++ b/src/ContactsEventHandler.ts @@ -121,7 +121,7 @@ export default class ContactsEventHandler extends MainEventHandler implements IC const appRoot = await this.certacrypt.path('/apps/filemanager') const view = this.certacrypt.graph.factory.get(GRAPH_VIEW) const visited: IVertex[] = [] - const path = await traverse(view.query(Generator.from([new QueryState(appRoot, [], [])])), [], 0) + const path = await traverse(view.query(Generator.from([new QueryState(appRoot, [], [], view)])), [], 0) const drivePath = path ? path.join('/') : undefined return {shareUrl, drivePath, name: share.name, info: share.info, owner: share.owner, sharedBy: share.sharedBy, sharedWith: share.sharedWith} @@ -136,7 +136,7 @@ export default class ContactsEventHandler extends MainEventHandler implements IC const promises: Promise[] = [] for(let result of results) { if(visited.find(v => v.equals(result.vertex))) continue - const query = view.query(Generator.from([new QueryState(result.vertex, [], [])])).out(result.label) + const query = view.query(Generator.from([new QueryState(result.vertex, [], [], view)])).out(result.label) const promise = traverse(query, path.concat([result.label]), depth + 1) .then((found) => { visited.push(result.vertex) diff --git a/src/DriveEventHandler.ts b/src/DriveEventHandler.ts index 5f5ab9f..8f36dd1 100644 --- a/src/DriveEventHandler.ts +++ b/src/DriveEventHandler.ts @@ -1,22 +1,24 @@ -import { CertaCrypt, GraphObjects, Hyperdrive, createUrl, parseUrl, URL_TYPES, DriveShare } from "certacrypt"; +import { CertaCrypt, GraphObjects, CryptoHyperdrive, createUrl, parseUrl, URL_TYPES, DriveShare } from "certacrypt"; import { IpcMain, dialog } from "electron"; import fs from 'fs' import { MainEventHandler } from "./MainEventHandler"; -import { IDriveEventHandler, FileDownload , Stat, readdirResult, Peer, Share} from "./EventInterfaces"; +import { IDriveEventHandler, FileDownload , Stat, readdirResult, Peer, Share, Space} from "./EventInterfaces"; import unixify from 'unixify' import { GraphObject, Vertex } from "hyper-graphdb"; import Client from '@hyperspace/client' import { DriveGraphObject } from "certacrypt/lib/graphObjects"; +import { CollaborationSpace } from "certacrypt/lib/space"; export default class DriveEventHandler extends MainEventHandler implements IDriveEventHandler{ private downloads: Array = [] + private static readonly appPath = '/apps/filemanager' - constructor(app: IpcMain, private drive: Hyperdrive, private certacrypt: CertaCrypt, private hyperspace: Client) { + constructor(app: IpcMain, private drive: CryptoHyperdrive, private certacrypt: CertaCrypt, private hyperspace: Client) { super(app, 'drive') } static async init(app: IpcMain, certacrypt: CertaCrypt, hyperspace: Client): Promise { - let rootVertex = > await certacrypt.path('/apps/filemanager') + let rootVertex = > await certacrypt.path(this.appPath) const drive = await certacrypt.drive(rootVertex) const shareEdge = rootVertex.getEdges('shares') @@ -151,6 +153,25 @@ export default class DriveEventHandler extends MainEventHandler implements IDriv const filename = pathParts[pathParts.length-1] return this.certacrypt.getFileUrl(file, filename) } + + async convertToSpace(path: string): Promise<{space: CollaborationSpace,metadata: Space}> { + await this.certacrypt.convertToCollaborationSpace(DriveEventHandler.appPath + path) + const spaceMeta = await this.drive.getSpace(path) + if(!spaceMeta?.metadata) { + throw new Error('convertToSpace: conversion failed for ' + path) + } + return spaceMeta + } + async addWriterToSpace(path: string, userUrl: string) { + let fileSpace = await this.drive.getSpace(path) + if(!fileSpace?.space) { + fileSpace = await this.convertToSpace(path) + } + const user = await this.certacrypt.getUserByUrl(userUrl) + await fileSpace.space.addWriter(user) + fileSpace.metadata.writers.push(userUrl) + return fileSpace.metadata + } } function filenameFromPath(path: string) { diff --git a/src/EventInterfaces.ts b/src/EventInterfaces.ts index f239a86..36cd2a7 100644 --- a/src/EventInterfaces.ts +++ b/src/EventInterfaces.ts @@ -1,4 +1,4 @@ -import { ContactProfile, FriendState, GraphObjects, CommShare } from 'certacrypt' +import { ContactProfile, FriendState, GraphObjects } from 'certacrypt' import { GraphObject } from 'hyper-graphdb'; export type Fd = number @@ -37,11 +37,13 @@ export type Share = { drivePath: string } +export type Space = {space: string, owner: string, writers: string[], isWriteable: boolean} + export type Peer = {}; export type FileDownload = {filename: string, size: number, downloaded: number, error?: Error, localPath?: string} -export type readdirResult = { name: string, path: string, writers: string[], stat: Stat } +export type readdirResult = { name: string, path: string, space?: Space, stat: Stat } export interface ICertaCryptEventHandler { @@ -84,4 +86,6 @@ export interface IDriveEventHandler { lookupPeers(url: string): Promise getFileUrl(path: string): Promise + + addWriterToSpace(path: string, user: string): Promise } \ No newline at end of file