diff --git a/API/.dockerignore b/API/.dockerignore deleted file mode 100644 index cd967fc3a..000000000 --- a/API/.dockerignore +++ /dev/null @@ -1,25 +0,0 @@ -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/.idea -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md \ No newline at end of file diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index 78663ca16..7ced84d6a 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -150,8 +150,7 @@ namespace API.Controllers { var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername()); var totalPages = await _cacheService.CacheBookmarkForSeries(user.Id, seriesId); - // TODO: Change Includes to None from LinkedSeries branch - var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); + var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.None); return Ok(new BookmarkInfoDto() { @@ -172,11 +171,6 @@ namespace API.Controllers if (!await _unitOfWork.CommitAsync()) return BadRequest("There was an issue saving progress"); - // var series = new List() - // {await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(markReadDto.SeriesId, user.Id)}; - // await _unitOfWork.SeriesRepository.AddSeriesModifiers(user.Id, series); - // await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate, - // MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName, markReadDto.SeriesId, series[0], series[0].Pages)); return Ok(); } @@ -194,16 +188,6 @@ namespace API.Controllers if (!await _unitOfWork.CommitAsync()) return BadRequest("There was an issue saving progress"); - // Should I do this for every chapter? Maybe in a background task? - // foreach (var chapterId in await - // _unitOfWork.SeriesRepository.GetChapterIdsForSeriesAsync(new List() {markReadDto.SeriesId})) - // { - // await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate, - // MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName, chapterId, MessageFactoryEntityTypes.Chapter, 0)); - // } - // - // await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate, - // MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName, markReadDto.SeriesId, MessageFactoryEntityTypes.Series, 0)); return Ok(); } @@ -580,6 +564,7 @@ namespace API.Controllers if (await _bookmarkService.BookmarkPage(user, bookmarkDto, path)) { + BackgroundJob.Enqueue(() => _cacheService.CleanupBookmarkCache(bookmarkDto.SeriesId)); return Ok(); } @@ -599,6 +584,7 @@ namespace API.Controllers if (await _bookmarkService.RemoveBookmarkPage(user, bookmarkDto)) { + BackgroundJob.Enqueue(() => _cacheService.CleanupBookmarkCache(bookmarkDto.SeriesId)); return Ok(); } diff --git a/API/Controllers/UploadController.cs b/API/Controllers/UploadController.cs index 98005e091..ca84acc8b 100644 --- a/API/Controllers/UploadController.cs +++ b/API/Controllers/UploadController.cs @@ -102,6 +102,8 @@ namespace API.Controllers if (_unitOfWork.HasChanges()) { + await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, + MessageFactory.CoverUpdateEvent(series.Id, MessageFactoryEntityTypes.Series), false); await _unitOfWork.CommitAsync(); return Ok(); } @@ -245,6 +247,10 @@ namespace API.Controllers if (_unitOfWork.HasChanges()) { await _unitOfWork.CommitAsync(); + await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, + MessageFactory.CoverUpdateEvent(chapter.VolumeId, MessageFactoryEntityTypes.Volume), false); + await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, + MessageFactory.CoverUpdateEvent(chapter.Id, MessageFactoryEntityTypes.Chapter), false); return Ok(); } diff --git a/API/Dockerfile b/API/Dockerfile deleted file mode 100644 index ce607a02f..000000000 --- a/API/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -#This Dockerfile pulls the latest git commit and builds Kavita from source -FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS builder - -ENV DEBIAN_FRONTEND=noninteractive -ARG TARGETPLATFORM - -#Installs nodejs and npm -RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \ - && apt-get install -y nodejs \ - && rm -rf /var/lib/apt/lists/* - -#Builds app based on platform -COPY build_target.sh /build_target.sh -RUN /build_target.sh - -#Production image -FROM ubuntu:focal - -#Move the output files to where they need to be -COPY --from=builder /Projects/Kavita/_output/build/Kavita /kavita - -#Installs program dependencies -RUN apt-get update \ - && apt-get install -y libicu-dev libssl1.1 pwgen \ - && rm -rf /var/lib/apt/lists/* - -#Creates the manga storage directory -RUN mkdir /manga /kavita/data - -RUN cp /kavita/appsettings.Development.json /kavita/appsettings.json \ - && sed -i 's/Data source=kavita.db/Data source=data\/kavita.db/g' /kavita/appsettings.json - -COPY entrypoint.sh /entrypoint.sh - -EXPOSE 5000 - -WORKDIR /kavita - -ENTRYPOINT ["/bin/bash"] -CMD ["/entrypoint.sh"] diff --git a/API/Services/CacheService.cs b/API/Services/CacheService.cs index 4dba47fc9..f55d74734 100644 --- a/API/Services/CacheService.cs +++ b/API/Services/CacheService.cs @@ -32,6 +32,7 @@ namespace API.Services string GetCachedEpubFile(int chapterId, Chapter chapter); public void ExtractChapterFiles(string extractPath, IReadOnlyList files); Task CacheBookmarkForSeries(int userId, int seriesId); + void CleanupBookmarkCache(int bookmarkDtoSeriesId); } public class CacheService : ICacheService { @@ -240,5 +241,17 @@ namespace API.Services _directoryService.Flatten(destDirectory); return files.Count; } + + /// + /// Clears a cached bookmarks for a series id folder + /// + /// + public void CleanupBookmarkCache(int seriesId) + { + var destDirectory = _directoryService.FileSystem.Path.Join(_directoryService.CacheDirectory, seriesId + "_bookmarks"); + if (!_directoryService.Exists(destDirectory)) return; + + _directoryService.ClearAndDeleteDirectory(destDirectory); + } } } diff --git a/API/Services/Tasks/CleanupService.cs b/API/Services/Tasks/CleanupService.cs index bbf81c9eb..647c0a066 100644 --- a/API/Services/Tasks/CleanupService.cs +++ b/API/Services/Tasks/CleanupService.cs @@ -182,7 +182,7 @@ namespace API.Services.Tasks /// public Task CleanupBookmarks() { - // This is disabled for now while we test and validate a new method of deleting bookmarks + // TODO: This is disabled for now while we test and validate a new method of deleting bookmarks return Task.CompletedTask; // Search all files in bookmarks/ except bookmark files and delete those // var bookmarkDirectory = diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 0bd8b458b..4bd37d009 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -215,12 +215,12 @@ public class ScannerService : IScannerService // That way logging and UI informing is all in one place with full context _logger.LogError("Some of the root folders for the library are empty. " + "Either your mount has been disconnected or you are trying to delete all series in the library. " + - "Scan will be aborted. " + + "Scan has be aborted. " + "Check that your mount is connected or change the library's root folder and rescan"); await _eventHub.SendMessageAsync(MessageFactory.Error, MessageFactory.ErrorEvent( $"Some of the root folders for the library, {libraryName}, are empty.", "Either your mount has been disconnected or you are trying to delete all series in the library. " + - "Scan will be aborted. " + + "Scan has be aborted. " + "Check that your mount is connected or change the library's root folder and rescan")); return false; diff --git a/Dockerfile b/Dockerfile index 11db76ef8..7233214f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ EXPOSE 5000 WORKDIR /kavita -HEALTHCHECK --interval=300s --timeout=15s --start-period=30s --retries=3 CMD curl --fail http://localhost:5000 || exit 1 +HEALTHCHECK --interval=30s --timeout=15s --start-period=30s --retries=3 CMD curl --fail http://localhost:5000 || exit 1 ENTRYPOINT [ "/bin/bash" ] CMD ["/entrypoint.sh"] diff --git a/UI/Web/src/app/_services/reading-list.service.ts b/UI/Web/src/app/_services/reading-list.service.ts index e2c5c6104..dae0708b6 100644 --- a/UI/Web/src/app/_services/reading-list.service.ts +++ b/UI/Web/src/app/_services/reading-list.service.ts @@ -2,6 +2,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { map } from 'rxjs/operators'; import { environment } from 'src/environments/environment'; +import { UtilityService } from '../shared/_services/utility.service'; import { PaginatedResult } from '../_models/pagination'; import { ReadingList, ReadingListItem } from '../_models/reading-list'; import { ActionItem } from './action-factory.service'; @@ -13,7 +14,7 @@ export class ReadingListService { baseUrl = environment.apiUrl; - constructor(private httpClient: HttpClient) { } + constructor(private httpClient: HttpClient, private utilityService: UtilityService) { } getReadingList(readingListId: number) { return this.httpClient.get(this.baseUrl + 'readinglist?readingListId=' + readingListId); @@ -21,11 +22,11 @@ export class ReadingListService { getReadingLists(includePromoted: boolean = true, pageNum?: number, itemsPerPage?: number) { let params = new HttpParams(); - params = this._addPaginationIfExists(params, pageNum, itemsPerPage); + params = this.utilityService.addPaginationIfExists(params, pageNum, itemsPerPage); return this.httpClient.post>(this.baseUrl + 'readinglist/lists?includePromoted=' + includePromoted, {}, {observe: 'response', params}).pipe( map((response: any) => { - return this._cachePaginatedResults(response, new PaginatedResult()); + return this.utilityService.createPaginatedResult(response, new PaginatedResult()); }) ); } @@ -86,29 +87,4 @@ export class ReadingListService { if (readingList?.promoted && !isAdmin) return false; return true; } - - _addPaginationIfExists(params: HttpParams, pageNum?: number, itemsPerPage?: number) { - // TODO: Move to utility service - if (pageNum !== null && pageNum !== undefined && itemsPerPage !== null && itemsPerPage !== undefined) { - params = params.append('pageNumber', pageNum + ''); - params = params.append('pageSize', itemsPerPage + ''); - } - return params; - } - - _cachePaginatedResults(response: any, paginatedVariable: PaginatedResult) { - // TODO: Move to utility service - if (response.body === null) { - paginatedVariable.result = []; - } else { - paginatedVariable.result = response.body; - } - - const pageHeader = response.headers.get('Pagination'); - if (pageHeader !== null) { - paginatedVariable.pagination = JSON.parse(pageHeader); - } - - return paginatedVariable; - } } diff --git a/UI/Web/src/app/_services/toggle.service.ts b/UI/Web/src/app/_services/toggle.service.ts index 5b9e90bd4..8b335394a 100644 --- a/UI/Web/src/app/_services/toggle.service.ts +++ b/UI/Web/src/app/_services/toggle.service.ts @@ -18,6 +18,7 @@ export class ToggleService { .pipe(filter(event => event instanceof NavigationStart)) .subscribe((event) => { this.toggleState = false; + this.toggleStateSource.next(this.toggleState); }); this.toggleStateSource.next(false); } @@ -26,7 +27,6 @@ export class ToggleService { this.toggleState = !this.toggleState; this.toggleStateSource.pipe(take(1)).subscribe(state => { this.toggleState = !state; - console.log('Toggle: ', this.toggleState) this.toggleStateSource.next(this.toggleState); }); diff --git a/UI/Web/src/app/book-reader/_models/book-black-theme.ts b/UI/Web/src/app/book-reader/_models/book-black-theme.ts index 1e89b49cf..cc588fb76 100644 --- a/UI/Web/src/app/book-reader/_models/book-black-theme.ts +++ b/UI/Web/src/app/book-reader/_models/book-black-theme.ts @@ -47,6 +47,15 @@ export const BookBlackTheme = ` --btn-disabled-text-color: white; --btn-disabled-border-color: #6c757d; + /* Inputs */ + --input-bg-color: #343a40; + --input-bg-readonly-color: #434648; + --input-focused-border-color: #ccc; + --input-text-color: #fff; + --input-placeholder-color: #aeaeae; + --input-border-color: #ccc; + --input-focus-boxshadow-color: rgb(255 255 255 / 50%); + /* Nav (Tabs) */ --nav-tab-border-color: rgba(44, 118, 88, 0.7); --nav-tab-text-color: var(--body-text-color); diff --git a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.html b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.html index d36c4bfcd..0de05a19b 100644 --- a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.html +++ b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.html @@ -94,7 +94,8 @@
- + Put reader in fullscreen mode diff --git a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.scss b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.scss index ed21da1eb..f379f66c3 100644 --- a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.scss +++ b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.scss @@ -1,6 +1,12 @@ .controls { margin: 0.25rem 0 0.25rem; + .form-select { + option{ + background-color: var(--input-bg-color); + } + } + .form-label { margin: 0; } diff --git a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.ts b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.ts index 5f5747643..8c4344d98 100644 --- a/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.ts +++ b/UI/Web/src/app/book-reader/reader-settings/reader-settings.component.ts @@ -284,6 +284,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy { } toggleFullscreen() { + this.isFullscreen = !this.isFullscreen; this.fullscreen.emit(); } } diff --git a/UI/Web/src/app/bookmark/bookmarks/bookmarks.component.ts b/UI/Web/src/app/bookmark/bookmarks/bookmarks.component.ts index e9c6b74c2..d8c939cc1 100644 --- a/UI/Web/src/app/bookmark/bookmarks/bookmarks.component.ts +++ b/UI/Web/src/app/bookmark/bookmarks/bookmarks.component.ts @@ -64,9 +64,7 @@ export class BookmarksComponent implements OnInit, OnDestroy { async handleAction(action: Action, series: Series) { switch (action) { case(Action.Delete): - if (!await this.confirmService.confirm('Are you sure you want to clear all bookmarks for ' + series.name + '? This cannot be undone.')) { - break; - } + this.clearBookmarks(series); break; case(Action.DownloadBookmark): this.downloadBookmarks(series); diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html index 8f5fd60df..0fa6d0888 100644 --- a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html +++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html @@ -30,7 +30,7 @@
- +
diff --git a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.scss b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.scss index 714c4bb19..edbf58368 100644 --- a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.scss +++ b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.scss @@ -10,8 +10,4 @@ .highlight { color: var(--bulk-selection-highlight-text-color) !important; -} - -::ng-deep button i.fa { - color: var(--bulk-selection-text-color); } \ No newline at end of file diff --git a/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts b/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts index 381abbb1e..59b25a142 100644 --- a/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts +++ b/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts @@ -55,6 +55,8 @@ export class EditSeriesRelationComponent implements OnInit, OnDestroy { this.setupRelationRows(relations.alternativeSettings, RelationKind.AlternativeSetting); this.setupRelationRows(relations.alternativeVersions, RelationKind.AlternativeVersion); this.setupRelationRows(relations.doujinshis, RelationKind.Doujinshi); + this.setupRelationRows(relations.contains, RelationKind.Contains); + this.setupRelationRows(relations.parent, RelationKind.Parent); }); this.libraryService.getLibraryNames().subscribe(names => { diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/manga-reader.component.html index fa9e7ed88..655d2f9a7 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/manga-reader.component.html @@ -31,9 +31,9 @@
-
+
-
+
@@ -47,7 +47,7 @@ title="Previous Page" aria-hidden="true">
-
+
diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.scss b/UI/Web/src/app/manga-reader/manga-reader.component.scss index 6eac3250e..c5beaccc9 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.scss +++ b/UI/Web/src/app/manga-reader/manga-reader.component.scss @@ -20,7 +20,7 @@ img { .reading-area { position: relative; overflow: auto; - height: calc(var(--vh)*100); + //height: calc(var(--vh)*100); // this needs to be applied on the DOM because it breaks infinite scroller } .image-container { @@ -248,6 +248,9 @@ img { width: 100%; } + //$pagination-bg: rgba(0, 0, 0, 0); + $pagination-bg: rgba(0, 0, 255, 0.4); + .pagination-area { cursor: pointer; z-index: 2; @@ -262,7 +265,7 @@ img { right: 0px; top: 0px; width: $side-width; - background: rgba(0, 0, 0, 0); + background: $pagination-bg; } .top { @@ -270,7 +273,7 @@ img { right: 0px; top: 0px; width: 100%; - background: rgba(0, 0, 0, 0); + background: $pagination-bg; } .left { @@ -278,7 +281,7 @@ img { left: 0px; top: 0px; width: $side-width; - background: rgba(0, 0, 0, 0); + background: $pagination-bg; } .bottom { @@ -286,7 +289,7 @@ img { left: 0px; bottom: 0px; width: 100%; - background: rgba(0, 0, 0, 0); + background: $pagination-bg; } } diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/manga-reader.component.ts index 989628d95..130ea87be 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/manga-reader.component.ts @@ -283,12 +283,24 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { return this.bookmarks.hasOwnProperty(this.pageNum); } + get WindowWidth() { + return this.readingArea?.nativeElement.scrollWidth + 'px'; + } + get WindowHeight() { return this.readingArea?.nativeElement.scrollHeight + 'px'; } + get ImageWidth() { + return this.image?.nativeElement.width + 'px'; + } + get ImageHeight() { - return this.image?.nativeElement.height + 'px'; + // If we are a cover image and implied fit to screen, then we need to take screen height rather than image height + if (this.isCoverImage()) { + return this.WindowHeight; + } + return this.image?.nativeElement.height + 'px'; } get splitIconClass() { @@ -1018,7 +1030,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { if (!this.ctx || !this.canvas) { return; } this.canvasImage.onload = null; - console.log('canvasImage: ', this.canvasImage?.height); this.setCanvasSize(); @@ -1055,8 +1066,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { || document.documentElement.clientHeight || document.body.clientHeight; - console.log(windowHeight); - const needsSplitting = this.isCoverImage(); let newScale = this.generalSettingsForm.get('fittingOption')?.value; const widthRatio = windowWidth / (this.canvasImage.width / (needsSplitting ? 2 : 1)); diff --git a/UI/Web/src/app/pipe/relationship.pipe.ts b/UI/Web/src/app/pipe/relationship.pipe.ts index 0aa84782b..f1778a5a6 100644 --- a/UI/Web/src/app/pipe/relationship.pipe.ts +++ b/UI/Web/src/app/pipe/relationship.pipe.ts @@ -10,7 +10,7 @@ export class RelationshipPipe implements PipeTransform { if (relationship === undefined) return ''; switch (relationship) { case RelationKind.Adaptation: - return 'Adaptaion'; + return 'Adaptation'; case RelationKind.AlternativeSetting: return 'Alternative Setting'; case RelationKind.AlternativeVersion: diff --git a/UI/Web/src/app/reading-list/reading-list-detail/reading-list-detail.component.html b/UI/Web/src/app/reading-list/reading-list-detail/reading-list-detail.component.html index f02d5d49d..c961560c9 100644 --- a/UI/Web/src/app/reading-list/reading-list-detail/reading-list-detail.component.html +++ b/UI/Web/src/app/reading-list/reading-list-detail/reading-list-detail.component.html @@ -20,7 +20,7 @@
@@ -47,7 +47,7 @@
- No chapters added + Nothing added
diff --git a/UI/Web/src/app/series-detail/series-detail.component.html b/UI/Web/src/app/series-detail/series-detail.component.html index 61015071a..22574502f 100644 --- a/UI/Web/src/app/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/series-detail.component.html @@ -132,9 +132,9 @@
  • Related -
    +
    - +
    diff --git a/UI/Web/src/app/shared/image/image.component.ts b/UI/Web/src/app/shared/image/image.component.ts index 4d4e50204..d19d661da 100644 --- a/UI/Web/src/app/shared/image/image.component.ts +++ b/UI/Web/src/app/shared/image/image.component.ts @@ -51,10 +51,13 @@ export class ImageComponent implements OnChanges, OnDestroy { if (this.imageUrl === undefined || this.imageUrl === null || this.imageUrl === '') return; const enityType = this.imageService.getEntityTypeFromUrl(this.imageUrl); if (enityType === updateEvent.entityType) { - const tokens = this.imageUrl.split('?')[1].split('&random='); + const tokens = this.imageUrl.split('?')[1].split('&'); //...seriesId=123&random= - const id = tokens[0].replace(enityType + 'Id=', ''); + let id = tokens[0].replace(enityType + 'Id=', ''); + if (id.includes('&')) { + id = id.split('&')[0]; + } if (id === (updateEvent.id + '')) { this.imageUrl = this.imageService.randomize(this.imageUrl); } diff --git a/UI/Web/src/app/typeahead/typeahead.component.ts b/UI/Web/src/app/typeahead/typeahead.component.ts index 229774a5f..7190864ef 100644 --- a/UI/Web/src/app/typeahead/typeahead.component.ts +++ b/UI/Web/src/app/typeahead/typeahead.component.ts @@ -217,7 +217,7 @@ export class TypeaheadComponent implements OnInit, OnDestroy { }), map(val => val.trim()), auditTime(this.settings.debounce), - distinctUntilChanged(), // ?!: BUG Doesn't trigger the search to run when filtered array changes + //distinctUntilChanged(), // ?!: BUG Doesn't trigger the search to run when filtered array changes filter(val => { // If minimum filter characters not met, do not filter if (this.settings.minCharacters === 0) return true; @@ -405,6 +405,7 @@ export class TypeaheadComponent implements OnInit, OnDestroy { openDropdown() { setTimeout(() => { this.typeaheadControl.setValue(this.typeaheadControl.value); + this.hasFocus = true; }); } @@ -454,6 +455,7 @@ export class TypeaheadComponent implements OnInit, OnDestroy { } updateShowAddItem(options: any[]) { + // ?! BUG This will still technicially allow you to add the same thing as a previously added item. (Code will just toggle it though) this.showAddItem = this.settings.addIfNonExisting && this.typeaheadControl.value.trim() && this.typeaheadControl.value.trim().length >= Math.max(this.settings.minCharacters, 1) && this.typeaheadControl.dirty