From 0e55476b51c53faaf488d3ebe7ae907bfa187bed Mon Sep 17 00:00:00 2001 From: Devendork Date: Fri, 4 Oct 2024 13:25:20 -0600 Subject: [PATCH 01/12] created skeleton of a sharing service that basically replicates a file, and stores a reference to it in a new database. Users can select to have it added to examples and this trigged an update of the upload class to contain images that are not indexed colors --- src/app/app.component.html | 7 + src/app/app.component.ts | 52 +++- src/app/core/core.module.ts | 4 +- .../imageeditor/imageeditor.component.scss | 1 - src/app/core/modal/share/share.component.html | 95 +++++++ src/app/core/modal/share/share.component.scss | 13 + .../core/modal/share/share.component.spec.ts | 23 ++ src/app/core/modal/share/share.component.ts | 245 ++++++++++++++++++ src/app/core/model/datatypes.ts | 27 ++ src/app/core/model/defaults.ts | 11 +- src/app/core/provider/file.service.ts | 14 +- src/app/core/provider/filesystem.service.ts | 148 ++++++++++- src/app/core/provider/media.service.ts | 71 ++++- src/app/core/provider/upload.service.ts | 1 - src/app/core/provider/workspace.service.ts | 67 ++--- .../ui/filebrowser/filebrowser.component.html | 73 +++--- .../ui/filebrowser/filebrowser.component.scss | 8 +- .../ui/filebrowser/filebrowser.component.ts | 20 +- .../upload-form/upload-form.component.ts | 12 +- .../parameter/parameter.component.html | 2 +- src/assets/img/by-nc-nd.png | Bin 0 -> 20935 bytes src/assets/img/by-nc-sa.png | Bin 0 -> 22475 bytes src/assets/img/by-nc.png | Bin 0 -> 17637 bytes src/assets/img/by-nd.png | Bin 0 -> 16192 bytes src/assets/img/by-sa.png | Bin 0 -> 17594 bytes src/assets/img/by.png | Bin 0 -> 12588 bytes src/assets/img/cc-zero.png | Bin 0 -> 6447 bytes 27 files changed, 787 insertions(+), 107 deletions(-) create mode 100644 src/app/core/modal/share/share.component.html create mode 100644 src/app/core/modal/share/share.component.scss create mode 100644 src/app/core/modal/share/share.component.spec.ts create mode 100644 src/app/core/modal/share/share.component.ts create mode 100644 src/assets/img/by-nc-nd.png create mode 100644 src/assets/img/by-nc-sa.png create mode 100644 src/assets/img/by-nc.png create mode 100644 src/assets/img/by-nd.png create mode 100644 src/assets/img/by-sa.png create mode 100644 src/assets/img/by.png create mode 100644 src/assets/img/cc-zero.png diff --git a/src/app/app.component.html b/src/app/app.component.html index b39894adb..dc2137777 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -290,6 +290,13 @@ color="primary"> + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b952eff82..08f2fe1ef 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -42,6 +42,7 @@ import { VersionService } from './core/provider/version.service'; import { MatSidenav } from '@angular/material/sidenav'; import { ViewadjustService } from './core/provider/viewadjust.service'; import { ViewadjustComponent } from './core/viewadjust/viewadjust.component'; +import { ShareComponent } from './core/modal/share/share.component'; @@ -386,6 +387,12 @@ export class AppComponent implements OnInit{ } + share(){ + const dialogRef = this.dialog.open(ShareComponent, { + width: '600px', + data: {fileid: this.files.getCurrentFileId()} + }); + } /** * this is called when a user pushes save from the topbar @@ -467,12 +474,17 @@ export class AppComponent implements OnInit{ return; } - if(user === null){ console.log("USER IS NULL") this.loadStarterFile(); }else{ + if(searchParams.has('share')){ + this.loadFromShare(searchParams.get('share')); + return; + } + + if(this.auth.isFirstSession() || (!this.auth.isFirstSession() && this.isBlankWorkspace())){ this.auth.getMostRecentFileIdFromUser(user).then(async fileid => { @@ -523,7 +535,6 @@ export class AppComponent implements OnInit{ } }else{ - console.log("load blank") this.loadBlankFile(); return; } @@ -621,9 +632,39 @@ export class AppComponent implements OnInit{ this.saveFile(); }); }) - } + + //must be online + async loadFromShare(shareid: string){ + console.log("LOAD FROM SHARE"); + + this.files.isShared(shareid).then(share_obj => { + + if(share_obj == null){ + console.log("THIS FILE IS NOT CURRENTLY SHARED"); + return Promise.reject("NO SHARED FILE EXISTS") + } + + var int_shareid: number = +shareid; + return Promise.all([this.files.getFile(int_shareid), share_obj, shareid]); + + + }).then(file_objs=>{ + console.log("GOT file OBJ", file_objs[0]) + return this.files.duplicate(this.auth.uid, file_objs[1].filename, file_objs[1].desc, file_objs[0]) + }).then(fileid => { + console.log("RETURNED FROM DUPLICATE WITH FILE ", fileid); + return Promise.all([this.files.getFile(fileid), this.files.getFileMeta(fileid), fileid]); + }).then(file_data => { + console.log("Loading File data ", file_data); + return this.prepAndLoadFile(file_data[1].filename, 'db', file_data[2], file_data[1].desc, file_data[0]) + }).catch(err => { + console.error(err); + }) + } + + //must be online async loadFromDB(fileid: number){ const ada = await this.files.getFile(fileid); @@ -1004,7 +1045,6 @@ async processFileData(data: FileObj) : Promise{ }) }else{ - console.log("LOADING OLDER VERSION ") data.ops.forEach(op => { const internal_op = this.ops.getOp(op.name); if(internal_op === undefined || internal_op == null|| internal_op.params === undefined) return; @@ -1023,9 +1063,7 @@ async processFileData(data: FileObj) : Promise{ } - - - + console.log("IMAGES TO LOAD ", images_to_load) return this.media.loadMedia(images_to_load).then(el => { //2. check the op names, if any op names are old, relink the newer version of that operation. If not match is found, replaces with Rect. return this.tree.replaceOutdatedOps(data.ops); diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 7b3bd6afb..26d11e1b0 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -57,6 +57,7 @@ import { ViewadjustComponent } from './viewadjust/viewadjust.component'; import { ImageeditorComponent } from './modal/imageeditor/imageeditor.component'; import { MediaService } from './provider/media.service'; import { OperationService } from './provider/operation.service'; +import { ShareComponent } from './modal/share/share.component'; @@ -112,7 +113,8 @@ import { OperationService } from './provider/operation.service'; EventsDirective, WelcomeComponent, SelectionComponent, - DraftRenderingComponent + DraftRenderingComponent, + ShareComponent ], providers: [ UploadService, diff --git a/src/app/core/modal/imageeditor/imageeditor.component.scss b/src/app/core/modal/imageeditor/imageeditor.component.scss index 47d98ef21..ae439711a 100644 --- a/src/app/core/modal/imageeditor/imageeditor.component.scss +++ b/src/app/core/modal/imageeditor/imageeditor.component.scss @@ -15,7 +15,6 @@ flex-direction: column; width: 66%; height: 100%; - margin-top: 12px; } .content_right{ diff --git a/src/app/core/modal/share/share.component.html b/src/app/core/modal/share/share.component.html new file mode 100644 index 000000000..f7be3c437 --- /dev/null +++ b/src/app/core/modal/share/share.component.html @@ -0,0 +1,95 @@ +
+

Publish this File to a Sharable Link

+ +
+ + + +

+ + Anyone can access this file with the link below + + + This file is not shared + + + + +

+ + + + + + +

Sharing Settings

+ + + License + + @for (license of licenses; track license) { + {{license.viewValue}} + } + + Need help picking? Visit Creative Commons + + + + Shared File Name + + + + + + File Description + + + + + Credit Line + + + + + URL to Additional Information + + + +
+ Include this in the examples section of AdaCAD +
+ + + + + + +
+ +

File History

+ +
+
{{cont.username}}
+
updated and shared this file on
+
{{cont.timestamp}}
+
+
+ +
+ + +

About Link Sharing: When you create a link, it is equivalent to the "save as" operation on most computers: a copy of this file, in this state, is created and can be opened by another user via the link. You will not see any edits they make to the file and they will not see any future edits you make to this file. If you want to publish edits you have made to this file, you must generate and share a new link. You can manage your shared files using the file manager

+ + +
+ + + + \ No newline at end of file diff --git a/src/app/core/modal/share/share.component.scss b/src/app/core/modal/share/share.component.scss new file mode 100644 index 000000000..2cc958341 --- /dev/null +++ b/src/app/core/modal/share/share.component.scss @@ -0,0 +1,13 @@ +.history_item{ + display: flex; + flex-direction: row; + gap: 11px; +} + +.about{ + font-size: small; +} + +.fullwidth{ + width: 100%; +} \ No newline at end of file diff --git a/src/app/core/modal/share/share.component.spec.ts b/src/app/core/modal/share/share.component.spec.ts new file mode 100644 index 000000000..44adc2f33 --- /dev/null +++ b/src/app/core/modal/share/share.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShareComponent } from './share.component'; + +describe('ShareComponent', () => { + let component: ShareComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ShareComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ShareComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/modal/share/share.component.ts b/src/app/core/modal/share/share.component.ts new file mode 100644 index 000000000..0a5e97969 --- /dev/null +++ b/src/app/core/modal/share/share.component.ts @@ -0,0 +1,245 @@ +import { Component, Inject, inject } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { AuthService } from '../../provider/auth.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { FilesystemService } from '../../provider/filesystem.service'; +import { share } from 'rxjs'; +import { AuthorContribution, IndexedColorImageInstance, MediaInstance, ShareObj } from '../../model/datatypes'; +import { WorkspaceService } from '../../provider/workspace.service'; +import { FileService } from '../../provider/file.service'; +import { defaults, licenses } from '../../model/defaults'; +import { MediaService } from '../../provider/media.service'; + +@Component({ + selector: 'app-share', + templateUrl: './share.component.html', + styleUrl: './share.component.scss' +}) +export class ShareComponent { + private _snackBar = inject(MatSnackBar); + + public shared_id: number = -1; + public share_obj: ShareObj; + public share_url: string; + + public licenses: Array = []; + public fileid: string; + + public author_list: Array = []; + + + constructor( + private auth: AuthService, + private fs: FilesystemService, + private file_serv: FileService, + private mediaService: MediaService, + private ws: WorkspaceService, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: any){ + + this.fileid = data.fileid; + this.licenses = licenses; + + // //check if a link has already been generated for this file. If yes, + // //include an update option. + this.fs.isShared(this.fileid.toString()).then(share_obj => { + + + if(share_obj == null){ + //this is not yet shared + this.share_obj = null; + + }else{ + this.share_obj = share_obj; + this.updateSettings(share_obj.license, share_obj.author_list); + this.share_url = "https://adacad-4-1.web.app/?share="+this.fileid; + } + }).catch(err => { + console.log("ENTRY NOT FOUND") + }); + + + } + + ngAfterViewInit(){ + + + } + + /** + * update the information on the page to match what is stored in the file system + * @param license + * @param author_list + */ + updateSettings(license: string, author_list: Array){ + this.author_list = author_list.slice(); + + } + + permissionChange(){ + //update in DB + // console.log("PERMISSION CHANGED ", this.selected_license) + // this.fs.updateSharedLicense(this.fileid, this.selected_license); + } + + toggleSharing(){ + + if(this.shared_id !== -1){ + this.removeLink(); + + }else{ + this.generateLink(); + } + + + } + + generateLink(){ + + + console.log("GENERATE LINK") + + this.file_serv.saver.ada() + .then(so => { + console.log("GOT SAVER OBJ", so) + //add the current time to the author list entry + this.author_list.push({ + uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', + username: (this.auth.isLoggedIn) ? this.auth.username : 'anonymous', + timestamp: Date.now() + }); + + + + return this.fs.duplicate(this.auth.uid, this.fs.getCurrentFileName(), this.fs.getCurrentFileDesc(), so.file) + }).then(new_id => { + console.log("DUPLICATED TO ", new_id) + + this.shared_id = new_id; + this.share_obj = { + license: 'by', + author_list: this.author_list, + filename: this.fs.getCurrentFileName(), + desc: this.fs.getCurrentFileDesc(), + owner_uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', + owner_creditline: (this.auth.isLoggedIn) ? 'created by '+this.auth.username : '', + public: false, + img: 'none' + + } + + return this.fs.createSharedFile(new_id.toString(), this.share_obj) + }).then(share_data => { + console.log("CREATED SHARED FILE ENTRY TO ", share_data) + + this.share_url = "https://adacad-4-1.web.app/?share="+this.shared_id; + }).catch(err => { + console.log("ERROR") + }) + + } + + // updateSharedFile(){ + // //does this share a new link + + // this.file_serv.saver.ada() + // .then(so => { + + // //add the current time to the author list entry + // this.author_list.push({ + // uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', + // username: (this.auth.isLoggedIn) ? this.auth.username : 'anonymous', + // timestamp: Date.now() + // }); + + // const share:ShareObj = { + // license: this.selected_license, + // author_list: this.author_list, + // ada: so.file, + // filename: this.fs.getCurrentFileName(), + // desc: this.fs.getCurrentFileDesc() + + // } + // this.fs.updateSharedFile(this.fileid, share) + + + + // }).catch(err => { + // console.log("ERROR") + // }) + + + + + + // } + + removeLink(){ + this.fs.removeSharedFile(this.fileid); + this.author_list = []; + } + + /** + * this is called by the upload services "On Data function" which uploads and analyzes the image data in the image and returns it as a image data object + * @param obj + */ + handleFile(obj: MediaInstance){ + console.log("GOT OBJ", obj[0]); + // this.share_obj.img = obj.ref; + + + if(obj === null || obj[0].data == null) return; + + + + const data = obj[0].data; + + const canvas: HTMLCanvasElement = document.getElementById('img_preview'); + const ctx = canvas.getContext('2d'); + + const max_dim = (data.width > data.height) ? data.width : data.height; + const use_width = (data.width > 400) ? data.width / max_dim * 400 : data.width; + const use_height = (data.height > 400) ? data.height / max_dim * 400 : data.height; + + canvas.width = use_width; + canvas.height = use_height; + + + + ctx.putImageData(data, 0, 0, 0, 0, use_width, use_height); + + + + + + + } + + + + updateLink(){ + //create a share option from the settings, have it return the id, post the id to the screen. + + } + + copyToClipboard(){ + navigator.clipboard.writeText(this.share_url).then( + ()=> { + this.openSnackBar('link copied', 'close')//on success + }, + () => { + //on fail + this.openSnackBar('could not copy link', 'close')//on success + + } + ) + + } + + openSnackBar(message: string, action: string) { + this._snackBar.open(message, action); + } + + +} diff --git a/src/app/core/model/datatypes.ts b/src/app/core/model/datatypes.ts index 637e93098..d3e7c7dce 100644 --- a/src/app/core/model/datatypes.ts +++ b/src/app/core/model/datatypes.ts @@ -381,6 +381,7 @@ export interface NodeComponentProxy{ export type MediaInstance ={ id: number; ref: string; + data: any; type: 'image' | 'indexed_color_image'; //currently we only support images } @@ -427,6 +428,7 @@ export interface FileObj{ } + export interface StatusMessage{ id: number, message: string, @@ -966,6 +968,31 @@ export type RenderingFlags = { } +/** + * File sharing + */ + + +export type AuthorContribution = { + uid: string, + username: string, + timestamp: number +} + +//consider if this should index on file id or share id. of if you even need both of them. +export type ShareObj = { + license: string, + owner_uid: string, + owner_creditline: string, + author_list: Array, + filename: string, + desc: string, + public:boolean, + img: string + +} + + diff --git a/src/app/core/model/defaults.ts b/src/app/core/model/defaults.ts index a98b19666..7b7d47ba0 100644 --- a/src/app/core/model/defaults.ts +++ b/src/app/core/model/defaults.ts @@ -139,4 +139,13 @@ export const paste_options = [ {value: 'copy', viewValue: 'Copy Selected Region', icon: "fa fa-clone",drawdown: true, threading: true, treadling: true, tieups: true, materials: true, systems: true}, {value: 'paste', viewValue: 'Paste Copied Pattern to Selected Region', icon: "fa fa-paste",drawdown: true, threading: true, treadling: true, tieups: true, materials: true, systems: true} ]; - \ No newline at end of file + + export const licenses = [ + {value: 'by', viewValue: 'CC BY', img: "by.png", desc:"This license enables reusers to distribute, remix, adapt, and build upon the material in any medium or format, so long as attribution is given to the creator. The license allows for commercial use."}, + {value: 'by-sa', viewValue: 'CC BY-SA', img: "by-sa.png", desc: "This license enables reusers to distribute, remix, adapt, and build upon the material in any medium or format, so long as attribution is given to the creator. The license allows for commercial use. If you remix, adapt, or build upon the material, you must license the modified material under identical terms."}, + {value: 'by-nc', viewValue: 'CC BY-NC', img: "by-nc.png", desc: "This license enables reusers to distribute, remix, adapt, and build upon the material in any medium or format for noncommercial purposes only, and only so long as attribution is given to the creator."}, + {value: 'by-nc-sa', viewValue: 'CC BY-NC-SA', img: "by-nc-sa.png", desc: "This license enables reusers to distribute, remix, adapt, and build upon the material in any medium or format for noncommercial purposes only, and only so long as attribution is given to the creator. If you remix, adapt, or build upon the material, you must license the modified material under identical terms."}, + {value: 'by-nd', viewValue: 'CC BY-ND', img: "by-nd.png", desc: "This license enables reusers to copy and distribute the material in any medium or format in unadapted form only, and only so long as attribution is given to the creator. The license allows for commercial use."}, + {value: 'by-nc-nd', viewValue: 'CC BY-NC-ND', img: "by-nc-nd.png", desc: "This license enables reusers to copy and distribute the material in any medium or format in unadapted form only, for noncommercial purposes only, and only so long as attribution is given to the creator. "}, + {value: 'cc-zero', viewValue: 'CC0', img: "cc-zero.png", desc: "CC0 (aka CC Zero) is a public dedication tool, which enables creators to give up their copyright and put their works into the worldwide public domain. CC0 enables reusers to distribute, remix, adapt, and build upon the material in any medium or format, with no conditions."}, + ] \ No newline at end of file diff --git a/src/app/core/provider/file.service.ts b/src/app/core/provider/file.service.ts index 5f5f5076f..b21034f49 100644 --- a/src/app/core/provider/file.service.ts +++ b/src/app/core/provider/file.service.ts @@ -318,6 +318,12 @@ export class FileService { }); } }) + + + let indexed_images = []; + if(data.indexed_image_data !== undefined){ + indexed_images = data.indexed_image_data; + } if(data.ops !== undefined){ @@ -332,10 +338,7 @@ export class FileService { }); } - let indexed_images = []; - if(data.indexed_image_data !== undefined){ - indexed_images = data.indexed_image_data; - } + const envt: FileObj = { @@ -488,6 +491,7 @@ export class FileService { return this.tree.exportDraftNodeProxiesForSaving().then(draft_nodes => { + console.log("EXPORTED NODES ", draft_nodes) const out: SaveObj = { version: this.vs.currentVersion(), @@ -502,7 +506,7 @@ export class FileService { materials: this.ms.exportForSaving(), indexed_image_data: this.media.exportIndexedColorImageData() } - + console.log("CREATED OUTPUT ", out) var theJSON = JSON.stringify(out); return Promise.resolve({json: theJSON, file: out}); }) diff --git a/src/app/core/provider/filesystem.service.ts b/src/app/core/provider/filesystem.service.ts index f745b11b5..db348d749 100644 --- a/src/app/core/provider/filesystem.service.ts +++ b/src/app/core/provider/filesystem.service.ts @@ -1,10 +1,10 @@ import { Injectable, Optional } from '@angular/core'; import { Auth, authState, getAuth } from '@angular/fire/auth'; -import { get as fbget, getDatabase, onChildAdded, onChildRemoved, onDisconnect, onValue, orderByChild, update, ref as fbref, ref, remove, query, onChildChanged } from '@angular/fire/database'; +import { get as fbget, getDatabase, onChildAdded, onChildRemoved, onDisconnect, onValue, orderByChild, update, ref as fbref, ref, remove, query, onChildChanged, set } from '@angular/fire/database'; // import { onChildAdded, onChildChanged, onChildRemoved, onDisconnect, onValue, orderByChild, update } from 'firebase/database'; import { Observable, Subject } from 'rxjs'; import { FilebrowserComponent } from '../ui/filebrowser/filebrowser.component'; -import { LoadedFile, SaveObj } from '../model/datatypes'; +import { LoadedFile, SaveObj, ShareObj } from '../model/datatypes'; import utilInstance from '../model/util'; @@ -103,6 +103,8 @@ export class FilesystemService { }); } + + public getLoadedFile(id: number) : LoadedFile{ let item = this.loaded_files.find(el => el.id == id); @@ -341,20 +343,138 @@ export class FilesystemService { } - /** - * takes the current state, gives it a new file ID and pushes it to the database - * @returns the id of the file - */ - duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ +/** + * takes the current state, gives it a new file ID and pushes it to the database + * @returns the id of the file + */ +duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ + console.log("WRITING NAME ", name) + + const fileid = this.generateFileId(); + this.writeFileData(fileid, ada); + this.writeNewFileMetaData(uid, fileid, name, desc) + return Promise.resolve(fileid); - const fileid = this.generateFileId(); - this.writeFileData(fileid, ada); - this.writeNewFileMetaData(uid, fileid, name, desc) - return Promise.resolve(fileid); - - } + } +/** + * creates a new reference for a shared file + * @param file_id + * @param share_data + * @returns + */ +createSharedFile(file_id: string, share_data: ShareObj) : Promise { + + + if(!this.connected) return; + + const db = getDatabase(); + const ref = fbref(db, 'shared/'+file_id); + + set(ref,share_data) + .then(success => { + console.log("SUCCESS"); + return Promise.resolve(file_id) + + }) + .catch(err => { + console.error(err); + return Promise.reject("could not create new shared item") + + }) + + +} + +/** + * checks if and how a particular file id is being shared + * @param file_id + */ +isShared(file_id:string) : Promise { + const db = getDatabase(); + + return fbget(fbref(db, `shared/${file_id}`)) + .then((filedata) => { + + + if(filedata.exists()){ + return Promise.resolve({ + ada: filedata.val().ada, + author_list: filedata.val().author_list, + license: filedata.val().license, + filename: filedata.val().filename, + desc: filedata.val().desc}); + + }else{ + return Promise.resolve(null) + } + + }) +} + + + +/** + * called when a user changes the license for a shared file. + * @param fileid + * @param license + * @returns + */ +updateSharedFile(fileid: string, share: ShareObj) : Promise{ + if(!this.connected) return Promise.reject("not logged in"); + + const db = getDatabase(); + const ref = fbref(db, 'shared/'+fileid); + + update(ref,share) + .then(success => { + return Promise.resolve(true); + }) + .catch(err => { + console.error(err); + return Promise.resolve(false); + }) + +} + + +/** + * called when a user changes the license for a shared file. + * @param fileid + * @param license + * @returns + */ +updateSharedLicense(fileid: string, license: string) : Promise{ + if(!this.connected) return Promise.reject("not logged in"); + + const db = getDatabase(); + const ref = fbref(db, 'shared/'+fileid); + + update(ref,{license: license}) + .then(success => { + return Promise.resolve(true); + }) + .catch(err => { + console.error(err); + return Promise.resolve(false); + }) + +} + +/** + * gets the file at a given id + * @returns the file data + */ +removeSharedFile(file_id: string) : Promise { + if(!this.connected) return Promise.reject("get shared file is not logged in"); + + const db = getDatabase(); + remove(fbref(db, `shared/${file_id}`)); + + + +} /** @@ -441,6 +561,8 @@ getFile(fileid: number) : Promise { } + + /** * writes the data for the currently open file to the database * @param cur_state diff --git a/src/app/core/provider/media.service.ts b/src/app/core/provider/media.service.ts index 4ee7ba89d..40ae421ea 100644 --- a/src/app/core/provider/media.service.ts +++ b/src/app/core/provider/media.service.ts @@ -30,10 +30,13 @@ export class MediaService { * @param to_load a list of ids and associated data * @returns */ - loadMedia(to_load: Array<{id: number, ref: string, data:any}>) : Promise { + loadMedia(to_load: Array) : Promise { const fns = to_load .filter(el => el.ref !== '') - .map(el => this.loadIndexedColorFile(el.id, el.ref, el.data)); + .map(el => { + if(el.type == 'indexed_color_image') return this.loadIndexedColorFile(el.id, el.ref, el.data) + else return this.loadImage(el.id, el.ref, el.data) + }); return Promise.all(fns); } @@ -196,6 +199,53 @@ export class MediaService { }); } + /** + * loads an indexed color file + * @param id the unique reference for this file + * @param data an object containing any color or color_mapping data that has already been stored for this item + * @returns + */ + loadImage(id: number, ref: string, data: any) : Promise{ + + if(id == -1){ + id = utilInstance.generateId(8); + } + + let url = ""; + + //retrieve the media object from the server + return this.upSvc.getDownloadData(ref).then(obj =>{ + if(obj === undefined) return null; + url = obj; + return this.processMedia(obj); + + }).then(blob => { + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + var image = new Image(); + image.src = url; + image.crossOrigin = "Anonymous"; + + return image.decode().then(() => { + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + + ctx.drawImage(image, 0, 0, canvas.width, canvas.height); + var imgdata = ctx.getImageData(0,0, canvas.width, canvas.height); + + return Promise.resolve(imgdata); + }).then(imageobj => { + + if(imageobj == null){ + return Promise.reject("no image object found when loading single image file"); + } + const media_ref = this.addSingleImageMediaInstance(id, ref, imageobj) + return Promise.resolve(media_ref); + }) ; + }); + } + addMediaNameFromStorage(ref: string, img: AnalyzedImage) : Promise{ @@ -275,10 +325,21 @@ export class MediaService { */ addIndexColorMediaInstance(id:number, ref: string, img: AnalyzedImage) : IndexedColorImageInstance{ - let obj: IndexedColorImageInstance = {id, ref,type: 'indexed_color_image', img} + let obj: IndexedColorImageInstance = {id, ref,type: 'indexed_color_image', data: null, img} this.current.push(obj); - console.log("INSTANCE ADDED ", this.current) + return obj; + } + + /** + * loads a media instance into the table + * @param id - a unique id for this media instance + * @param ref - the reference to media in Firebase storage + * @param img - specific settings for this media instance + */ + addSingleImageMediaInstance(id:number, ref: string, data: any) : MediaInstance{ + let obj: MediaInstance = {id, ref,type: 'image', data} + this.current.push(obj); return obj; } @@ -330,9 +391,7 @@ export class MediaService { } removeInstance(id: number){ - console.log("REMOVING ", id, " FROM ",this.current) this.current = this.current.filter(el => el.id !== id); - console.log("INSTANCES STORED ", this.current) } exportIndexedColorImageData(): Array{ diff --git a/src/app/core/provider/upload.service.ts b/src/app/core/provider/upload.service.ts index e9d88f427..1b38aad62 100644 --- a/src/app/core/provider/upload.service.ts +++ b/src/app/core/provider/upload.service.ts @@ -51,7 +51,6 @@ export class UploadService { new Uint8Array(data) .reduce((data, byte) => data + String.fromCharCode(byte), '') ); - console.log("RESOLVING WITH ", base64) resolve(base64); } ); diff --git a/src/app/core/provider/workspace.service.ts b/src/app/core/provider/workspace.service.ts index ad3ee3d75..b0d64177f 100644 --- a/src/app/core/provider/workspace.service.ts +++ b/src/app/core/provider/workspace.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; -import { LoomSettings } from '../model/datatypes'; +import { AuthorContribution, LoomSettings } from '../model/datatypes'; import { defaults } from '../model/defaults'; import utilInstance from '../model/util'; +import { defaultMaxListeners } from 'events'; @Injectable({ providedIn: 'root' @@ -15,6 +16,7 @@ export class WorkspaceService { file_favorites: Array = []; + loaded_from_share_id: number = -1; min_frames: number = defaults.loom_settings.frames; min_treadles: number = defaults.loom_settings.treadles; type: string = defaults.loom_settings.type; //'rigid', 'direct', 'frame', 'jacquard' @@ -48,6 +50,7 @@ export class WorkspaceService { this.selected_origin_option = defaults.selected_origin_option; this.hide_mixer_drafts = defaults.hide_mixer_drafts; this.show_advanced_operations = defaults.show_advanced_operations; + this.loaded_from_share_id = -1; } @@ -64,7 +67,7 @@ export class WorkspaceService { this.file_favorites = (data.file_favorites === undefined) ? [] : data.file_favorites; this.hide_mixer_drafts = (data.hide_mixer_drafts === undefined) ? true : data.hide_mixer_drafts; this.show_advanced_operations = (data.show_advanced_operations === undefined) ? false : data.show_advanced_operations; - + this.loaded_from_share_id = (data.loaded_from_share_id === undefined) ? [] : data.loaded_from_share_id; } @@ -73,38 +76,42 @@ export class WorkspaceService { return false; } + // addAuthor(author_id: string){ + // this.authors.push(author_id); + // } + /** * given an array of looms, infers the data from what is most commonly used * this assumes that most exports will have common loom data * @param looms */ - async inferData(loom_settings: Array) : Promise { - if(loom_settings.length === 0) return Promise.resolve("no looms"); - - //filter out null or undefined looms - loom_settings = loom_settings.filter(el => !(el === undefined || el === null)); - - - this.min_frames = utilInstance.getMostCommon( - loom_settings.map(el => el.frames) - ); - this.min_treadles = utilInstance.getMostCommon( - loom_settings.map(el => el.treadles) - ); - this.type = utilInstance.getMostCommon( - loom_settings.map(el => el.type) - ); - this.units = utilInstance.getMostCommon( - loom_settings.map(el => el.units) - ); - - this.epi = utilInstance.getMostCommon( - loom_settings.map(el => el.epi) - ); - - return "done"; - } + // async inferData(loom_settings: Array) : Promise { + // if(loom_settings.length === 0) return Promise.resolve("no looms"); + + // //filter out null or undefined looms + // loom_settings = loom_settings.filter(el => !(el === undefined || el === null)); + + + // this.min_frames = utilInstance.getMostCommon( + // loom_settings.map(el => el.frames) + // ); + // this.min_treadles = utilInstance.getMostCommon( + // loom_settings.map(el => el.treadles) + // ); + // this.type = utilInstance.getMostCommon( + // loom_settings.map(el => el.type) + // ); + // this.units = utilInstance.getMostCommon( + // loom_settings.map(el => el.units) + // ); + + // this.epi = utilInstance.getMostCommon( + // loom_settings.map(el => el.epi) + // ); + + // return "done"; + // } exportWorkspace() : any{ return { @@ -119,8 +126,8 @@ export class WorkspaceService { selected_origin_option: this.selected_origin_option, file_favorites: this.file_favorites.slice(), hide_mixer_drafts: this.hide_mixer_drafts, - show_advanced_operations: this.show_advanced_operations - + show_advanced_operations: this.show_advanced_operations, + loaded_from_share_id: this.loaded_from_share_id } } diff --git a/src/app/core/ui/filebrowser/filebrowser.component.html b/src/app/core/ui/filebrowser/filebrowser.component.html index 152b8876c..83c86bc41 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.html +++ b/src/app/core/ui/filebrowser/filebrowser.component.html @@ -188,54 +188,67 @@

Files Saved on AdaCAD Server

- + + + + + + + - + - - - + - - + + + + + + + + + + + + + + + diff --git a/src/app/core/modal/share/share.component.ts b/src/app/core/modal/share/share.component.ts index 8ee140d1e..23a05a8a1 100644 --- a/src/app/core/modal/share/share.component.ts +++ b/src/app/core/modal/share/share.component.ts @@ -19,7 +19,7 @@ import { MediaService } from '../../provider/media.service'; export class ShareComponent { private _snackBar = inject(MatSnackBar); - public shared_id: number = -1; + public shared_id: string = ''; public share_obj: ShareObj; public share_url: string; @@ -48,7 +48,7 @@ export class ShareComponent { // //check if a link has already been generated for this file. If yes, // //include an update option. this.fs.isShared(this.fileid.toString()).then(share_obj => { - + console.log("CHECK IS SHARE RETURNED ", share_obj) if(share_obj == null){ //this is not yet shared @@ -56,6 +56,7 @@ export class ShareComponent { }else{ this.share_obj = share_obj; + this.shared_id = this.fileid.toString(); this.updateSettings(share_obj.license, share_obj.author_list); this.share_url = "https://adacad-4-1.web.app/?share="+this.fileid; } @@ -86,15 +87,9 @@ export class ShareComponent { this.fs.updateSharedFile(this.shared_id.toString(), this.share_obj) } - permissionChange(){ - //update in DB - // console.log("PERMISSION CHANGED ", this.selected_license) - // this.fs.updateSharedLicense(this.fileid, this.selected_license); - } - toggleSharing(){ - if(this.shared_id !== -1){ + if(this.shared_id !== ''){ this.removeLink(); }else{ @@ -109,8 +104,12 @@ export class ShareComponent { console.log("GENERATE LINK") - this.file_serv.saver.ada() - .then(so => { + let int_id: number = +this.fileid; + + this.fs.getFileMeta(int_id).then(meta => { + return Promise.all([this.file_serv.saver.ada(), meta]) + + }).then(so => { console.log("GOT SAVER OBJ", so) //add the current time to the author list entry this.author_list.push({ @@ -119,17 +118,15 @@ export class ShareComponent { timestamp: Date.now() }); - - - return this.fs.duplicate(this.auth.uid, this.fs.getCurrentFileName(), this.fs.getCurrentFileDesc(), so.file) - }).then(new_id => { - - this.shared_id = new_id; + return Promise.all([this.fs.duplicate(this.auth.uid, so[1].name,so[1].desc, so[0].file), so[1]]) + }).then(id_and_meta => { + console.log("ID AND META ", id_and_meta) + this.shared_id = id_and_meta[0].toString(); this.share_obj = { license: 'by', author_list: this.author_list, - filename: this.fs.getCurrentFileName(), - desc: this.fs.getCurrentFileDesc(), + filename: id_and_meta[1].name, + desc: id_and_meta[1].desc, owner_uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', owner_creditline: (this.auth.isLoggedIn) ? 'created by '+this.auth.username : '', public: false, @@ -137,7 +134,7 @@ export class ShareComponent { } - return this.fs.createSharedFile(new_id.toString(), this.share_obj) + return this.fs.createSharedFile(this.shared_id , this.share_obj) }).then(share_data => { console.log("CREATED SHARED FILE ENTRY TO ", share_data) @@ -188,6 +185,11 @@ export class ShareComponent { this.author_list = []; } + formatDate(date: number){ + var dateFormat = new Date(date); + return dateFormat.toLocaleTimeString(); + } + /** * this is called by the upload services "On Data function" which uploads and analyzes the image data in the image and returns it as a image data object * @param obj diff --git a/src/app/core/model/datatypes.ts b/src/app/core/model/datatypes.ts index d3e7c7dce..e65a245e2 100644 --- a/src/app/core/model/datatypes.ts +++ b/src/app/core/model/datatypes.ts @@ -12,17 +12,6 @@ import { MaterialsService } from "../provider/materials.service"; /*** APPLICATION STATE MANAGEMENT */ -/** - * a local instance of a file that is currently open within the users's workspace - */ -export interface LoadedFile{ - id: number, - name: string, - desc: string, - ada: SaveObj, - last_saved_time: number -} - /***** OBJECTS/TYPES RELATED TO DRAFTS *******/ diff --git a/src/app/core/provider/filesystem.service.ts b/src/app/core/provider/filesystem.service.ts index db348d749..5b743c915 100644 --- a/src/app/core/provider/filesystem.service.ts +++ b/src/app/core/provider/filesystem.service.ts @@ -4,7 +4,7 @@ import { get as fbget, getDatabase, onChildAdded, onChildRemoved, onDisconnect, // import { onChildAdded, onChildChanged, onChildRemoved, onDisconnect, onValue, orderByChild, update } from 'firebase/database'; import { Observable, Subject } from 'rxjs'; import { FilebrowserComponent } from '../ui/filebrowser/filebrowser.component'; -import { LoadedFile, SaveObj, ShareObj } from '../model/datatypes'; +import { SaveObj, ShareObj } from '../model/datatypes'; import utilInstance from '../model/util'; @@ -16,15 +16,13 @@ export class FilesystemService { file_tree_change$ = new Subject(); file_saved_change$ = new Subject(); - loaded_file_change$ = new Subject(); + shared_file_change$ = new Subject(); file_tree: Array = []; - loaded_files: Array; - - private current_file_id: number = -1; public current_file_name: string = ''; + public current_file_desc: string = ''; connected: boolean = false; @@ -34,8 +32,6 @@ export class FilesystemService { constructor(@Optional() private auth: Auth) { - this.loaded_files = []; - const db = getDatabase(); const presenceRef = ref(db, "disconnectmessage"); @@ -65,16 +61,29 @@ export class FilesystemService { const userFiles = query(ref(db, 'users/'+user.uid+'/files'), orderByChild('timestamp')); - + const sharedFiles = query(ref(db, 'shared')); //called once per item, then on subsequent changes onChildAdded(userFiles, (childsnapshot) => { - //console.log("CHILD ADDED ", userFiles) + console.log("ON CHILD ADDED") //only add values that haven't already been added if(this.file_tree.find(el => el.id === parseInt(childsnapshot.key)) === undefined){ - this.addToTree(parseInt(childsnapshot.key), childsnapshot.val()); - this.file_tree_change$.next(this.file_tree.slice()); + this.isShared(childsnapshot.key).then(res => { + this.addToTree(parseInt(childsnapshot.key), childsnapshot.val(), res); + this.file_tree_change$.next(this.file_tree.slice()); + + }) + + + } + }); + + onChildAdded(sharedFiles, (childsnapshot) => { + console.log("ON SHARE ADDED") + //only add values that haven't already been added + if(this.file_tree.find(el => el.id === parseInt(childsnapshot.key)) !== undefined){ + this.shared_file_change$.next(this.file_tree.slice()); } }); @@ -82,41 +91,55 @@ export class FilesystemService { //called when anything in meta changes onChildChanged(userFiles, (data) => { - // console.log("CHILD CHANGED ", userFiles) + console.log("CHILD CHANGED ", userFiles) const ndx = this.file_tree.findIndex(el => parseInt(el.id) === parseInt(data.key)); if(ndx !== -1){ - this.file_tree[ndx].meta.name = data.val().name; - this.file_tree_change$.next(this.file_tree.slice()); + this.isShared(data.key).then(res => { + this.file_tree[ndx].meta.name = data.val().name; + this.file_tree[ndx].shared = res; + this.file_tree_change$.next(this.file_tree.slice()); + }) + + } + }); + + onChildChanged(sharedFiles, (data) => { + const ndx = this.file_tree.findIndex(el => parseInt(el.id) === parseInt(data.key)); + if(ndx !== -1){ + this.isShared(data.key).then(res => { + this.file_tree[ndx].shared = res; + this.shared_file_change$.next(this.file_tree.slice()); + }) + } }); //needs to redraw the files list onChildRemoved(userFiles, (removedItem) => { - //console.log("CHILD REMOVED ", userFiles) - const removedId = removedItem.key; this.file_tree = this.file_tree.filter(el => parseInt(el.id) !== parseInt(removedId)); this.file_tree_change$.next(this.file_tree.slice()); }); - - - }); - } - + //needs to redraw the files list + onChildRemoved(sharedFiles, (removedItem) => { - public getLoadedFile(id: number) : LoadedFile{ + const removedId = removedItem.key; + const ndx = this.file_tree.findIndex(el => parseInt(el.id) === parseInt(removedId)); + if(ndx !== -1){ + this.file_tree[ndx].shared = null; + this.shared_file_change$.next(this.file_tree.slice()); + } + }); + + + + + }); - let item = this.loaded_files.find(el => el.id == id); - if(item == undefined){ - return null; - }else{ - return item - } + } - - /** * given a new file that has just been loaded, update the meta-data to match the value of this item. * @param id @@ -125,35 +148,31 @@ export class FilesystemService { */ public pushToLoadedFilesAndFocus(id: number, name: string, desc: string) : Promise{ - - // let item = this.getLoadedFile(id); - - // if(item === null){ return this.getFileMeta(id) .then(res => { - this.loaded_files.push({ - id: id, - name: res.name, - desc: res.desc, - ada: undefined, - last_saved_time: 0 - }); + // this.loaded_files.push({ + // id: id, + // name: res.name, + // desc: res.desc, + // ada: undefined, + // last_saved_time: 0 + // }); this.setCurrentFileInfo(id, res.name, res.desc); - this.loaded_file_change$.next(this.file_tree.slice()); + // this.loaded_file_change$.next(this.file_tree.slice()); return Promise.resolve(true); }) .catch(nodata => { - this.loaded_files.push({ - id: id, - name:name, - desc: desc, - ada: undefined, - last_saved_time: 0 - }) + // this.loaded_files.push({ + // id: id, + // name:name, + // desc: desc, + // ada: undefined, + // last_saved_time: 0 + // }) this.setCurrentFileInfo(id, name, desc); - this.loaded_file_change$.next(this.file_tree.slice()); + // this.loaded_file_change$.next(this.file_tree.slice()); return Promise.resolve(false); }); @@ -163,12 +182,12 @@ export class FilesystemService { // } } - public unloadFile(id: number){ - this.loaded_files = this.loaded_files.filter(el => el.id !== id); - if(this.current_file_id == id) this.current_file_id = -1; - this.loaded_file_change$.next(this.file_tree.slice()); + // public unloadFile(id: number){ + // this.loaded_files = this.loaded_files.filter(el => el.id !== id); + // if(this.current_file_id == id) this.current_file_id = -1; + // this.loaded_file_change$.next(this.file_tree.slice()); - } + // } public setCurrentFileId(id: number){ this.current_file_id = id; @@ -179,21 +198,11 @@ export class FilesystemService { } public getCurrentFileName() : string{ - let item = this.getLoadedFile(this.current_file_id); - if(item !== null) return item.name; - return ''; + return this.current_file_name; } public getCurrentFileDesc() : string{ - let item = this.getLoadedFile(this.current_file_id); - if(item !== null) return item.desc; - return null; - } - - public getCurrentFileObj() : SaveObj{ - let item = this.getLoadedFile(this.current_file_id); - if(item !== null) return item.ada; - return null; + return this.current_file_desc; } @@ -215,14 +224,15 @@ export class FilesystemService { /** * adds to the local tree for the UI */ - addToTree(fileid: number, meta: any){ + addToTree(fileid: number, meta: any, shared: ShareObj){ var dateFormat = new Date(meta.timestamp); meta.date = dateFormat.toLocaleDateString(); this.file_tree.unshift({ id: fileid, - meta: meta + meta: meta, + shared: shared }) } @@ -242,20 +252,6 @@ export class FilesystemService { this.setCurrentFileId(fileid); this.current_file_name = name; - - const item = this.getLoadedFile(this.current_file_id); - if(item == null){ - console.log("CANNOT FIND ITEM in LOADED FILES IN SET DCURRENT FILE INFO") - }else{ - item.name = name; - item.desc = desc; - } - - // item.current_file_id = fileid; - // this.current_file_name = name; - // this.current_file_desc = desc; - - const stamp = Date.now(); if(!this.connected) return; @@ -281,14 +277,6 @@ export class FilesystemService { if(newname === null || newname === undefined) newname = 'no name'; if(fileid == this.getCurrentFileId()) this.current_file_name = newname; - const item = this.getLoadedFile(fileid); - - if(item == null){ - //this file is not currently loaded but we can still rename it - }else{ - //if it is cureently loaded, get it here too - item.name = newname; - } if(!this.connected) return; @@ -306,13 +294,7 @@ export class FilesystemService { if(fileid === null || fileid == undefined) return; if(desc === null || desc === undefined) desc = ''; - const item = this.getLoadedFile(fileid); - if(item == null){ - //this file is not currently loaded - }else{ - item.desc = desc; - } - + if(!this.connected) return; const auth = getAuth(); @@ -348,7 +330,7 @@ export class FilesystemService { * @returns the id of the file */ duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ - console.log("WRITING NAME ", name) + console.log("DUPLICATING") const fileid = this.generateFileId(); this.writeFileData(fileid, ada); @@ -365,7 +347,7 @@ duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ * @returns */ createSharedFile(file_id: string, share_data: ShareObj) : Promise { - + console.log("CREATING SHARED FILE ", file_id, share_data) if(!this.connected) return; @@ -463,16 +445,30 @@ updateSharedLicense(fileid: string, license: string) : Promise{ } /** - * gets the file at a given id + * The shared entry is not the same as the file. This operation removes the entry to this file in the shared DB but the file that was shared still exists in the files DB. This will automatically rename that file to reflect that it used to be shared. * @returns the file data */ removeSharedFile(file_id: string) : Promise { if(!this.connected) return Promise.reject("get shared file is not logged in"); + + let int_id: number = +file_id; const db = getDatabase(); + const auth = getAuth(); + const user = auth.currentUser; remove(fbref(db, `shared/${file_id}`)); + this.getFileMeta(int_id).then(meta => { + console.log("META GOT ", meta) + if(user){ + console.log("UPDATED ", meta) + + update(fbref(db, 'users/'+user.uid+'/files/'+file_id),{name: meta.name + "(previously shared)"}); + } + }) + + } @@ -511,9 +507,7 @@ getFile(fileid: number) : Promise { * @returns */ removeFile(fileid: number) { - - this.unloadFile(fileid); - + if(!this.connected) return; const db = getDatabase(); @@ -552,13 +546,14 @@ getFile(fileid: number) : Promise { - updateCurrentStateInLoadedFiles(fileid: number, cur_state: SaveObj){ - const item = this.getLoadedFile(fileid); - - if(item !== null){ - item.ada = cur_state; - } - + updateSaveTime(fileid: number){ + if(!this.connected) return; + const auth = getAuth(); + const user = auth.currentUser; + const stamp = Date.now(); + const db = getDatabase(); + update(fbref(db, 'users/'+user.uid+'/files/'+fileid),{ + timestamp: stamp}); } @@ -569,6 +564,7 @@ getFile(fileid: number) : Promise { * @returns */ writeFileData(fileid: number, cur_state: SaveObj) { + console.log("ATTEMPTING TO WRITE FILE DATA ", cur_state, fileid) if(!this.connected) return; @@ -577,13 +573,7 @@ getFile(fileid: number) : Promise { update(ref,{ada: cur_state}) .then(success => { - const item = this.getLoadedFile(fileid); - if(item !== null){ - item.last_saved_time = Date.now(); - }else{ - console.error("could not get loaded file after save") - } - + this.updateSaveTime(fileid) }) .catch(err => { console.error(err); @@ -593,13 +583,8 @@ getFile(fileid: number) : Promise { writeNewFileMetaData(uid: string, fileid: number, name: string, desc: string) { - const item = this.getLoadedFile(fileid); - if(item !== null){ - item.name = name, - item.desc = desc - } - if(!this.connected) return; + const stamp = Date.now(); const db = getDatabase(); update(fbref(db, 'users/'+uid+'/files/'+fileid),{ diff --git a/src/app/core/provider/media.service.ts b/src/app/core/provider/media.service.ts index 40ae421ea..e58c98287 100644 --- a/src/app/core/provider/media.service.ts +++ b/src/app/core/provider/media.service.ts @@ -206,6 +206,7 @@ export class MediaService { * @returns */ loadImage(id: number, ref: string, data: any) : Promise{ + console.log("LOADING IMAGE") if(id == -1){ id = utilInstance.generateId(8); diff --git a/src/app/core/provider/state.service.ts b/src/app/core/provider/state.service.ts index 87bfad73e..bf6cb7c78 100644 --- a/src/app/core/provider/state.service.ts +++ b/src/app/core/provider/state.service.ts @@ -67,8 +67,6 @@ export class StateService { */ public addMixerHistoryState(ada:{json: string, file: SaveObj}){ - this.files.updateCurrentStateInLoadedFiles(this.files.getCurrentFileId(), ada.file); - var state = { draft: null, ada: { diff --git a/src/app/core/ui/filebrowser/filebrowser.component.html b/src/app/core/ui/filebrowser/filebrowser.component.html index 83c86bc41..88aeb927e 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.html +++ b/src/app/core/ui/filebrowser/filebrowser.component.html @@ -34,106 +34,122 @@ - - +
- + -
+ + + -
{{formatDate(file.last_saved_time)}}
+ -
last saved at: {{last_saved_time}}
--> + + - + -

Files Saved on AdaCAD Server

+ +
+ +

All Files Saved on AdaCAD Server

@@ -175,7 +191,7 @@

Files Saved on AdaCAD Server

- {{file.meta.name}} + {{file.meta.name}} {{file.id}} @@ -288,7 +304,7 @@

Files Saved on AdaCAD Server

{{folder.updated | date}}
--> - + diff --git a/src/app/core/ui/filebrowser/filebrowser.component.scss b/src/app/core/ui/filebrowser/filebrowser.component.scss index fab813057..06bce3465 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.scss +++ b/src/app/core/ui/filebrowser/filebrowser.component.scss @@ -13,14 +13,14 @@ h3{ display: flex; flex-direction: column; gap: 10px; - width: max-content; + width: 100%; } .browser_list{ display: flex; flex-direction: column; - overflow-y: scroll; + // overflow-y: scroll; gap: 10px; } @@ -48,6 +48,7 @@ h3{ flex-direction: row; justify-content: space-evenly; width: 100%; + border-bottom: thin dotted; align-items: center; } diff --git a/src/app/core/ui/filebrowser/filebrowser.component.ts b/src/app/core/ui/filebrowser/filebrowser.component.ts index a218897a8..145866d3f 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.ts +++ b/src/app/core/ui/filebrowser/filebrowser.component.ts @@ -6,6 +6,7 @@ import { WorkspaceService } from '../../provider/workspace.service'; import { FileService } from '../../provider/file.service'; import { LoginComponent } from '../../modal/login/login.component'; import { ShareComponent } from '../../modal/share/share.component'; +import { share } from 'rxjs'; @Component({ selector: 'app-filebrowser', @@ -23,6 +24,7 @@ export class FilebrowserComponent implements OnInit { unopened_filelist = []; + shared_filelist = []; file_list = []; @@ -46,17 +48,10 @@ export class FilebrowserComponent implements OnInit { this.files.file_tree_change$.subscribe(data => { this.updateFileData(data);}); - - - this.files.loaded_file_change$.subscribe(data => { - this.updateFileData(data) - }); - - - - - + this.files.shared_file_change$.subscribe(data => { + this.updateFileData(data);}); + } @@ -66,12 +61,12 @@ export class FilebrowserComponent implements OnInit { } - shareWorkspace(){ + shareWorkspace(file_id: number){ const dialogRef = this.dialog.open(ShareComponent, { width: '600px', - data: {fileid: this.files.getCurrentFileId()} + data: {fileid: file_id} }); } @@ -90,20 +85,38 @@ export class FilebrowserComponent implements OnInit { return dateFormat.toLocaleTimeString(); } + /** + * takes an array of file data and parses it into lists + * @param data + */ updateFileData(data: Array){ this.unopened_filelist = []; - console.log("DATA FROM FILES ", data) - + this.shared_filelist = []; this.file_list = []; data.forEach(file => { this.file_list.push(file); - // if(this.files.loaded_files.find(el => el.id == file.id) == undefined){ - // this.unopened_filelist.push(file); - // } - this.unopened_filelist.push(file); - - }) + + if(file.shared == undefined) this.unopened_filelist.push(file); + else this.shared_filelist.push(file); + }) + + } + + editSharedFile(id: number){ + + const dialogRef = this.dialog.open(ShareComponent, { + width: '600px', + data: {fileid:id} + }); + + + } + + + unshare(id: number){ + console.log("UNSHARING ", id) + this.files.removeSharedFile(id.toString()); } @@ -116,12 +129,6 @@ export class FilebrowserComponent implements OnInit { } - close(id: number){ - let item = this.files.getLoadedFile(id); - if(item == null) return; - this.files.unloadFile(id) - } - rename(id: number){ @@ -154,23 +161,40 @@ export class FilebrowserComponent implements OnInit { const link = document.createElement('a') - let loaded = this.files.getLoadedFile(id); - if(loaded !== null){ - var theJSON = JSON.stringify(loaded.ada); + let fns = [ this.files.getFile(id), this.files.getFileMeta(id)]; + Promise.all(fns) + .then(res => { + var theJSON = JSON.stringify(res[0]); + link.href = "data:application/json;charset=UTF-8," + encodeURIComponent(theJSON); + link.download = res[1].name + ".ada"; + link.click(); + }).catch(err => {console.error(err)}); + + + + + + + } + + /** + * this is called when a user pushes save from the topbar + * @param event + */ + public exportSharedWorkspace(id: number){ + + const link = document.createElement('a') + + let fns = [ this.files.getFile(id), this.files.isShared(id.toString())]; + Promise.all(fns) + .then(res => { + var theJSON = JSON.stringify(res[0]); link.href = "data:application/json;charset=UTF-8," + encodeURIComponent(theJSON); - link.download = loaded.name + ".ada"; + link.download = res[1].filename + ".ada"; link.click(); - }else{ - let fns = [ this.files.getFile(id), this.files.getFileMeta(id)]; - Promise.all(fns) - .then(res => { - var theJSON = JSON.stringify(res[0]); - link.href = "data:application/json;charset=UTF-8," + encodeURIComponent(theJSON); - link.download = res[1].name + ".ada"; - link.click(); - }).catch(err => {console.error(err)}); + }).catch(err => {console.error(err)}); + - } From 8a7c325b6d2f52b9a4d43717a953073569ae3dab Mon Sep 17 00:00:00 2001 From: Devendork Date: Thu, 10 Oct 2024 14:14:56 -0600 Subject: [PATCH 04/12] added from_load to meta to handle records of a file being shared --- src/app/app.component.scss | 1 + src/app/app.component.ts | 92 +++++++----- .../core/modal/loadfile/loadfile.component.ts | 2 +- src/app/core/modal/share/share.component.html | 89 ++++++++++-- src/app/core/modal/share/share.component.scss | 54 +++++++ src/app/core/modal/share/share.component.ts | 134 ++++++++---------- src/app/core/model/datatypes.ts | 17 ++- src/app/core/model/defaults.ts | 3 +- src/app/core/provider/file.service.ts | 10 +- src/app/core/provider/filesystem.service.ts | 112 +++++++-------- src/app/core/provider/media.service.ts | 22 ++- src/app/core/provider/workspace.service.ts | 4 - .../ui/filebrowser/filebrowser.component.html | 46 ++---- .../ui/filebrowser/filebrowser.component.scss | 27 +++- .../ui/filebrowser/filebrowser.component.ts | 31 +++- 15 files changed, 399 insertions(+), 245 deletions(-) diff --git a/src/app/app.component.scss b/src/app/app.component.scss index c5b6b694f..44c9c4f95 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -161,6 +161,7 @@ font-size: 14px; display: flex; flex: 0 1; + gap: 8px; } .design_mode{ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 008c4a88d..b1e4322cf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -446,10 +446,7 @@ export class AppComponent implements OnInit{ .then( data => { this.mixer.changeDesignmode('move') this.clearAll(); - - - - console.log("imported new file", result, result.data) + console.log("imported new file", result, result.data) }) .catch(console.error); @@ -497,25 +494,22 @@ export class AppComponent implements OnInit{ const meta = await this.files.getFileMeta(fileid).catch(console.error); - console.log("FIRST SESSION", ada, meta) - if(ada === undefined){ this.loadBlankFile(); }else if(meta === undefined){ - this.files.setCurrentFileInfo(fileid, 'file name not found', ''); - this.prepAndLoadFile('file name not found', 'db', fileid, '', ada); + this.files.setCurrentFileInfo(fileid, 'file name not found', '', ''); + this.prepAndLoadFile('file name not found', 'db', fileid, '', ada, ''); }else{ - this.files.setCurrentFileInfo(fileid, meta.name, meta.desc); - this.prepAndLoadFile(meta.name,'db', fileid, meta.desc, ada); + this.files.setCurrentFileInfo(fileid, meta.name, meta.desc, meta.from_share); + this.prepAndLoadFile(meta.name,'db', fileid, meta.desc, ada, meta.from_share); } }else{ this.auth.getMostRecentAdaFromUser(user).then(async adafile => { - console.log("ADA FILE IS ", adafile) if(adafile !== null){ let fileid = await this.files.convertAdaToFile(user.uid, adafile); console.log("convert ada to file id ", fileid) @@ -526,12 +520,12 @@ export class AppComponent implements OnInit{ if(ada === undefined){ this.loadBlankFile(); }else if(meta === undefined){ - this.files.setCurrentFileInfo(fileid, 'file name not found', ''); - this.prepAndLoadFile('file name not found','db', fileid, '', ada); + this.files.setCurrentFileInfo(fileid, 'file name not found', '', ''); + this.prepAndLoadFile('file name not found','db', fileid, '', ada, ''); }else{ - this.files.setCurrentFileInfo(fileid, meta.name, meta.desc); - this.prepAndLoadFile(meta.name, 'db', fileid, meta.desc, ada); + this.files.setCurrentFileInfo(fileid, meta.name, meta.desc, meta.from_share); + this.prepAndLoadFile(meta.name, 'db', fileid, meta.desc, ada, meta.from_share); } }else{ @@ -546,7 +540,12 @@ export class AppComponent implements OnInit{ //this.loadBlankFile(); this.saveFile(); - this.files.writeNewFileMetaData(user.uid, this.files.getCurrentFileId(), this.files.getCurrentFileName(), this.files.getCurrentFileDesc()) + this.files.writeNewFileMetaData( + user.uid, + this.files.getCurrentFileId(), + this.files.getCurrentFileName(), + this.files.getCurrentFileDesc(), + this.files.getCurrentFileFromShare()) } @@ -619,38 +618,42 @@ export class AppComponent implements OnInit{ async duplicateFileInDB(fileid: number){ const ada = await this.files.getFile(fileid); const meta = await this.files.getFileMeta(fileid); - this.files.duplicate(this.auth.uid, meta.name+"-copy", meta.desc, ada).then(fileid => { - this.prepAndLoadFile(meta.name, 'db', fileid, meta.desc, ada).then(res => { + this.files.duplicate(this.auth.uid, meta.name+"-copy", meta.desc, ada, meta.from_share).then(fileid => { + this.prepAndLoadFile(meta.name, 'db', fileid, meta.desc, ada, meta.from_share).then(res => { this.saveFile(); }); }) } - //must be online + /** + * when someone loads a URL of a shared example, + * the system is going to use the fileID in the url to lookup the file in the files database. + * it is then going to duplicate that file into the users file list. + * while doing so, it needs to copy over elements of the shared file that retain it's legacy and owner. + * so if it is shared again, that information is retained. + * @param shareid + */ async loadFromShare(shareid: string){ - console.log("LOAD FROM SHARE"); + let share_id = -1; + //GET THE SHARED FILE this.files.isShared(shareid).then(share_obj => { if(share_obj == null){ - console.log("THIS FILE IS NOT CURRENTLY SHARED"); return Promise.reject("NO SHARED FILE EXISTS") } var int_shareid: number = +shareid; + share_id = int_shareid; return Promise.all([this.files.getFile(int_shareid), share_obj, shareid]); - }).then(file_objs=>{ - console.log("GOT file OBJ", file_objs[0]) - return this.files.duplicate(this.auth.uid, file_objs[1].filename, file_objs[1].desc, file_objs[0]) + return this.files.duplicate(this.auth.uid, file_objs[1].filename, file_objs[1].desc, file_objs[0], shareid) }).then(fileid => { - console.log("RETURNED FROM DUPLICATE WITH FILE ", fileid); return Promise.all([this.files.getFile(fileid), this.files.getFileMeta(fileid), fileid]); }).then(file_data => { - console.log("Loading File data ", file_data); - return this.prepAndLoadFile(file_data[1].filename, 'db', file_data[2], file_data[1].desc, file_data[0]) + return this.prepAndLoadFile(file_data[1].name, 'db', file_data[2], file_data[1].desc, file_data[0],file_data[1].from_share ) }).catch(err => { console.error(err); }) @@ -661,9 +664,8 @@ export class AppComponent implements OnInit{ async loadFromDB(fileid: number){ const ada = await this.files.getFile(fileid); const meta = await this.files.getFileMeta(fileid); - console.log("GOT ADA ", ada, " and META ", meta) - this.prepAndLoadFile(meta.name, 'db',fileid, meta.desc, ada) + this.prepAndLoadFile(meta.name, 'db',fileid, meta.desc, ada, meta.from_share) .then(res => { this.saveFile(); }); @@ -673,7 +675,7 @@ export class AppComponent implements OnInit{ loadBlankFile(){ this.clearAll(); - this.files.pushToLoadedFilesAndFocus(this.files.generateFileId(), 'new file', '') + this.files.setCurrentFile(this.files.generateFileId(), 'new file', '', '') .then(res => { this.filename_form.setValue(this.files.getCurrentFileName()) this.saveFile(); @@ -687,7 +689,7 @@ export class AppComponent implements OnInit{ */ loadStarterFile(){ - this.files.pushToLoadedFilesAndFocus(this.files.generateFileId(), 'welcome', '').then(res => { + this.files.setCurrentFile(this.files.generateFileId(), 'welcome', '', '').then(res => { let obj = { warps: defaults.warps, wefts: defaults.wefts, @@ -718,7 +720,7 @@ export class AppComponent implements OnInit{ console.log(res); if(res.status == 404) return; this.clearAll(); - return this.fs.loader.ada(name, 'upload',-1, '', res.body) + return this.fs.loader.ada(name, 'upload',-1, '', res.body, '') .then(loadresponse => { this.loadNewFile(loadresponse, 'loadURL') }); @@ -733,9 +735,9 @@ export class AppComponent implements OnInit{ */ loadNewFile(result: LoadResponse, source: string) : Promise{ + console.log("LOAD NEW FILE ", result) - - return this.files.pushToLoadedFilesAndFocus(result.id, result.name, result.desc) + return this.files.setCurrentFile(result.id, result.name, result.desc, result.from_share) .then(res => { this.filename_form.setValue(this.files.getCurrentFileName()) return this.processFileData(result.data) @@ -1010,9 +1012,9 @@ originChange(e:any){ -prepAndLoadFile(name: string, src: string, id: number, desc: string, ada: any) : Promise{ +prepAndLoadFile(name: string, src: string, id: number, desc: string, ada: any, from_share: '') : Promise{ this.clearAll(); - return this.fs.loader.ada(name, src, id,desc, ada).then(lr => { + return this.fs.loader.ada(name, src, id,desc, ada, from_share).then(lr => { return this.loadNewFile(lr, 'prepAndLoad'); }); } @@ -1333,7 +1335,14 @@ redo() { this.viewer.clearView(); this.tree.clear(); - this.fs.loader.ada(this.files.getCurrentFileName(),'redo', this.files.getCurrentFileId(),this.files.getCurrentFileDesc(), so) + this.fs.loader.ada( + this.files.getCurrentFileName(), + 'redo', + this.files.getCurrentFileId(), + this.files.getCurrentFileDesc(), + so, + this.files.getCurrentFileFromShare() + ) .then(lr => this.loadNewFile(lr, 'statechange')); @@ -1356,7 +1365,14 @@ redo() { this.tree.clear(); - this.fs.loader.ada(this.files.getCurrentFileName(), 'undo', this.files.getCurrentFileId(), this.files.getCurrentFileDesc(), so).then(lr => { + this.fs.loader.ada( + this.files.getCurrentFileName(), + 'undo', + this.files.getCurrentFileId(), + this.files.getCurrentFileDesc(), + so, + this.files.getCurrentFileFromShare() + ).then(lr => { this.loadNewFile(lr, 'statechange'); diff --git a/src/app/core/modal/loadfile/loadfile.component.ts b/src/app/core/modal/loadfile/loadfile.component.ts index 84823deda..efec88b0f 100644 --- a/src/app/core/modal/loadfile/loadfile.component.ts +++ b/src/app/core/modal/loadfile/loadfile.component.ts @@ -56,7 +56,7 @@ export class LoadfileComponent { case 'ada': - return this.fls.loader.ada(e.name,'upload',-1, '', e.data) + return this.fls.loader.ada(e.name,'upload',-1, '', e.data, '') .then( res => this.dialogRef.close(res) ); diff --git a/src/app/core/modal/share/share.component.html b/src/app/core/modal/share/share.component.html index d6fc81cd6..9793d9e62 100644 --- a/src/app/core/modal/share/share.component.html +++ b/src/app/core/modal/share/share.component.html @@ -1,38 +1,93 @@
-

Publish this File to a Sharable Link {{fileid}}

+

Configure Sharing

+ + + +

Anyone can access this file with the link below - This file is not shared + This file is not shared, click to turn on sharing

- - - - + -

Sharing Settings

+ +
+
+ +
+ +
License @for (license of licenses; track license) { - {{license.viewValue}} + {{license.viewValue}} + } Need help picking? Visit Creative Commons +
+
Shared File Name @@ -68,10 +123,15 @@

Sharing Settings

Include this in the examples section of AdaCAD
+ + + has uploaded image {{has_uploaded_image}} + replace {{replace_img}} - + - Sharing Settings (onError)="handleError($event)" class="upload-form-box"> + + +
@@ -88,7 +155,7 @@

File History

{{cont.username}}
updated and shared this file on
-
{{cont.timestamp}}
+
{{formatDate(cont.timestamp)}}
diff --git a/src/app/core/modal/share/share.component.scss b/src/app/core/modal/share/share.component.scss index 2cc958341..8da63f93d 100644 --- a/src/app/core/modal/share/share.component.scss +++ b/src/app/core/modal/share/share.component.scss @@ -1,7 +1,57 @@ +h2{ + color: black; + font-weight: normal; + padding-top: 32px; +} + +.owner_setting_line{ + display: flex; + flex-direction: row; +} + +.share_in_history{ + color: red; +} + +.license_link{ + height: 40px; +} + .history_item{ display: flex; flex-direction: row; gap: 11px; + align-items: center; +} + +.license{ + width: 90%; +} + +.share_button{ + margin: 16px 0px; +} +.license-row{ + margin-top: 16px; + display: flex; + flex-direction: row; + align-items: center; +} + +.replace{ + display: block; +} + +.hide{ + display: none; +} + +.license-left{ + flex: 1 1 25%; +} + +.license-right{ + flex: 1 1 75%; } .about{ @@ -10,4 +60,8 @@ .fullwidth{ width: 100%; +} + +.share_in_history{ + color: red; } \ No newline at end of file diff --git a/src/app/core/modal/share/share.component.ts b/src/app/core/modal/share/share.component.ts index 23a05a8a1..7780cbdae 100644 --- a/src/app/core/modal/share/share.component.ts +++ b/src/app/core/modal/share/share.component.ts @@ -1,11 +1,10 @@ import { Component, Inject, inject } from '@angular/core'; -import { FormBuilder, FormControl } from '@angular/forms'; +import { FormControl } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AuthService } from '../../provider/auth.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { FilesystemService } from '../../provider/filesystem.service'; -import { share } from 'rxjs'; -import { AuthorContribution, IndexedColorImageInstance, MediaInstance, ShareObj } from '../../model/datatypes'; +import { AuthorContribution, IndexedColorImageInstance, MediaInstance, ShareObj, SingleImage } from '../../model/datatypes'; import { WorkspaceService } from '../../provider/workspace.service'; import { FileService } from '../../provider/file.service'; import { defaults, licenses } from '../../model/defaults'; @@ -22,6 +21,7 @@ export class ShareComponent { public shared_id: string = ''; public share_obj: ShareObj; public share_url: string; + public has_uploaded_image: boolean = false; public licenses: Array = []; public fileid: string; @@ -29,6 +29,9 @@ export class ShareComponent { public author_list: Array = []; public fc: FormControl; + public share_in_history: ShareObj; + public replace_img: boolean = false; + constructor( private auth: AuthService, private fs: FilesystemService, @@ -42,13 +45,23 @@ export class ShareComponent { this.licenses = licenses; - + //CHECK IF THIS WAS, AT ANY POINT, LOADED FROM A SHARED FILE (which data is held in workspace) + this.fs.getFileMeta(+this.fileid).then(meta => { + if(meta.from_share == '') return Promise.resolve(null); + + return this.fs.isShared(meta.from_share.toString()); + }).then(shareobj => { + this.share_in_history = shareobj; + }); + + + + + - // //check if a link has already been generated for this file. If yes, - // //include an update option. + // CHECK IF A LINK HAD ALREADY BEEN GENERATED FROM THIS (e.g. Edit Share is Called); this.fs.isShared(this.fileid.toString()).then(share_obj => { - console.log("CHECK IS SHARE RETURNED ", share_obj) if(share_obj == null){ //this is not yet shared @@ -57,8 +70,8 @@ export class ShareComponent { }else{ this.share_obj = share_obj; this.shared_id = this.fileid.toString(); - this.updateSettings(share_obj.license, share_obj.author_list); - this.share_url = "https://adacad-4-1.web.app/?share="+this.fileid; + this.updateSettings(share_obj); + this.share_url = defaults.share_url_base+this.fileid; } }).catch(err => { console.log("ENTRY NOT FOUND") @@ -77,13 +90,21 @@ export class ShareComponent { * @param license * @param author_list */ - updateSettings(license: string, author_list: Array){ - this.author_list = author_list.slice(); - + updateSettings(share_obj: ShareObj){ + this.author_list = share_obj.author_list.slice(); + + //upload the image + if(share_obj.img !== 'none'){ + this.mediaService.loadImage(-1, share_obj.img).then(media => { + this.has_uploaded_image = true; + this.drawImage(media.data) + + }); + } + } updateChange(){ - console.log("CHANGE - SHARE OBJ ", this.share_obj) this.fs.updateSharedFile(this.shared_id.toString(), this.share_obj) } @@ -102,15 +123,12 @@ export class ShareComponent { generateLink(){ - console.log("GENERATE LINK") - let int_id: number = +this.fileid; this.fs.getFileMeta(int_id).then(meta => { return Promise.all([this.file_serv.saver.ada(), meta]) }).then(so => { - console.log("GOT SAVER OBJ", so) //add the current time to the author list entry this.author_list.push({ uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', @@ -118,7 +136,7 @@ export class ShareComponent { timestamp: Date.now() }); - return Promise.all([this.fs.duplicate(this.auth.uid, so[1].name,so[1].desc, so[0].file), so[1]]) + return Promise.all([this.fs.duplicate(this.auth.uid, so[1].name,so[1].desc, so[0].file, ''), so[1]]) }).then(id_and_meta => { console.log("ID AND META ", id_and_meta) this.shared_id = id_and_meta[0].toString(); @@ -129,6 +147,7 @@ export class ShareComponent { desc: id_and_meta[1].desc, owner_uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', owner_creditline: (this.auth.isLoggedIn) ? 'created by '+this.auth.username : '', + owner_url: '', public: false, img: 'none' @@ -136,58 +155,25 @@ export class ShareComponent { return this.fs.createSharedFile(this.shared_id , this.share_obj) }).then(share_data => { - console.log("CREATED SHARED FILE ENTRY TO ", share_data) - - this.share_url = "https://adacad-4-1.web.app/?share="+this.shared_id; + this.share_url = defaults.share_url_base+this.shared_id; }).catch(err => { console.log("ERROR") }) } - // updateSharedFile(){ - // //does this share a new link - - // this.file_serv.saver.ada() - // .then(so => { - - // //add the current time to the author list entry - // this.author_list.push({ - // uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', - // username: (this.auth.isLoggedIn) ? this.auth.username : 'anonymous', - // timestamp: Date.now() - // }); - - // const share:ShareObj = { - // license: this.selected_license, - // author_list: this.author_list, - // ada: so.file, - // filename: this.fs.getCurrentFileName(), - // desc: this.fs.getCurrentFileDesc() - - // } - // this.fs.updateSharedFile(this.fileid, share) - - - - // }).catch(err => { - // console.log("ERROR") - // }) - - - - - - // } - removeLink(){ this.fs.removeSharedFile(this.fileid); this.author_list = []; } + replaceImg(){ + this.replace_img = true; + } + formatDate(date: number){ var dateFormat = new Date(date); - return dateFormat.toLocaleTimeString(); + return dateFormat.toLocaleDateString(); } /** @@ -196,33 +182,39 @@ export class ShareComponent { */ handleFile(obj: MediaInstance){ + this.replace_img = false; + this.has_uploaded_image = true; if(obj === null || obj[0].data == null) return; this.share_obj.img = obj[0].ref; this.updateChange(); + this.drawImage(obj[0].data); - const data = obj[0].data; - - const canvas: HTMLCanvasElement = document.getElementById('img_preview'); - const ctx = canvas.getContext('2d'); - - const max_dim = (data.width > data.height) ? data.width : data.height; - const use_width = (data.width > 400) ? data.width / max_dim * 400 : data.width; - const use_height = (data.height > 400) ? data.height / max_dim * 400 : data.height; - - canvas.width = use_width; - canvas.height = use_height; + } - ctx.putImageData(data, 0, 0, 0, 0, use_width, use_height); - - - + drawImage(img: SingleImage){ + + console.log("DATA ", img) + + const canvas: HTMLCanvasElement = document.getElementById('img_preview'); + const ctx = canvas.getContext('2d'); + const max_dim = (img.width > img.height) ? img.width : img.height; + const use_width = (img.width > 400) ? img.width / max_dim * 400 : img.width; + const use_height = (img.height > 400) ? img.height / max_dim * 400 : img.height; + canvas.width = use_width; + canvas.height = use_height; + + + + ctx.drawImage(img.image, 0, 0, img.width, img.height, 0, 0, use_width, use_height); + + } diff --git a/src/app/core/model/datatypes.ts b/src/app/core/model/datatypes.ts index e65a245e2..db25aa408 100644 --- a/src/app/core/model/datatypes.ts +++ b/src/app/core/model/datatypes.ts @@ -429,11 +429,12 @@ export interface LoadResponse{ id: number, name: string, desc: string, - status: number; + status: number, + from_share: string } export interface Fileloader{ - ada: (filename: string, src: string, id: number, desc: string, data: any) => Promise, + ada: (filename: string, src: string, id: number, desc: string, data: any, from_share: string) => Promise, paste: (data: any) => Promise, //wif: (filename: string, data: any) => Promise, } @@ -679,6 +680,17 @@ export type DynamicOperation = Operation & { } +export interface SingleImage{ + name: string, + data: ImageData, + image: HTMLImageElement, + width:number, + height: number, + type: string, + warning: string +} + + /****************** OBJECTS/TYPES RELATED to OPERATION TREE *****************/ @@ -973,6 +985,7 @@ export type ShareObj = { license: string, owner_uid: string, owner_creditline: string, + owner_url: string, author_list: Array, filename: string, desc: string, diff --git a/src/app/core/model/defaults.ts b/src/app/core/model/defaults.ts index 7b7d47ba0..b81231971 100644 --- a/src/app/core/model/defaults.ts +++ b/src/app/core/model/defaults.ts @@ -35,7 +35,8 @@ export const defaults = { zoom_ndx_mixer: 10, zoom_ndx_editor: 12, zoom_ndx_viewer: 7, - show_advanced_operations: false + show_advanced_operations: false, + share_url_base: 'http://localhost:4200/?share=' } diff --git a/src/app/core/provider/file.service.ts b/src/app/core/provider/file.service.ts index b21034f49..e44113a3e 100644 --- a/src/app/core/provider/file.service.ts +++ b/src/app/core/provider/file.service.ts @@ -57,10 +57,12 @@ export class FileService { */ const dloader: Fileloader = { - ada: async (filename: string, src: string, id: number, desc: string, data: any) : Promise => { + ada: async (filename: string, src: string, id: number, desc: string, data: any, from_share: string) : Promise => { + if(desc === undefined) desc = "" if(filename == undefined) filename = 'draft' + if(from_share == undefined) from_share = '' if(id === -1) id = this.files.generateFileId(); let draft_nodes: Array = []; @@ -100,7 +102,7 @@ export class FileService { const draft_elements = []; const draft_fns = []; - + console.log("VERSION IS ", version) if(!utilInstance.sameOrNewerVersion(version, '3.4.9')){ data.nodes.forEach(node => { if(node.bounds !== undefined) node.topleft = node.bounds.topleft; @@ -241,7 +243,7 @@ export class FileService { indexed_image_data: indexed_images } - return Promise.resolve({data: envt, name: filename, desc: desc, status: 0, id:id }); + return Promise.resolve({data: envt, name: filename, desc: desc, status: 0, id:id, from_share: from_share }); } ) @@ -354,7 +356,7 @@ export class FileService { indexed_image_data: indexed_images } - return Promise.resolve({data: envt, name: 'paste', desc: 'a file represeting copied information', status: 0, id:-1 }); + return Promise.resolve({data: envt, name: 'paste', desc: 'a file represeting copied information', status: 0, id:-1, from_share: '' }); } ) diff --git a/src/app/core/provider/filesystem.service.ts b/src/app/core/provider/filesystem.service.ts index 5b743c915..d3add0154 100644 --- a/src/app/core/provider/filesystem.service.ts +++ b/src/app/core/provider/filesystem.service.ts @@ -2,7 +2,7 @@ import { Injectable, Optional } from '@angular/core'; import { Auth, authState, getAuth } from '@angular/fire/auth'; import { get as fbget, getDatabase, onChildAdded, onChildRemoved, onDisconnect, onValue, orderByChild, update, ref as fbref, ref, remove, query, onChildChanged, set } from '@angular/fire/database'; // import { onChildAdded, onChildChanged, onChildRemoved, onDisconnect, onValue, orderByChild, update } from 'firebase/database'; -import { Observable, Subject } from 'rxjs'; +import { Observable, Subject, from } from 'rxjs'; import { FilebrowserComponent } from '../ui/filebrowser/filebrowser.component'; import { SaveObj, ShareObj } from '../model/datatypes'; import utilInstance from '../model/util'; @@ -23,7 +23,7 @@ export class FilesystemService { private current_file_id: number = -1; public current_file_name: string = ''; public current_file_desc: string = ''; - + public current_file_from_share: string = ''; connected: boolean = false; @@ -65,7 +65,6 @@ export class FilesystemService { //called once per item, then on subsequent changes onChildAdded(userFiles, (childsnapshot) => { - console.log("ON CHILD ADDED") //only add values that haven't already been added if(this.file_tree.find(el => el.id === parseInt(childsnapshot.key)) === undefined){ @@ -80,7 +79,6 @@ export class FilesystemService { }); onChildAdded(sharedFiles, (childsnapshot) => { - console.log("ON SHARE ADDED") //only add values that haven't already been added if(this.file_tree.find(el => el.id === parseInt(childsnapshot.key)) !== undefined){ this.shared_file_change$.next(this.file_tree.slice()); @@ -91,7 +89,6 @@ export class FilesystemService { //called when anything in meta changes onChildChanged(userFiles, (data) => { - console.log("CHILD CHANGED ", userFiles) const ndx = this.file_tree.findIndex(el => parseInt(el.id) === parseInt(data.key)); if(ndx !== -1){ this.isShared(data.key).then(res => { @@ -141,45 +138,13 @@ export class FilesystemService { } /** - * given a new file that has just been loaded, update the meta-data to match the value of this item. - * @param id - * @param ada - * @returns boolean representing if the id had meta-data already stored or if new meta-data was created + sets the current file data */ - public pushToLoadedFilesAndFocus(id: number, name: string, desc: string) : Promise{ + public setCurrentFile(id: number, name: string, desc: string, from_share: string) : Promise{ - - return this.getFileMeta(id) - .then(res => { - // this.loaded_files.push({ - // id: id, - // name: res.name, - // desc: res.desc, - // ada: undefined, - // last_saved_time: 0 - // }); - this.setCurrentFileInfo(id, res.name, res.desc); - // this.loaded_file_change$.next(this.file_tree.slice()); + this.setCurrentFileInfo(id, name, desc, from_share); return Promise.resolve(true); - - }) - .catch(nodata => { - // this.loaded_files.push({ - // id: id, - // name:name, - // desc: desc, - // ada: undefined, - // last_saved_time: 0 - // }) - this.setCurrentFileInfo(id, name, desc); - // this.loaded_file_change$.next(this.file_tree.slice()); - return Promise.resolve(false); - }); - - // }else{ - // this.setCurrentFileInfo(item.id, item.name, item.desc); - // return Promise.reject('this file has already been loaded') - // } + } // public unloadFile(id: number){ @@ -205,6 +170,11 @@ export class FilesystemService { return this.current_file_desc; } + public getCurrentFileFromShare() : string{ + return this.current_file_from_share; + } + + public changeObserver(target: FilebrowserComponent) : Observable>{ @@ -243,15 +213,18 @@ export class FilesystemService { * @param name * @param desc */ - setCurrentFileInfo(fileid: number, name: string, desc: string){ + setCurrentFileInfo(fileid: number, name: string, desc: string, from_share: string){ if(fileid === null || fileid == undefined) return; if(desc === null || desc === undefined) desc = ''; if(name === null || name === undefined) name = 'no name'; + if(from_share === null || from_share === undefined) name = ''; this.setCurrentFileId(fileid); this.current_file_name = name; + this.current_file_desc = desc; + this.current_file_from_share = from_share; const stamp = Date.now(); if(!this.connected) return; @@ -264,7 +237,8 @@ export class FilesystemService { update(fbref(db, 'users/'+user.uid+'/files/'+fileid),{ name: name, desc: desc, - timestamp: stamp}); + timestamp: stamp, + from_share: from_share}); } @@ -320,7 +294,7 @@ export class FilesystemService { const fileid = this.generateFileId(); this.writeFileData(fileid, ada); - this.writeNewFileMetaData(uid, fileid, 'recovered draft', '') + this.writeNewFileMetaData(uid, fileid, 'recovered draft', '', '') return Promise.resolve(fileid); } @@ -329,12 +303,11 @@ export class FilesystemService { * takes the current state, gives it a new file ID and pushes it to the database * @returns the id of the file */ -duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ - console.log("DUPLICATING") +duplicate(uid: string, name: string, desc: string, ada: any, from_share: string) : Promise{ const fileid = this.generateFileId(); this.writeFileData(fileid, ada); - this.writeNewFileMetaData(uid, fileid, name, desc) + this.writeNewFileMetaData(uid, fileid, name, desc, from_share) return Promise.resolve(fileid); } @@ -347,16 +320,13 @@ duplicate(uid: string, name: string, desc: string, ada: any) : Promise{ * @returns */ createSharedFile(file_id: string, share_data: ShareObj) : Promise { - console.log("CREATING SHARED FILE ", file_id, share_data) - if(!this.connected) return; const db = getDatabase(); const ref = fbref(db, 'shared/'+file_id); - set(ref,share_data) + return set(ref,share_data) .then(success => { - console.log("SUCCESS"); return Promise.resolve(file_id) }) @@ -379,15 +349,22 @@ isShared(file_id:string) : Promise { return fbget(fbref(db, `shared/${file_id}`)) .then((filedata) => { - if(filedata.exists()){ - return Promise.resolve({ - ada: filedata.val().ada, + + const share_obj:ShareObj = { author_list: filedata.val().author_list, + desc: filedata.val().desc, license: filedata.val().license, filename: filedata.val().filename, - desc: filedata.val().desc}); + img: filedata.val().img, + owner_creditline: filedata.val().owner_creditline, + owner_uid: filedata.val().owner_uid, + owner_url: filedata.val().owner_url, + public: filedata.val().public + } + return Promise.resolve(share_obj); + }else{ return Promise.resolve(null) } @@ -460,10 +437,7 @@ removeSharedFile(file_id: string) : Promise { this.getFileMeta(int_id).then(meta => { - console.log("META GOT ", meta) if(user){ - console.log("UPDATED ", meta) - update(fbref(db, 'users/'+user.uid+'/files/'+file_id),{name: meta.name + "(previously shared)"}); } }) @@ -535,8 +509,17 @@ getFile(fileid: number) : Promise { return fbget(fbref(db, 'users/'+user.uid+'/files/'+fileid)).then((meta) => { - if(meta.exists()){ - return Promise.resolve(meta.val()); + if(meta.exists()){ + let obj = { + desc: meta.val().desc, + last_opened: meta.val().last_opened, + name: meta.val().name, + timestamp: meta.val().timestamp, + from_share: (meta.val().from_share == undefined ) ? '' : meta.val().from_share + } + + + return Promise.resolve(obj); }else{ return Promise.reject("No meta data found for file id "+fileid) } @@ -564,7 +547,6 @@ getFile(fileid: number) : Promise { * @returns */ writeFileData(fileid: number, cur_state: SaveObj) { - console.log("ATTEMPTING TO WRITE FILE DATA ", cur_state, fileid) if(!this.connected) return; @@ -581,17 +563,21 @@ getFile(fileid: number) : Promise { } - writeNewFileMetaData(uid: string, fileid: number, name: string, desc: string) { + writeNewFileMetaData(uid: string, fileid: number, name: string, desc: string, from_share: string) { if(!this.connected) return; + if(from_share == undefined || from_share == null) from_share = ''; + const stamp = Date.now(); const db = getDatabase(); update(fbref(db, 'users/'+uid+'/files/'+fileid),{ name: name, desc: desc, timestamp: stamp, - last_opened:fileid}); + last_opened:fileid, + from_share: from_share + }); } diff --git a/src/app/core/provider/media.service.ts b/src/app/core/provider/media.service.ts index e58c98287..50795e04e 100644 --- a/src/app/core/provider/media.service.ts +++ b/src/app/core/provider/media.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { AnalyzedImage, Color, IndexedColorImageInstance, IndexedColorMediaProxy, MediaInstance } from '../model/datatypes'; +import { AnalyzedImage, Color, IndexedColorImageInstance, IndexedColorMediaProxy, MediaInstance, SingleImage } from '../model/datatypes'; import { UploadService } from './upload.service'; import { HttpClient } from '@angular/common/http'; import utilInstance from '../model/util'; @@ -35,7 +35,7 @@ export class MediaService { .filter(el => el.ref !== '') .map(el => { if(el.type == 'indexed_color_image') return this.loadIndexedColorFile(el.id, el.ref, el.data) - else return this.loadImage(el.id, el.ref, el.data) + else return this.loadImage(el.id, el.ref) }); return Promise.all(fns); } @@ -200,12 +200,12 @@ export class MediaService { } /** - * loads an indexed color file + * loads an image file * @param id the unique reference for this file * @param data an object containing any color or color_mapping data that has already been stored for this item * @returns */ - loadImage(id: number, ref: string, data: any) : Promise{ + loadImage(id: number, ref: string) : Promise{ console.log("LOADING IMAGE") if(id == -1){ @@ -235,7 +235,19 @@ export class MediaService { ctx.drawImage(image, 0, 0, canvas.width, canvas.height); var imgdata = ctx.getImageData(0,0, canvas.width, canvas.height); - return Promise.resolve(imgdata); + + var obj: SingleImage = { + name: 'placeholder', + data: imgdata, + image: image, + width: imgdata.width, + height: imgdata.height, + type: 'image', + warning: '' + } + + + return Promise.resolve(obj); }).then(imageobj => { if(imageobj == null){ diff --git a/src/app/core/provider/workspace.service.ts b/src/app/core/provider/workspace.service.ts index b0d64177f..04b007ce6 100644 --- a/src/app/core/provider/workspace.service.ts +++ b/src/app/core/provider/workspace.service.ts @@ -16,7 +16,6 @@ export class WorkspaceService { file_favorites: Array = []; - loaded_from_share_id: number = -1; min_frames: number = defaults.loom_settings.frames; min_treadles: number = defaults.loom_settings.treadles; type: string = defaults.loom_settings.type; //'rigid', 'direct', 'frame', 'jacquard' @@ -50,7 +49,6 @@ export class WorkspaceService { this.selected_origin_option = defaults.selected_origin_option; this.hide_mixer_drafts = defaults.hide_mixer_drafts; this.show_advanced_operations = defaults.show_advanced_operations; - this.loaded_from_share_id = -1; } @@ -67,7 +65,6 @@ export class WorkspaceService { this.file_favorites = (data.file_favorites === undefined) ? [] : data.file_favorites; this.hide_mixer_drafts = (data.hide_mixer_drafts === undefined) ? true : data.hide_mixer_drafts; this.show_advanced_operations = (data.show_advanced_operations === undefined) ? false : data.show_advanced_operations; - this.loaded_from_share_id = (data.loaded_from_share_id === undefined) ? [] : data.loaded_from_share_id; } @@ -127,7 +124,6 @@ export class WorkspaceService { file_favorites: this.file_favorites.slice(), hide_mixer_drafts: this.hide_mixer_drafts, show_advanced_operations: this.show_advanced_operations, - loaded_from_share_id: this.loaded_from_share_id } } diff --git a/src/app/core/ui/filebrowser/filebrowser.component.html b/src/app/core/ui/filebrowser/filebrowser.component.html index 88aeb927e..91c6c0be1 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.html +++ b/src/app/core/ui/filebrowser/filebrowser.component.html @@ -11,18 +11,6 @@ - - - -
to load and save files online
@@ -51,13 +39,16 @@

Your Shared Files

-

{{file.shared.filename}}

-

{{file.shared.desc}}

+

{{file.shared.filename}}

+

{{file.shared.desc}}

-
+
+ +
@@ -65,7 +56,7 @@

{{file.shared.filename}}

- + @@ -141,15 +132,9 @@

{{file.shared.filename}}

- - - - - -
-

All Files Saved on AdaCAD Server

+

All Files

@@ -172,11 +157,9 @@

All Files Saved on AdaCAD Server

- - Rename Workspace - + + - - - +

{{file.meta.name}}

- {{file.meta.name}} {{file.id}}
-
{{file.meta.date}}
+
opened
+
{{file.meta.date}}
@@ -204,7 +186,7 @@

All Files Saved on AdaCAD Server

- + diff --git a/src/app/core/ui/filebrowser/filebrowser.component.scss b/src/app/core/ui/filebrowser/filebrowser.component.scss index 06bce3465..09c064aa4 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.scss +++ b/src/app/core/ui/filebrowser/filebrowser.component.scss @@ -4,15 +4,27 @@ h2{ } h3{ - font-size: 1em; + font-size: 1.5em; padding-top: 40px; font-weight: bold; } +h4{ + padding: 0px; + margin: 0px; + line-height: 40px; + +} + +.desc{ + font-size: .8em; + padding: 0px; + margin: 0px; +} + .filebrowser{ display: flex; flex-direction: column; - gap: 10px; width: 100%; } @@ -21,7 +33,6 @@ h3{ display: flex; flex-direction: column; // overflow-y: scroll; - gap: 10px; } @@ -43,17 +54,21 @@ h3{ } + .file-item-block{ display: flex; flex-direction: row; justify-content: space-evenly; width: 100%; - border-bottom: thin dotted; - align-items: center; + border-bottom: thin dotted grey; + align-items: flex-start; + padding: 8px 0px; + gap: 12px; + } .current{ - background-color: #ff4081; + color: #ff4081; } diff --git a/src/app/core/ui/filebrowser/filebrowser.component.ts b/src/app/core/ui/filebrowser/filebrowser.component.ts index 145866d3f..eb6639825 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.ts +++ b/src/app/core/ui/filebrowser/filebrowser.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Inject, OnInit, Optional, Output,ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Inject, OnInit, Optional, Output,ViewEncapsulation, inject } from '@angular/core'; import { AuthService } from '../../provider/auth.service'; import { FilesystemService } from '../../provider/filesystem.service'; import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; @@ -7,6 +7,8 @@ import { FileService } from '../../provider/file.service'; import { LoginComponent } from '../../modal/login/login.component'; import { ShareComponent } from '../../modal/share/share.component'; import { share } from 'rxjs'; +import { defaults } from '../../model/defaults'; +import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ selector: 'app-filebrowser', @@ -16,6 +18,7 @@ import { share } from 'rxjs'; }) export class FilebrowserComponent implements OnInit { + private _snackBar = inject(MatSnackBar); @Output() onLoadFromDB: any = new EventEmitter(); @Output() onCreateFile: any = new EventEmitter(); @@ -80,11 +83,6 @@ export class FilebrowserComponent implements OnInit { this.onCreateFile.emit(); } - formatDate(date: number){ - var dateFormat = new Date(date); - return dateFormat.toLocaleTimeString(); - } - /** * takes an array of file data and parses it into lists * @param data @@ -115,7 +113,6 @@ export class FilebrowserComponent implements OnInit { unshare(id: number){ - console.log("UNSHARING ", id) this.files.removeSharedFile(id.toString()); } @@ -177,6 +174,26 @@ export class FilebrowserComponent implements OnInit { } + openSnackBar(message: string, action: string) { + this._snackBar.open(message, action); + } + + + copyToClipboard(id: number){ + const share_url = defaults.share_url_base+id; + navigator.clipboard.writeText(share_url) + .then( + ()=> { + this.openSnackBar('link copied', 'close')//on success + }, + () => { + //on fail + this.openSnackBar('could not copy link', 'close')//on success + + } + ) + + } /** * this is called when a user pushes save from the topbar * @param event From 3e07928b91c82c02561a3ab2c129c520d0ab83e1 Mon Sep 17 00:00:00 2001 From: Devendork Date: Thu, 10 Oct 2024 14:32:37 -0600 Subject: [PATCH 05/12] removed history from share link and author list from database --- src/app/core/modal/share/share.component.html | 22 ++++++++++++------- src/app/core/modal/share/share.component.ts | 12 ++-------- src/app/core/model/datatypes.ts | 8 ------- src/app/core/provider/filesystem.service.ts | 3 +-- src/app/core/provider/workspace.service.ts | 3 --- .../ui/filebrowser/filebrowser.component.html | 2 ++ 6 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/app/core/modal/share/share.component.html b/src/app/core/modal/share/share.component.html index 9793d9e62..27d513c77 100644 --- a/src/app/core/modal/share/share.component.html +++ b/src/app/core/modal/share/share.component.html @@ -66,7 +66,16 @@

Owner Settings

- + @@ -124,10 +133,6 @@

Owner Settings

Include this in the examples section of AdaCAD - - has uploaded image {{has_uploaded_image}} - replace {{replace_img}} - Owner Settings (click)="replaceImg()">replace image -
+
-

About Link Sharing: When you create a link, it is equivalent to the "save as" operation on most computers: a copy of this file, in this state, is created and can be opened by another user via the link. You will not see any edits they make to the file and they will not see any future edits you make to this file. If you want to publish edits you have made to this file, you must generate and share a new link. You can manage your shared files using the file manager

+

About Link Sharing: Sharing a file is equivalent to a "save as" operation on most computers: a copy of this file, in this state, is created and can be opened by another user using a sharing link that AdaCAD will generate. + You will not see any edits they make to the file and other users will not see any future edits you make to this file. If you want to publish edits you have made to this file, you must generate and share a new link.

diff --git a/src/app/core/modal/share/share.component.ts b/src/app/core/modal/share/share.component.ts index 7780cbdae..8066b47f8 100644 --- a/src/app/core/modal/share/share.component.ts +++ b/src/app/core/modal/share/share.component.ts @@ -4,7 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AuthService } from '../../provider/auth.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { FilesystemService } from '../../provider/filesystem.service'; -import { AuthorContribution, IndexedColorImageInstance, MediaInstance, ShareObj, SingleImage } from '../../model/datatypes'; +import { MediaInstance, ShareObj, SingleImage } from '../../model/datatypes'; import { WorkspaceService } from '../../provider/workspace.service'; import { FileService } from '../../provider/file.service'; import { defaults, licenses } from '../../model/defaults'; @@ -26,7 +26,6 @@ export class ShareComponent { public licenses: Array = []; public fileid: string; - public author_list: Array = []; public fc: FormControl; public share_in_history: ShareObj; @@ -52,6 +51,7 @@ export class ShareComponent { return this.fs.isShared(meta.from_share.toString()); }).then(shareobj => { this.share_in_history = shareobj; + }); @@ -91,7 +91,6 @@ export class ShareComponent { * @param author_list */ updateSettings(share_obj: ShareObj){ - this.author_list = share_obj.author_list.slice(); //upload the image if(share_obj.img !== 'none'){ @@ -130,11 +129,6 @@ export class ShareComponent { }).then(so => { //add the current time to the author list entry - this.author_list.push({ - uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', - username: (this.auth.isLoggedIn) ? this.auth.username : 'anonymous', - timestamp: Date.now() - }); return Promise.all([this.fs.duplicate(this.auth.uid, so[1].name,so[1].desc, so[0].file, ''), so[1]]) }).then(id_and_meta => { @@ -142,7 +136,6 @@ export class ShareComponent { this.shared_id = id_and_meta[0].toString(); this.share_obj = { license: 'by', - author_list: this.author_list, filename: id_and_meta[1].name, desc: id_and_meta[1].desc, owner_uid: (this.auth.isLoggedIn) ? this.auth.uid : 'anon', @@ -164,7 +157,6 @@ export class ShareComponent { removeLink(){ this.fs.removeSharedFile(this.fileid); - this.author_list = []; } replaceImg(){ diff --git a/src/app/core/model/datatypes.ts b/src/app/core/model/datatypes.ts index db25aa408..ca979b9ef 100644 --- a/src/app/core/model/datatypes.ts +++ b/src/app/core/model/datatypes.ts @@ -974,19 +974,11 @@ export type RenderingFlags = { */ -export type AuthorContribution = { - uid: string, - username: string, - timestamp: number -} - -//consider if this should index on file id or share id. of if you even need both of them. export type ShareObj = { license: string, owner_uid: string, owner_creditline: string, owner_url: string, - author_list: Array, filename: string, desc: string, public:boolean, diff --git a/src/app/core/provider/filesystem.service.ts b/src/app/core/provider/filesystem.service.ts index d3add0154..a7e27cea6 100644 --- a/src/app/core/provider/filesystem.service.ts +++ b/src/app/core/provider/filesystem.service.ts @@ -2,7 +2,7 @@ import { Injectable, Optional } from '@angular/core'; import { Auth, authState, getAuth } from '@angular/fire/auth'; import { get as fbget, getDatabase, onChildAdded, onChildRemoved, onDisconnect, onValue, orderByChild, update, ref as fbref, ref, remove, query, onChildChanged, set } from '@angular/fire/database'; // import { onChildAdded, onChildChanged, onChildRemoved, onDisconnect, onValue, orderByChild, update } from 'firebase/database'; -import { Observable, Subject, from } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import { FilebrowserComponent } from '../ui/filebrowser/filebrowser.component'; import { SaveObj, ShareObj } from '../model/datatypes'; import utilInstance from '../model/util'; @@ -352,7 +352,6 @@ isShared(file_id:string) : Promise { if(filedata.exists()){ const share_obj:ShareObj = { - author_list: filedata.val().author_list, desc: filedata.val().desc, license: filedata.val().license, filename: filedata.val().filename, diff --git a/src/app/core/provider/workspace.service.ts b/src/app/core/provider/workspace.service.ts index 04b007ce6..5d9d1badc 100644 --- a/src/app/core/provider/workspace.service.ts +++ b/src/app/core/provider/workspace.service.ts @@ -1,8 +1,5 @@ import { Injectable } from '@angular/core'; -import { AuthorContribution, LoomSettings } from '../model/datatypes'; import { defaults } from '../model/defaults'; -import utilInstance from '../model/util'; -import { defaultMaxListeners } from 'events'; @Injectable({ providedIn: 'root' diff --git a/src/app/core/ui/filebrowser/filebrowser.component.html b/src/app/core/ui/filebrowser/filebrowser.component.html index 91c6c0be1..d55c116ff 100644 --- a/src/app/core/ui/filebrowser/filebrowser.component.html +++ b/src/app/core/ui/filebrowser/filebrowser.component.html @@ -22,6 +22,7 @@ +

Your Shared Files

@@ -133,6 +134,7 @@

{{file.shared.filename}}

+

All Files

From 07f8e7fe7345d16a16f637260ad8bc2086f58ce6 Mon Sep 17 00:00:00 2001 From: Devendork Date: Thu, 10 Oct 2024 14:40:05 -0600 Subject: [PATCH 06/12] finalized styling on share warning --- src/app/core/modal/share/share.component.html | 15 +++++++++------ src/app/core/modal/share/share.component.scss | 5 ++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/app/core/modal/share/share.component.html b/src/app/core/modal/share/share.component.html index 27d513c77..3d68baeae 100644 --- a/src/app/core/modal/share/share.component.html +++ b/src/app/core/modal/share/share.component.html @@ -10,20 +10,23 @@

Configure Sharing

Owner Settings

+ -
+
- credit line: + original credit line:
{{share_in_history.owner_creditline}} @@ -31,9 +34,9 @@

Owner Settings

-
+
- url + url:
{{share_in_history.owner_url}} diff --git a/src/app/core/modal/share/share.component.scss b/src/app/core/modal/share/share.component.scss index 8da63f93d..5b5427236 100644 --- a/src/app/core/modal/share/share.component.scss +++ b/src/app/core/modal/share/share.component.scss @@ -7,14 +7,17 @@ h2{ .owner_setting_line{ display: flex; flex-direction: row; + align-items: center; + height: 40px; } .share_in_history{ color: red; } -.license_link{ +.license_history{ height: 40px; + display: inline-block; } .history_item{ From 474aba040fd3eccceb8a717917d15bcbc1695b00 Mon Sep 17 00:00:00 2001 From: Devendork Date: Fri, 11 Oct 2024 09:15:43 -0600 Subject: [PATCH 07/12] enabled ability to load shared files as long as you are connected to the internet --- src/app/app.component.ts | 57 ++++++++++++++----- .../core/modal/examples/examples.component.ts | 16 +++++- src/app/core/modal/share/share.component.html | 17 +++++- src/app/core/modal/share/share.component.ts | 15 ++++- src/app/core/model/drafts.ts | 9 ++- src/app/core/provider/file.service.ts | 2 - src/app/core/provider/filesystem.service.ts | 26 +++++++++ .../ui/filebrowser/filebrowser.component.html | 2 +- 8 files changed, 115 insertions(+), 29 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b1e4322cf..1184d4a9d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -471,17 +471,16 @@ export class AppComponent implements OnInit{ return; } + if(searchParams.has('share')){ + this.loadFromShare(searchParams.get('share')); + return; + } + if(user === null){ console.log("USER IS NULL") this.loadStarterFile(); }else{ - if(searchParams.has('share')){ - this.loadFromShare(searchParams.get('share')); - return; - } - - if(this.auth.isFirstSession() || (!this.auth.isFirstSession() && this.isBlankWorkspace())){ this.auth.getMostRecentFileIdFromUser(user).then(async fileid => { @@ -636,6 +635,9 @@ export class AppComponent implements OnInit{ */ async loadFromShare(shareid: string){ let share_id = -1; + console.log("LOAD FROM SHARE ", shareid, this.auth.isLoggedIn) + + //GET THE SHARED FILE this.files.isShared(shareid).then(share_obj => { @@ -649,15 +651,44 @@ export class AppComponent implements OnInit{ return Promise.all([this.files.getFile(int_shareid), share_obj, shareid]); }).then(file_objs=>{ - return this.files.duplicate(this.auth.uid, file_objs[1].filename, file_objs[1].desc, file_objs[0], shareid) - }).then(fileid => { - return Promise.all([this.files.getFile(fileid), this.files.getFileMeta(fileid), fileid]); - }).then(file_data => { - return this.prepAndLoadFile(file_data[1].name, 'db', file_data[2], file_data[1].desc, file_data[0],file_data[1].from_share ) - }).catch(err => { - console.error(err); + + if(this.auth.isLoggedIn) this.loadFromShareWhileLoggedIn(file_objs); + else this.loadFromShareWhileLoggedOut(file_objs); + + }) } + + /** + * if the user is logged in, this automatically duplicates the shared file into a new file within their + * local directory + * @param file_objs + */ + loadFromShareWhileLoggedIn(file_objs:any){ + + this.files.duplicate(this.auth.uid, file_objs[1].filename, file_objs[1].desc, file_objs[0], file_objs[2]) + .then(fileid => { + return Promise.all([this.files.getFile(fileid), this.files.getFileMeta(fileid), fileid]); + }).then(file_data => { + return this.prepAndLoadFile(file_data[1].name, 'db', file_data[2], file_data[1].desc, file_data[0],file_data[1].from_share ) + }).catch(err => { + console.error(err); + }) + + } + + /** + * if the user isn't logged in, they should still be able to load the file locally. It should just get + * a new id in the case they eventually login. + * @param share_id + */ + loadFromShareWhileLoggedOut(file_objs: any){ + + this.prepAndLoadFile(file_objs[1].filename, 'db', utilInstance.generateId(8), file_objs[1].desc, file_objs[0], file_objs[2]); + + + } + //must be online diff --git a/src/app/core/modal/examples/examples.component.ts b/src/app/core/modal/examples/examples.component.ts index 8f4a48986..ccf74e989 100644 --- a/src/app/core/modal/examples/examples.component.ts +++ b/src/app/core/modal/examples/examples.component.ts @@ -5,6 +5,8 @@ import { AuthService } from '../../provider/auth.service'; import { FileService } from '../../provider/file.service'; import { ExampleserviceService } from '../../provider/exampleservice.service'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { FilebrowserComponent } from '../../ui/filebrowser/filebrowser.component'; +import { FilesystemService } from '../../provider/filesystem.service'; @Component({ selector: 'app-examples', @@ -14,16 +16,24 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog'; export class ExamplesComponent { @Output() onLoadExample = new EventEmitter (); local_examples: any; + community_examples: any; + constructor( - private fls: FileService, - private auth: AuthService, - private http: HttpClient, + public fs: FilesystemService, public examples: ExampleserviceService, private dialog: MatDialog, public dialogRef: MatDialogRef) { this.local_examples = examples.getExamples(); + + + console.log("PUBLIC FILES:", this.fs.public_files) + this.fs.public_file_change$.subscribe(data => { + this.community_examples = data.slice(); + console.log("COMMUNITY EXAMPLES ", this.community_examples) + }); + } loadExample(filename: string){ diff --git a/src/app/core/modal/share/share.component.html b/src/app/core/modal/share/share.component.html index 3d68baeae..f62a0902b 100644 --- a/src/app/core/modal/share/share.component.html +++ b/src/app/core/modal/share/share.component.html @@ -5,7 +5,19 @@

Configure Sharing

-