Server Settings
@@ -116,6 +116,7 @@
{{user.username | sentenceCase}}
diff --git a/UI/Web/src/app/nav-header/nav-header.component.scss b/UI/Web/src/app/nav-header/nav-header.component.scss
index 99fe40200..1ff9cc3f0 100644
--- a/UI/Web/src/app/nav-header/nav-header.component.scss
+++ b/UI/Web/src/app/nav-header/nav-header.component.scss
@@ -18,6 +18,22 @@ $bg-color: rgb(22, 27, 34);
}
}
+// On Really small screens, hide the server settings wheel and show it in nav
+.xs-only {
+ display: none;
+}
+.not-xs-only {
+ display: inherit;
+}
+@media only screen and (max-width:300px) {
+ .xs-only {
+ display: inherit;
+ }
+ .not-xs-only {
+ display: none;
+ }
+}
+
.nav-item.dropdown {
position: unset;
}
diff --git a/UI/Web/src/app/registration/add-email-to-account-migration-modal/add-email-to-account-migration-modal.component.ts b/UI/Web/src/app/registration/add-email-to-account-migration-modal/add-email-to-account-migration-modal.component.ts
index ca781fb39..2b50053fa 100644
--- a/UI/Web/src/app/registration/add-email-to-account-migration-modal/add-email-to-account-migration-modal.component.ts
+++ b/UI/Web/src/app/registration/add-email-to-account-migration-modal/add-email-to-account-migration-modal.component.ts
@@ -46,10 +46,10 @@ export class AddEmailToAccountMigrationModalComponent implements OnInit {
if (!canAccess) {
// Display the email to the user
this.emailLink = email;
- await this.confirmService.alert('Please click this link to confirm your email. You must confirm to be able to login. You may need to log out of the current account before clicking.
' + this.emailLink + '');
+ await this.confirmService.alert('Please click this link to confirm your email. You must confirm to be able to login. The link is in your logs. You may need to log out of the current account before clicking.
' + this.emailLink + '');
this.modal.close(true);
} else {
- await this.confirmService.alert('Please check your email for the confirmation link. You must confirm to be able to login.');
+ await this.confirmService.alert('Please check your email (or logs) for the confirmation link. You must confirm to be able to login.');
this.modal.close(true);
}
});
diff --git a/UI/Web/src/app/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/series-detail.component.ts
index 673c6515b..86da2b219 100644
--- a/UI/Web/src/app/series-detail/series-detail.component.ts
+++ b/UI/Web/src/app/series-detail/series-detail.component.ts
@@ -281,10 +281,10 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
handleVolumeActionCallback(action: Action, volume: Volume) {
switch(action) {
case(Action.MarkAsRead):
- this.markAsRead(volume);
+ this.markVolumeAsRead(volume);
break;
case(Action.MarkAsUnread):
- this.markAsUnread(volume);
+ this.markVolumeAsUnread(volume);
break;
case(Action.Edit):
this.openViewInfo(volume);
@@ -334,22 +334,6 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
});
}
- markSeriesAsUnread(series: Series) {
- this.seriesService.markUnread(series.id).subscribe(res => {
- this.toastr.success(series.name + ' is now unread');
- series.pagesRead = 0;
- this.loadSeries(series.id);
- });
- }
-
- markSeriesAsRead(series: Series) {
- this.seriesService.markRead(series.id).subscribe(res => {
- series.pagesRead = series.pages;
- this.toastr.success(series.name + ' is now read');
- this.loadSeries(series.id);
- });
- }
-
loadSeries(seriesId: number) {
this.coverImageOffset = 0;
@@ -444,25 +428,23 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
this.readerService.getCurrentChapter(this.series.id).subscribe(chapter => this.currentlyReadingChapter = chapter);
}
- markAsRead(vol: Volume) {
+ markVolumeAsRead(vol: Volume) {
if (this.series === undefined) {
return;
}
- const seriesId = this.series.id;
- this.actionService.markVolumeAsRead(seriesId, vol, () => {
+ this.actionService.markVolumeAsRead(this.series.id, vol, () => {
this.setContinuePoint();
this.actionInProgress = false;
});
}
- markAsUnread(vol: Volume) {
+ markVolumeAsUnread(vol: Volume) {
if (this.series === undefined) {
return;
}
- const seriesId = this.series.id;
- this.actionService.markVolumeAsUnread(seriesId, vol, () => {
+ this.actionService.markVolumeAsUnread(this.series.id, vol, () => {
this.setContinuePoint();
this.actionInProgress = false;
});
@@ -472,9 +454,8 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
if (this.series === undefined) {
return;
}
- const seriesId = this.series.id;
- this.actionService.markChapterAsRead(seriesId, chapter, () => {
+ this.actionService.markChapterAsRead(this.series.id, chapter, () => {
this.setContinuePoint();
this.actionInProgress = false;
});
@@ -484,9 +465,8 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
if (this.series === undefined) {
return;
}
- const seriesId = this.series.id;
- this.actionService.markChapterAsUnread(seriesId, chapter, () => {
+ this.actionService.markChapterAsUnread(this.series.id, chapter, () => {
this.setContinuePoint();
this.actionInProgress = false;
});
diff --git a/UI/Web/src/app/typeahead/typeahead-settings.ts b/UI/Web/src/app/typeahead/typeahead-settings.ts
index 7e531bf84..6e4406165 100644
--- a/UI/Web/src/app/typeahead/typeahead-settings.ts
+++ b/UI/Web/src/app/typeahead/typeahead-settings.ts
@@ -1,6 +1,8 @@
import { Observable } from 'rxjs';
import { FormControl } from '@angular/forms';
+export type SelectionCompareFn
= (a: T, b: T) => boolean;
+
export class TypeaheadSettings {
/**
* How many ms between typing actions before pipeline to load data is triggered
@@ -24,7 +26,11 @@ export class TypeaheadSettings {
/**
* Function to compare the elements. Should return all elements that fit the matching criteria. This is only used with non-Observable based fetchFn, but must be defined for all uses of typeahead (TODO)
*/
- compareFn!: ((optionList: T[], filter: string) => T[]);
+ compareFn!: ((optionList: T[], filter: string) => T[]);
+ /**
+ * Function which is used for comparing objects when keeping track of state. Useful over shallow equal when you have image urls that have random numbers on them.
+ */
+ singleCompareFn?: SelectionCompareFn;
/**
* Function to fetch the data from the server. If data is mainatined in memory, wrap in an observable.
*/
diff --git a/UI/Web/src/app/typeahead/typeahead.component.ts b/UI/Web/src/app/typeahead/typeahead.component.ts
index d26912044..a02ccdd08 100644
--- a/UI/Web/src/app/typeahead/typeahead.component.ts
+++ b/UI/Web/src/app/typeahead/typeahead.component.ts
@@ -3,9 +3,9 @@ import { FormControl, FormGroup } from '@angular/forms';
import { Observable, of, ReplaySubject, Subject } from 'rxjs';
import { debounceTime, filter, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators';
import { KEY_CODES } from '../shared/_services/utility.service';
-import { TypeaheadSettings } from './typeahead-settings';
+import { SelectionCompareFn, TypeaheadSettings } from './typeahead-settings';
-export type SelectionCompareFn = (a: T, b: T) => boolean;
+//export type SelectionCompareFn = (a: T, b: T) => boolean;
/**
* SelectionModel is used for keeping track of multiple selections. Simple interface with ability to toggle.
@@ -60,14 +60,16 @@ export class SelectionModel {
* @param compareFn optional method to use to perform comparisons
* @returns boolean
*/
- isSelected(data: T, compareFn?: ((d: T) => boolean)): boolean {
+ isSelected(data: T, compareFn?: SelectionCompareFn): boolean {
let dataItem: Array;
+
+ let lookupMethod = this.shallowEqual;
if (compareFn != undefined || compareFn != null) {
- dataItem = this._data.filter(d => compareFn(d.value));
- } else {
- dataItem = this._data.filter(d => this.shallowEqual(d.value, data));
+ lookupMethod = compareFn;
}
+ dataItem = this._data.filter(d => lookupMethod(d.value, data));
+
if (dataItem.length > 0) {
return dataItem[0].selected;
}
@@ -283,13 +285,17 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
if (item.classList.contains('active')) {
this.filteredOptions.pipe(take(1)).subscribe((res: any[]) => {
// This isn't giving back the filtered array, but everything
- const result = this.settings.compareFn(res, (item.textContent || '').trim());
+
+ if (this.settings.addIfNonExisting && item.classList.contains('add-item')) {
+ this.addNewItem(this.typeaheadControl.value);
+ this.resetField();
+ this.focusedIndex = 0;
+ return;
+ }
+
+ const result = this.settings.compareFn(res, (this.typeaheadControl.value || '').trim());
if (result.length === 1) {
- if (item.classList.contains('add-item')) {
- this.addNewItem(this.typeaheadControl.value);
- } else {
- this.toggleSelection(result[0]);
- }
+ this.toggleSelection(result[0]);
this.resetField();
this.focusedIndex = 0;
}
@@ -320,12 +326,12 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
}
toggleSelection(opt: any): void {
- this.optionSelection.toggle(opt);
+ this.optionSelection.toggle(opt, undefined, this.settings.singleCompareFn);
this.selectedData.emit(this.optionSelection.selected());
}
removeSelectedOption(opt: any) {
- this.optionSelection.toggle(opt);
+ this.optionSelection.toggle(opt, undefined, this.settings.singleCompareFn);
this.selectedData.emit(this.optionSelection.selected());
this.resetField();
}
@@ -351,7 +357,7 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
filterSelected(item: any) {
if (this.settings.unique && this.settings.multiple) {
- return !this.optionSelection.isSelected(item);
+ return !this.optionSelection.isSelected(item, this.settings.singleCompareFn);
}
return true;