) => {
+ const debouncedText$ = text$.pipe(debounceTime(200), distinctUntilChanged());
+ const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
+ const inputFocus$ = this.focus$;
+
+ return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$, text$).pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ tap(() => this.searching = true),
+ switchMap(term =>
+ this.libraryService.listDirectories(this.path).pipe(
+ tap(() => this.searchFailed = false),
+ tap((folders) => this.folders = folders),
+ map(folders => folders.map(f => f.fullPath)),
+ catchError(() => {
+ this.searchFailed = true;
+ return of([]);
+ }))
+ ),
+ tap(() => this.searching = false)
+ )
+ }
constructor(public modal: NgbActiveModal, private libraryService: LibraryService) {
@@ -51,15 +85,16 @@ export class DirectoryPickerComponent implements OnInit {
}
}
- filterFolder = (folder: string) => {
- return folder.toLowerCase().indexOf((this.filterQuery || '').toLowerCase()) >= 0;
+ updateTable() {
+ this.loadChildren(this.path);
}
- selectNode(folderName: string) {
- this.currentRoot = folderName;
- this.routeStack.push(folderName);
- const fullPath = this.routeStack.items.join('/');
- this.loadChildren(fullPath);
+
+ selectNode(folder: DirectoryDto) {
+ this.currentRoot = folder.name;
+ this.routeStack.push(folder.name);
+ this.path = folder.fullPath;
+ this.loadChildren(this.path);
}
goBack() {
@@ -77,7 +112,6 @@ export class DirectoryPickerComponent implements OnInit {
loadChildren(path: string) {
this.libraryService.listDirectories(path).subscribe(folders => {
- this.filterQuery = '';
this.folders = folders;
}, err => {
// If there was an error, pop off last directory added to stack
@@ -85,19 +119,17 @@ export class DirectoryPickerComponent implements OnInit {
});
}
- shareFolder(folderName: string, event: any) {
+ shareFolder(fullPath: string, event: any) {
event.preventDefault();
event.stopPropagation();
- let fullPath = folderName;
- if (this.routeStack.items.length > 0) {
- const pathJoin = this.routeStack.items.join('/');
- fullPath = pathJoin + ((pathJoin.endsWith('/') || pathJoin.endsWith('\\')) ? '' : '/') + folderName;
- }
-
this.modal.close({success: true, folderPath: fullPath});
}
+ share() {
+ this.modal.close({success: true, folderPath: this.path});
+ }
+
close() {
this.modal.close({success: false, folderPath: undefined});
}
@@ -122,6 +154,9 @@ export class DirectoryPickerComponent implements OnInit {
}
const fullPath = this.routeStack.items.join('/');
+ this.path = fullPath;
this.loadChildren(fullPath);
}
}
+
+
diff --git a/UI/Web/src/app/admin/_models/server-settings.ts b/UI/Web/src/app/admin/_models/server-settings.ts
index 6a493a0aa..6600a8794 100644
--- a/UI/Web/src/app/admin/_models/server-settings.ts
+++ b/UI/Web/src/app/admin/_models/server-settings.ts
@@ -10,4 +10,5 @@ export interface ServerSettings {
bookmarksDirectory: string;
emailServiceUrl: string;
convertBookmarkToWebP: boolean;
+ enableSwaggerUi: boolean;
}
diff --git a/UI/Web/src/app/admin/admin.module.ts b/UI/Web/src/app/admin/admin.module.ts
index 2b30cadd6..dd20d02ca 100644
--- a/UI/Web/src/app/admin/admin.module.ts
+++ b/UI/Web/src/app/admin/admin.module.ts
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { DashboardComponent } from './dashboard/dashboard.component';
-import { NgbDropdownModule, NgbNavModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgbDropdownModule, NgbNavModule, NgbTooltipModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { ManageLibraryComponent } from './manage-library/manage-library.component';
import { ManageUsersComponent } from './manage-users/manage-users.component';
import { LibraryEditorModalComponent } from './_modals/library-editor-modal/library-editor-modal.component';
@@ -53,11 +53,12 @@ import { ManageTasksSettingsComponent } from './manage-tasks-settings/manage-tas
FormsModule,
NgbNavModule,
NgbTooltipModule,
+ NgbTypeaheadModule, // Directory Picker
NgbDropdownModule,
SharedModule,
PipeModule,
SidenavModule,
- UserSettingsModule // API-key componet
+ UserSettingsModule, // API-key componet
],
providers: []
})
diff --git a/UI/Web/src/app/admin/manage-settings/manage-settings.component.html b/UI/Web/src/app/admin/manage-settings/manage-settings.component.html
index c6b999671..baca9f382 100644
--- a/UI/Web/src/app/admin/manage-settings/manage-settings.component.html
+++ b/UI/Web/src/app/admin/manage-settings/manage-settings.component.html
@@ -47,6 +47,15 @@
+
+
Expose Swagger UI
+
Allows Swagger UI to be exposed via swagger/ on your server. Authentication is not required, but a valid JWT token is. Requires a restart to take effect.
+
+
+ Enable Swagger UI
+
+
+
OPDS
diff --git a/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts b/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts
index 6be0935d0..fe463521b 100644
--- a/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts
+++ b/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts
@@ -3,8 +3,7 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs/operators';
-import { ConfirmService } from 'src/app/shared/confirm.service';
-import { EmailTestResult, SettingsService } from '../settings.service';
+import { SettingsService } from '../settings.service';
import { DirectoryPickerComponent, DirectoryPickerResult } from '../_modals/directory-picker/directory-picker.component';
import { ServerSettings } from '../_models/server-settings';
@@ -21,7 +20,7 @@ export class ManageSettingsComponent implements OnInit {
taskFrequencies: Array = [];
logLevels: Array = [];
- constructor(private settingsService: SettingsService, private toastr: ToastrService, private confirmService: ConfirmService,
+ constructor(private settingsService: SettingsService, private toastr: ToastrService,
private modalService: NgbModal) { }
ngOnInit(): void {
@@ -43,6 +42,7 @@ export class ManageSettingsComponent implements OnInit {
this.settingsForm.addControl('enableOpds', new FormControl(this.serverSettings.enableOpds, [Validators.required]));
this.settingsForm.addControl('baseUrl', new FormControl(this.serverSettings.baseUrl, [Validators.required]));
this.settingsForm.addControl('emailServiceUrl', new FormControl(this.serverSettings.emailServiceUrl, [Validators.required]));
+ this.settingsForm.addControl('enableSwaggerUi', new FormControl(this.serverSettings.enableSwaggerUi, [Validators.required]));
});
}
@@ -57,6 +57,7 @@ export class ManageSettingsComponent implements OnInit {
this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds);
this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl);
this.settingsForm.get('emailServiceUrl')?.setValue(this.serverSettings.emailServiceUrl);
+ this.settingsForm.get('enableSwaggerUi')?.setValue(this.serverSettings.enableSwaggerUi);
}
async saveSettings() {
@@ -92,29 +93,4 @@ export class ManageSettingsComponent implements OnInit {
}
});
}
-
- // resetEmailServiceUrl() {
- // this.settingsService.resetEmailServerSettings().pipe(take(1)).subscribe(async (settings: ServerSettings) => {
- // this.serverSettings.emailServiceUrl = settings.emailServiceUrl;
- // this.resetForm();
- // this.toastr.success('Email Service Reset');
- // }, (err: any) => {
- // console.error('error: ', err);
- // });
- // }
-
- // testEmailServiceUrl() {
- // this.settingsService.testEmailServerSettings(this.settingsForm.get('emailServiceUrl')?.value || '').pipe(take(1)).subscribe(async (result: EmailTestResult) => {
- // if (result.successful) {
- // this.toastr.success('Email Service Url validated');
- // } else {
- // this.toastr.error('Email Service Url did not respond. ' + result.errorMessage);
- // }
-
- // }, (err: any) => {
- // console.error('error: ', err);
- // });
-
- // }
-
}