diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index d8731df..55499ff 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -16,7 +16,7 @@ Run backend: `rclone rcd --rc-user="" --rc-pass="" Run frontend: `ng serve` -Api calling will be proxied to backed [config](./src/proxy.conf.mjs) +Api calling will be proxied to backed [config](../src/proxy.conf.mjs) ## Translation diff --git a/src/app/features/functions/backend/new-backend/new-backend.component.ts b/src/app/features/functions/backend/new-backend/new-backend.component.ts index e30036f..1dfe372 100644 --- a/src/app/features/functions/backend/new-backend/new-backend.component.ts +++ b/src/app/features/functions/backend/new-backend/new-backend.component.ts @@ -93,17 +93,24 @@ export class NewBackendComponent implements OnInit { } // warn user + const enum action { + back, + continue, + } this.dialog .open(SimpleDialogComponent, { data: { title: $localize`Warning`, message: $localize`This provider requires authentication. Because you are using a remote backend, automatic authentication (OAuth) is not possible for you. You may need to authorize on local rclone instance and copy the token to the backend.`, - actions: ['Go back', 'Continue Anyway'], + actions: [ + { label: $localize`Go back`, value: action.back }, + { label: $localize`Continue Anyway`, value: action.continue }, + ], }, }) .afterClosed() .subscribe((result) => { - if (result !== 'Continue Anyway') { + if (result !== action.continue) { this.providerSelected = undefined; this.stepperSelectedIndex = 0; } diff --git a/src/app/features/functions/mount/mount.component.ts b/src/app/features/functions/mount/mount.component.ts index 6131cb9..d0bf8be 100644 --- a/src/app/features/functions/mount/mount.component.ts +++ b/src/app/features/functions/mount/mount.component.ts @@ -50,6 +50,8 @@ export class MountComponent implements OnInit { noModTime?: boolean; vfsCacheMode?: string; vfsCacheMaxAge?: string; + customMountOpt?: string; + customVfsOpt?: string; } > = this.dialog.open(NewMountDialogComponent, { data: { @@ -83,8 +85,8 @@ export class MountComponent implements OnInit { return; } } - const mountOpt: { [key: string]: string | boolean | number } = {}; - const vfsOpt: { [key: string]: string | boolean | number } = {}; + let mountOpt: { [key: string]: string | boolean | number } = {}; + let vfsOpt: { [key: string]: string | boolean | number } = {}; if (dialogResult.readonly !== undefined) { mountOpt['readonly'] = dialogResult.readonly; } @@ -106,6 +108,12 @@ export class MountComponent implements OnInit { if (dialogResult.vfsCacheMaxAge !== undefined) { vfsOpt['vfsCacheMaxAge'] = dialogResult.vfsCacheMaxAge; } + if (dialogResult.customMountOpt !== undefined) { + mountOpt = { ...mountOpt, ...JSON.parse(dialogResult.customMountOpt) }; + } + if (dialogResult.customVfsOpt !== undefined) { + vfsOpt = { ...vfsOpt, ...JSON.parse(dialogResult.customVfsOpt) }; + } const id = await this.mountService.createSetting({ Fs: dialogResult.Fs, diff --git a/src/app/features/functions/mount/mount.module.ts b/src/app/features/functions/mount/mount.module.ts index 07504c5..21eeb47 100644 --- a/src/app/features/functions/mount/mount.module.ts +++ b/src/app/features/functions/mount/mount.module.ts @@ -15,6 +15,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { MountRoutingModule } from './mount-routing.module'; import { MountComponent } from './mount.component'; @@ -26,6 +27,7 @@ import { NewMountDialogComponent } from './new-mount-dialog/new-mount-dialog.com CommonModule, ReactiveFormsModule, MountRoutingModule, + MatTooltipModule, MatListModule, MatButtonModule, MatIconModule, diff --git a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.html b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.html index db49ccd..cfa25e0 100644 --- a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.html +++ b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.html @@ -8,16 +8,16 @@

Create MountPoint

- + Auto Mount on Startup - - + Automatic MountPoint - + MountPoint @@ -35,16 +35,16 @@

Create MountPoint

- + Mount as Readonly Drive - - + Mount as Windows Network Drive - + Cache Mode @@ -58,6 +58,10 @@

Create MountPoint

Full
+ + Cache Max Age + + File Permissions @@ -66,9 +70,35 @@

Create MountPoint

Directory Permissions
- + Skip modification time (can speed things up) - + + + Custom Mount Option (Json) + + + + + Custom Vfs Option (Json) + + +
diff --git a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.scss b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.scss index a1ae13f..ace46ae 100644 --- a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.scss +++ b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.scss @@ -7,3 +7,7 @@ display: flex; justify-content: center; } + +mat-slide-toggle { + margin: 1em; +} diff --git a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.ts b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.ts index 0a9e391..6c5dbac 100644 --- a/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.ts +++ b/src/app/features/functions/mount/new-mount-dialog/new-mount-dialog.component.ts @@ -1,7 +1,10 @@ import { Component, Inject } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; + +import { jsonStringValidator } from 'src/app/shared/json-string-validator.directive'; +import { SimpleDialogComponent } from 'src/app/shared/simple-dialog/simple-dialog.component'; import { environment } from 'src/environments/environment'; @Component({ @@ -32,11 +35,14 @@ export class NewMountDialogComponent { '1h', [Validators.required, Validators.pattern(/^\d+[smhd]$/)], ], + customMountOpt: ['{\n}', jsonStringValidator()], + customVfsOpt: ['{\n}', jsonStringValidator()], }); showAdvancedOptions = false; hasCron = environment.electron; constructor( + private dialog: MatDialog, private fb: FormBuilder, @Inject(MAT_DIALOG_DATA) public data: { @@ -51,4 +57,23 @@ export class NewMountDialogComponent { this.mountForm.controls.MountPoint.setValue('/mnt/rclone'); } } + + getMountOptHelp() { + this.dialog.open(SimpleDialogComponent, { + data: { + title: $localize`Information`, + message: $localize`This is options for advanced user only!\nPlease input options in JSON format, keys are in PascalCase.\nAvailable options: please refer to https://github.com/rclone/rclone/blob/master/cmd/mountlib/mount.go\nfind "type Options struct" part`, + actions: [{ label: $localize`Close`, value: 0 }], + }, + }); + } + getVfsOptHelp() { + this.dialog.open(SimpleDialogComponent, { + data: { + title: $localize`Information`, + message: $localize`This is options for advanced user only!\nPlease input options in JSON format, keys are in PascalCase.\nAvailable options: please refer to https://github.com/rclone/rclone/blob/master/vfs/vfscommon/options.go\nfind "type Options struct" part`, + actions: [{ label: $localize`Close`, value: 0 }], + }, + }); + } } diff --git a/src/app/shared/json-string-validator.directive.ts b/src/app/shared/json-string-validator.directive.ts new file mode 100644 index 0000000..903b9a6 --- /dev/null +++ b/src/app/shared/json-string-validator.directive.ts @@ -0,0 +1,12 @@ +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; + +export function jsonStringValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + try { + JSON.parse(control.value); + return null; + } catch { + return { invalidJsonString: true }; + } + }; +} diff --git a/src/app/shared/simple-dialog/simple-dialog.component.html b/src/app/shared/simple-dialog/simple-dialog.component.html index 0ee0b45..fed1426 100644 --- a/src/app/shared/simple-dialog/simple-dialog.component.html +++ b/src/app/shared/simple-dialog/simple-dialog.component.html @@ -1,11 +1,11 @@

{{ data.title }}

-
{{ data.message }}
+
{{ data.message }}
diff --git a/src/app/shared/simple-dialog/simple-dialog.component.scss b/src/app/shared/simple-dialog/simple-dialog.component.scss index e69de29..5c908c7 100644 --- a/src/app/shared/simple-dialog/simple-dialog.component.scss +++ b/src/app/shared/simple-dialog/simple-dialog.component.scss @@ -0,0 +1,3 @@ +.message { + white-space: pre-wrap; +} diff --git a/src/app/shared/simple-dialog/simple-dialog.component.ts b/src/app/shared/simple-dialog/simple-dialog.component.ts index 4e2ae07..77e8710 100644 --- a/src/app/shared/simple-dialog/simple-dialog.component.ts +++ b/src/app/shared/simple-dialog/simple-dialog.component.ts @@ -10,13 +10,16 @@ import { MatButtonModule } from '@angular/material/button'; templateUrl: './simple-dialog.component.html', styleUrls: ['./simple-dialog.component.scss'], }) -export class SimpleDialogComponent { +export class SimpleDialogComponent { constructor( @Inject(MAT_DIALOG_DATA) public data: { title: string; message: string; - actions: string[]; + actions: { + value: T; + label: string; + }[]; }, ) {} }