-
Notifications
You must be signed in to change notification settings - Fork 104
feat(dataset): add dataset file editor #3654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ff28d58
0576f67
6f850b0
ec3d83d
87e1140
a5ca833
ff6da19
a1446e1
a55fca7
5939a06
7fde500
73adc7b
720b665
4358435
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,16 +46,21 @@ | |
| "@types/lodash-es": "4.17.4", | ||
| "@types/plotly.js-basic-dist-min": "2.12.4", | ||
| "ajv": "8.10.0", | ||
| "angular-markdown-editor": "^3.1.1", | ||
| "backbone": "1.4.1", | ||
| "bootstrap": "^5.3.7", | ||
| "bootstrap-markdown": "^2.10.0", | ||
| "content-disposition": "0.5.4", | ||
| "dagre": "0.8.5", | ||
| "deep-map": "2.0.0", | ||
| "edit-distance": "1.0.4", | ||
| "es6-weak-map": "2.0.3", | ||
| "file-saver": "2.0.5", | ||
| "font-awesome": "^4.7.0", | ||
| "fuse.js": "6.5.3", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
| "html2canvas": "1.4.1", | ||
| "jointjs": "3.5.4", | ||
| "jquery": "^3.7.1", | ||
| "js-abbreviation-number": "1.4.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move it to the dependee package, and leave a comment |
||
| "jszip": "3.10.1", | ||
| "lodash-es": "4.17.21", | ||
|
|
@@ -111,11 +116,13 @@ | |
| "@nrwl/nx-cloud": "19.1.0", | ||
| "@nx/angular": "20.0.3", | ||
| "@types/backbone": "1.4.15", | ||
| "@types/bootstrap": "^5", | ||
| "@types/content-disposition": "0", | ||
| "@types/dagre": "0.7.47", | ||
| "@types/file-saver": "2.0.5", | ||
| "@types/graphlib": "2.1.8", | ||
| "@types/jasmine": "4.6.4", | ||
| "@types/jquery": "^3", | ||
| "@types/json-schema": "7.0.9", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aglinxinyuan Please take a look to see if both devDependencies & dependencies need to have these packages |
||
| "@types/lodash": "4.14.179", | ||
| "@types/node": "18.15.5", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,7 @@ import { DatasetStagedObject } from "../../../../../common/type/dataset-staged-o | |
| import { NzModalService } from "ng-zorro-antd/modal"; | ||
| import { UserDatasetVersionCreatorComponent } from "./user-dataset-version-creator/user-dataset-version-creator.component"; | ||
| import { AdminSettingsService } from "../../../../service/admin/settings/admin-settings.service"; | ||
| import { of } from "rxjs"; | ||
| import { HttpErrorResponse } from "@angular/common/http"; | ||
| import { Subscription } from "rxjs"; | ||
| import { formatSpeed, formatTime } from "src/app/common/util/format.util"; | ||
|
|
@@ -102,6 +103,9 @@ export class DatasetDetailComponent implements OnInit { | |
| versionName: string = ""; | ||
| isCreatingVersion: boolean = false; | ||
|
|
||
| public isCreatingReadme: boolean = false; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this |
||
| public isEditMode: boolean = false; | ||
|
|
||
| // List of upload tasks – each task tracked by its filePath | ||
| public uploadTasks: Array< | ||
| MultipartUploadProgress & { | ||
|
|
@@ -221,6 +225,149 @@ export class DatasetDetailComponent implements OnInit { | |
| } | ||
| } | ||
|
|
||
| public onFileChanged(): void { | ||
| this.userMakeChanges.emit(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this, simply reload the dataset to show the latest version |
||
|
|
||
| // Get the current filename to re-select after refresh | ||
| const currentFileName = this.currentDisplayedFileName; | ||
|
|
||
| this.retrieveDatasetVersionList(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the subscribe style to achieve the call handling |
||
|
|
||
| // Wait a bit for the version list to update, then refresh the current version | ||
| setTimeout(() => { | ||
| if (this.versions.length > 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this |
||
| // Select the latest version (newly created) | ||
| this.selectedVersion = this.versions[0]; | ||
|
|
||
| // Refresh the file tree for the new version | ||
| if (this.did && this.selectedVersion.dvid) { | ||
| this.datasetService | ||
| .retrieveDatasetVersionFileTree(this.did, this.selectedVersion.dvid, this.isLogin) | ||
| .pipe(untilDestroyed(this)) | ||
| .subscribe(data => { | ||
| this.fileTreeNodeList = data.fileNodes; | ||
| this.currentDatasetVersionSize = data.size; | ||
|
|
||
| // Try to find and re-select the same file we were editing | ||
| const fileNode = this.findFileInTree(currentFileName); | ||
| if (fileNode) { | ||
| this.loadFileContent(fileNode); | ||
| } else { | ||
| // Fallback to first file if our file isn't found | ||
| let currentNode = this.fileTreeNodeList[0]; | ||
| while (currentNode && currentNode.type === "directory" && currentNode.children) { | ||
| currentNode = currentNode.children[0]; | ||
| } | ||
| if (currentNode) { | ||
| this.loadFileContent(currentNode); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| }, 500); // Small delay to ensure backend has processed the new version | ||
|
|
||
| this.exitEditMode(); | ||
| } | ||
|
|
||
| private findFileInTree(fileName: string, nodes: DatasetFileNode[] = this.fileTreeNodeList): DatasetFileNode | null { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this, by default, show the README.md(if any) of a dataset version when users open a dataset, otherwise, show the first file in the file tree. |
||
| for (const node of nodes) { | ||
| if (node.name === fileName && node.type === "file") { | ||
| return node; | ||
| } | ||
| if (node.children) { | ||
| const found = this.findFileInTree(fileName, node.children); | ||
| if (found) { | ||
| return found; | ||
| } | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| public onClickCreateReadme(): void { | ||
| this.modalService.confirm({ | ||
| nzTitle: "Create README.md", | ||
| nzContent: "Are you sure you want to create a README.md file for this dataset?", | ||
| nzOkText: "Yes, Create", | ||
| nzCancelText: "Cancel", | ||
| nzOnOk: () => { | ||
| this.createReadmeFile(); | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| private createReadmeFile(): void { | ||
| if (!this.did) return; | ||
|
|
||
| this.isCreatingReadme = true; | ||
| const defaultReadmeContent = "# Dataset README\n\nDescribe your dataset here..."; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace 'Dataset' with actual dataset name |
||
|
|
||
| this.datasetService | ||
| .getDataset(this.did, this.isLogin) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see if you can avoid this additional request |
||
| .pipe( | ||
| switchMap(dashboardDataset => { | ||
| const datasetName = dashboardDataset.dataset.name; | ||
| const readmeBlob = new Blob([defaultReadmeContent], { type: "text/markdown" }); | ||
| const readmeFile = new File([readmeBlob], "README.md", { type: "text/markdown" }); | ||
| return this.datasetService.multipartUpload( | ||
| datasetName, | ||
| "README.md", | ||
| readmeFile, | ||
| this.chunkSizeMB * 1024 * 1024, | ||
| this.maxConcurrentChunks | ||
| ); | ||
| }), | ||
| switchMap(progress => { | ||
| if (progress.status === "finished") { | ||
| return this.datasetService.createDatasetVersion(this.did!, "Created README.md"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Created" => "Create"; "README.md" => "README" |
||
| } | ||
| return of(progress); | ||
| }), | ||
| untilDestroyed(this) | ||
| ) | ||
| .subscribe({ | ||
| next: result => { | ||
| if (result && typeof result === "object" && "dvid" in result) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this |
||
| this.isCreatingReadme = false; | ||
| this.notificationService.success("README created successfully!"); | ||
|
|
||
| this.currentDisplayedFileName = "README.md"; | ||
| this.onFileChanged(); | ||
|
|
||
| setTimeout(() => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this |
||
| this.isEditMode = true; | ||
| }, 1000); | ||
| } | ||
| }, | ||
| error: (error: unknown) => { | ||
| this.isCreatingReadme = false; | ||
| console.error("Error creating README:", error); | ||
| this.notificationService.error("Failed to create README"); | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| public hasReadmeFile(): boolean { | ||
| return this.findFileInTree("README.md") !== null; | ||
| } | ||
|
|
||
| public isEditableFile(fileName: string): boolean { | ||
| const extension = fileName.toLowerCase().split(".").pop(); | ||
| const editableExtensions = ["md", "markdown", "txt", "log", "yml", "yaml"]; | ||
| return editableExtensions.includes(extension || ""); | ||
| } | ||
|
|
||
| public onClickEditFile(): void { | ||
| if (!this.selectedVersion || !this.currentDisplayedFileName) return; | ||
|
|
||
| this.isEditMode = !this.isEditMode; | ||
| } | ||
|
|
||
| public exitEditMode(): void { | ||
| this.isEditMode = false; | ||
| } | ||
|
|
||
| onPublicStatusChange(checked: boolean): void { | ||
| // Handle the change in dataset public status | ||
| if (this.did) { | ||
|
|
@@ -341,6 +488,8 @@ export class DatasetDetailComponent implements OnInit { | |
| } | ||
|
|
||
| onVersionSelected(version: DatasetVersion): void { | ||
| this.exitEditMode(); | ||
|
|
||
| this.selectedVersion = version; | ||
| if (this.did && this.selectedVersion.dvid) | ||
| this.datasetService | ||
|
|
@@ -362,6 +511,7 @@ export class DatasetDetailComponent implements OnInit { | |
| } | ||
|
|
||
| onVersionFileTreeNodeSelected(node: DatasetFileNode) { | ||
| this.exitEditMode(); | ||
| this.loadFileContent(node); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix the versions of the libraries you introduced by removing
^