diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 7fc8769e7..dfa30b18d 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -525,6 +525,12 @@ namespace API.Controllers return Ok("An email will be sent to the email if it exists in our database"); } + var roles = await _userManager.GetRolesAsync(user); + + + if (!roles.Any(r => r is PolicyConstants.AdminRole or PolicyConstants.ChangePasswordRole)) + return Unauthorized("You are not permitted to this operation."); + var emailLink = GenerateEmailLink(await _userManager.GeneratePasswordResetTokenAsync(user), "confirm-reset-password", user.Email); _logger.LogCritical("[Forgot Password]: Email Link for {UserName}: {Link}", user.UserName, emailLink); var host = _environment.IsDevelopment() ? "localhost:4200" : Request.Host.ToString(); diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs index a5c2ae676..85880a38d 100644 --- a/API/Controllers/LibraryController.cs +++ b/API/Controllers/LibraryController.cs @@ -197,6 +197,12 @@ namespace API.Controllers _taskScheduler.CleanupChapters(chapterIds); } + foreach (var seriesId in seriesIds) + { + await _eventHub.SendMessageAsync(MessageFactory.SeriesRemoved, + MessageFactory.SeriesRemovedEvent(seriesId, string.Empty, libraryId), false); + } + await _eventHub.SendMessageAsync(MessageFactory.LibraryModified, MessageFactory.LibraryModifiedEvent(libraryId, "delete"), false); return Ok(true); diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index 1e6d381a1..91607db7d 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -618,6 +618,12 @@ public class OpdsController : BaseApiController { if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds) return BadRequest("OPDS is not enabled on this server"); + var user = await _unitOfWork.UserRepository.GetUserByIdAsync(await GetUser(apiKey)); + if (!await _downloadService.HasDownloadPermission(user)) + { + return BadRequest("User does not have download permissions"); + } + var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId); var (bytes, contentType, fileDownloadName) = await _downloadService.GetFirstFileDownload(files); return File(bytes, contentType, fileDownloadName); @@ -776,6 +782,7 @@ public class OpdsController : BaseApiController { CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"), CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"), + accLink, CreatePageStreamLink(seriesId, volumeId, chapterId, mangaFile, apiKey) }, Content = new FeedEntryContent() @@ -785,11 +792,12 @@ public class OpdsController : BaseApiController } }; - var user = await _unitOfWork.UserRepository.GetUserByIdAsync(await GetUser(apiKey)); - if (await _downloadService.HasDownloadPermission(user)) - { - entry.Links.Add(accLink); - } + // We can't not show acc link in the feed, panels wont work like that. We have to block download directly + // var user = await _unitOfWork.UserRepository.GetUserByIdAsync(await GetUser(apiKey)); + // if (await _downloadService.HasDownloadPermission(user)) + // { + // entry.Links.Add(accLink); + // } return entry; diff --git a/API/Controllers/UploadController.cs b/API/Controllers/UploadController.cs index 3dd23db1a..c0cc46e3e 100644 --- a/API/Controllers/UploadController.cs +++ b/API/Controllers/UploadController.cs @@ -47,12 +47,24 @@ namespace API.Controllers { var dateString = $"{DateTime.Now.ToShortDateString()}_{DateTime.Now.ToLongTimeString()}".Replace("/", "_").Replace(":", "_"); var format = _directoryService.FileSystem.Path.GetExtension(dto.Url.Split('?')[0]).Replace(".", ""); - var path = await dto.Url - .DownloadFileAsync(_directoryService.TempDirectory, $"coverupload_{dateString}.{format}"); + try + { + var path = await dto.Url + .DownloadFileAsync(_directoryService.TempDirectory, $"coverupload_{dateString}.{format}"); - if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"Could not download file"); + if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) + return BadRequest($"Could not download file"); - return $"coverupload_{dateString}.{format}"; + return $"coverupload_{dateString}.{format}"; + } + catch (FlurlHttpException ex) + { + // Unauthorized + if (ex.StatusCode == 401) + return BadRequest("The server requires authentication to load the url externally"); + } + + return BadRequest("Unable to download image, please use another url or upload by file"); } /// diff --git a/API/Data/Metadata/ComicInfo.cs b/API/Data/Metadata/ComicInfo.cs index f2f3734a4..040d7f6b7 100644 --- a/API/Data/Metadata/ComicInfo.cs +++ b/API/Data/Metadata/ComicInfo.cs @@ -14,6 +14,7 @@ namespace API.Data.Metadata public string Summary { get; set; } = string.Empty; public string Title { get; set; } = string.Empty; public string Series { get; set; } = string.Empty; + public string SeriesSort { get; set; } = string.Empty; public string Number { get; set; } = string.Empty; /// /// The total number of items in the series. diff --git a/API/Data/Repositories/LibraryRepository.cs b/API/Data/Repositories/LibraryRepository.cs index dd4d5afa9..d78c5a95d 100644 --- a/API/Data/Repositories/LibraryRepository.cs +++ b/API/Data/Repositories/LibraryRepository.cs @@ -89,6 +89,7 @@ public class LibraryRepository : ILibraryRepository { var library = await GetLibraryForIdAsync(libraryId, LibraryIncludes.Folders | LibraryIncludes.Series); _context.Library.Remove(library); + return await _context.SaveChangesAsync() > 0; } diff --git a/API/Data/Repositories/SeriesRepository.cs b/API/Data/Repositories/SeriesRepository.cs index 3e93abda3..ef97dfc87 100644 --- a/API/Data/Repositories/SeriesRepository.cs +++ b/API/Data/Repositories/SeriesRepository.cs @@ -624,7 +624,7 @@ public class SeriesRepository : ISeriesRepository LastReadingProgress = _context.AppUserProgresses .Where(p => p.Id == progress.Id && p.AppUserId == userId) .Max(p => p.LastModified), - // This is only taking into account chapters that have progress on them, not all chapters in said series + // BUG: This is only taking into account chapters that have progress on them, not all chapters in said series LastChapterCreated = _context.Chapter.Where(c => progress.ChapterId == c.Id).Max(c => c.Created), //LastChapterCreated = _context.Chapter.Where(c => allChapters.Contains(c.Id)).Max(c => c.Created) }); diff --git a/API/Extensions/IdentityServiceExtensions.cs b/API/Extensions/IdentityServiceExtensions.cs index 16404949b..043e5c919 100644 --- a/API/Extensions/IdentityServiceExtensions.cs +++ b/API/Extensions/IdentityServiceExtensions.cs @@ -15,6 +15,12 @@ namespace API.Extensions { public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config) { + services.Configure(options => + { + options.User.AllowedUserNameCharacters = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+/"; + }); + services.AddIdentityCore(opt => { opt.Password.RequireNonAlphanumeric = false; diff --git a/API/Program.cs b/API/Program.cs index 07d596ff0..3bd895353 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -72,9 +72,8 @@ namespace API } await context.Database.MigrateAsync(); - var roleManager = services.GetRequiredService>(); - await Seed.SeedRoles(roleManager); + await Seed.SeedRoles(services.GetRequiredService>()); await Seed.SeedSettings(context, directoryService); await Seed.SeedThemes(context); await Seed.SeedUserApiKeys(context); @@ -110,7 +109,7 @@ namespace API (await context.ServerSetting.SingleOrDefaultAsync(s => s.Key == ServerSettingKey.InstallVersion))?.Value; } - catch + catch (Exception) { // ignored } diff --git a/API/Services/SeriesService.cs b/API/Services/SeriesService.cs index 3f82af55d..3146ce6dc 100644 --- a/API/Services/SeriesService.cs +++ b/API/Services/SeriesService.cs @@ -164,7 +164,7 @@ public class SeriesService : ISeriesService } await _eventHub.SendMessageAsync(MessageFactory.ScanSeries, - MessageFactory.ScanSeriesEvent(series.Id, series.Name), false); + MessageFactory.ScanSeriesEvent(series.LibraryId, series.Id, series.Name), false); await _unitOfWork.CollectionTagRepository.RemoveTagsWithoutSeries(); diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index 35dc332c6..d749c20ca 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -15,7 +15,7 @@ public interface ITaskScheduler Task ScheduleTasks(); Task ScheduleStatsTasks(); void ScheduleUpdaterTasks(); - void ScanLibrary(int libraryId, bool forceUpdate = false); + void ScanLibrary(int libraryId); void CleanupChapters(int[] chapterIds); void RefreshMetadata(int libraryId, bool forceUpdate = true); void RefreshSeriesMetadata(int libraryId, int seriesId, bool forceUpdate = false); @@ -146,7 +146,7 @@ public class TaskScheduler : ITaskScheduler } #endregion - public void ScanLibrary(int libraryId, bool forceUpdate = false) + public void ScanLibrary(int libraryId) { _logger.LogInformation("Enqueuing library scan for: {LibraryId}", libraryId); BackgroundJob.Enqueue(() => _scannerService.ScanLibrary(libraryId)); diff --git a/API/Services/Tasks/Scanner/ParseScannedFiles.cs b/API/Services/Tasks/Scanner/ParseScannedFiles.cs index d9128fbe5..ddefd00e3 100644 --- a/API/Services/Tasks/Scanner/ParseScannedFiles.cs +++ b/API/Services/Tasks/Scanner/ParseScannedFiles.cs @@ -117,10 +117,15 @@ namespace API.Services.Tasks.Scanner } // Patch is SeriesSort from ComicInfo - if (info.ComicInfo != null && !string.IsNullOrEmpty(info.ComicInfo.TitleSort)) + if (!string.IsNullOrEmpty(info.ComicInfo.TitleSort)) { info.SeriesSort = info.ComicInfo.TitleSort; } + + if (!string.IsNullOrEmpty(info.ComicInfo.SeriesSort)) + { + info.SeriesSort = info.ComicInfo.SeriesSort; + } } TrackSeries(info); diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 78218293b..08c13e338 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -162,7 +162,7 @@ public class ScannerService : IScannerService } // Tell UI that this series is done await _eventHub.SendMessageAsync(MessageFactory.ScanSeries, - MessageFactory.ScanSeriesEvent(seriesId, series.Name)); + MessageFactory.ScanSeriesEvent(libraryId, seriesId, series.Name)); await CleanupDbEntities(); BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); BackgroundJob.Enqueue(() => _metadataService.RefreshMetadataForSeries(libraryId, series.Id, false)); @@ -428,7 +428,7 @@ public class ScannerService : IScannerService foreach (var series in librarySeries) { // This is something more like, the series has finished updating in the backend. It may or may not have been modified. - await _eventHub.SendMessageAsync(MessageFactory.ScanSeries, MessageFactory.ScanSeriesEvent(series.Id, series.Name)); + await _eventHub.SendMessageAsync(MessageFactory.ScanSeries, MessageFactory.ScanSeriesEvent(library.Id, series.Id, series.Name)); } } @@ -523,7 +523,7 @@ public class ScannerService : IScannerService series.Format = parsedInfos[0].Format; } series.OriginalName ??= parsedInfos[0].Series; - if (!series.SortNameLocked) series.SortName ??= parsedInfos[0].SeriesSort; + if (!series.SortNameLocked) series.SortName = parsedInfos[0].SeriesSort; await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.LibraryScanProgressEvent(library.Name, ProgressEventType.Ended, series.Name)); diff --git a/API/Services/Tasks/SiteThemeService.cs b/API/Services/Tasks/SiteThemeService.cs index 4a579434f..e0e1bc2d8 100644 --- a/API/Services/Tasks/SiteThemeService.cs +++ b/API/Services/Tasks/SiteThemeService.cs @@ -100,6 +100,21 @@ public class SiteThemeService : ISiteThemeService await _unitOfWork.CommitAsync(); } + // if there are no default themes, reselect Dark as default + var postSaveThemes = (await _unitOfWork.SiteThemeRepository.GetThemes()).ToList(); + if (!postSaveThemes.Any(t => t.IsDefault)) + { + var defaultThemeName = Seed.DefaultThemes.Single(t => t.IsDefault).NormalizedName; + var theme = postSaveThemes.SingleOrDefault(t => t.NormalizedName == defaultThemeName); + if (theme != null) + { + theme.IsDefault = true; + _unitOfWork.SiteThemeRepository.Update(theme); + await _unitOfWork.CommitAsync(); + } + + } + await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.SiteThemeProgressEvent("", "", ProgressEventType.Ended)); diff --git a/API/Services/Tasks/StatsService.cs b/API/Services/Tasks/StatsService.cs index faf0414b0..831e8f3a0 100644 --- a/API/Services/Tasks/StatsService.cs +++ b/API/Services/Tasks/StatsService.cs @@ -102,11 +102,6 @@ public class StatsService : IStatsService var installId = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId); var installVersion = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion); - var firstAdminUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).First(); - var firstAdminUserPref = (await _unitOfWork.UserRepository.GetPreferencesAsync(firstAdminUser.UserName)); - - var activeTheme = firstAdminUserPref.Theme ?? Seed.DefaultThemes.First(t => t.IsDefault); - var serverInfo = new ServerInfoDto { InstallId = installId.Value, @@ -117,15 +112,24 @@ public class StatsService : IStatsService NumOfCores = Math.Max(Environment.ProcessorCount, 1), HasBookmarks = (await _unitOfWork.UserRepository.GetAllBookmarksAsync()).Any(), NumberOfLibraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).Count(), - ActiveSiteTheme = activeTheme.Name, NumberOfCollections = (await _unitOfWork.CollectionTagRepository.GetAllTagsAsync()).Count(), NumberOfReadingLists = await _unitOfWork.ReadingListRepository.Count(), OPDSEnabled = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds, NumberOfUsers = (await _unitOfWork.UserRepository.GetAllUsers()).Count(), TotalFiles = await _unitOfWork.LibraryRepository.GetTotalFiles(), - MangaReaderMode = firstAdminUserPref.ReaderMode }; + var firstAdminUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).FirstOrDefault(); + + if (firstAdminUser != null) + { + var firstAdminUserPref = (await _unitOfWork.UserRepository.GetPreferencesAsync(firstAdminUser.UserName)); + var activeTheme = firstAdminUserPref.Theme ?? Seed.DefaultThemes.First(t => t.IsDefault); + + serverInfo.ActiveSiteTheme = activeTheme.Name; + serverInfo.MangaReaderMode = firstAdminUserPref.ReaderMode; + } + return serverInfo; } } diff --git a/API/SignalR/MessageFactory.cs b/API/SignalR/MessageFactory.cs index 6fc6560c2..485a2fc37 100644 --- a/API/SignalR/MessageFactory.cs +++ b/API/SignalR/MessageFactory.cs @@ -80,13 +80,15 @@ namespace API.SignalR public const string LibraryModified = "LibraryModified"; - public static SignalRMessage ScanSeriesEvent(int seriesId, string seriesName) + public static SignalRMessage ScanSeriesEvent(int libraryId, int seriesId, string seriesName) { return new SignalRMessage() { Name = ScanSeries, + EventType = ProgressEventType.Single, Body = new { + LibraryId = libraryId, SeriesId = seriesId, SeriesName = seriesName } diff --git a/API/Startup.cs b/API/Startup.cs index c9cbb32c5..2f8ac4d1a 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -120,6 +120,7 @@ namespace API ForwardedHeaders.All; }); + services.AddHangfire(configuration => configuration .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() @@ -149,7 +150,6 @@ namespace API // Apply all migrations on startup var logger = serviceProvider.GetRequiredService>(); var userManager = serviceProvider.GetRequiredService>(); - var context = serviceProvider.GetRequiredService(); await MigrateBookmarks.Migrate(directoryService, unitOfWork, logger, cacheService); @@ -161,6 +161,7 @@ namespace API var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion); installVersion.Value = BuildInfo.Version.ToString(); unitOfWork.SettingsRepository.Update(installVersion); + await unitOfWork.CommitAsync(); }).GetAwaiter() .GetResult(); diff --git a/UI/Web/src/app/_modals/review-series-modal/review-series-modal.component.html b/UI/Web/src/app/_modals/review-series-modal/review-series-modal.component.html index cfb00e0a8..f64ef951f 100644 --- a/UI/Web/src/app/_modals/review-series-modal/review-series-modal.component.html +++ b/UI/Web/src/app/_modals/review-series-modal/review-series-modal.component.html @@ -12,7 +12,7 @@
- +
diff --git a/UI/Web/src/app/_models/events/scan-series-event.ts b/UI/Web/src/app/_models/events/scan-series-event.ts index f60d82e17..65199aca4 100644 --- a/UI/Web/src/app/_models/events/scan-series-event.ts +++ b/UI/Web/src/app/_models/events/scan-series-event.ts @@ -1,4 +1,5 @@ export interface ScanSeriesEvent { + libraryId: number; seriesId: number; seriesName: string; } \ No newline at end of file diff --git a/UI/Web/src/app/_services/reading-list.service.ts b/UI/Web/src/app/_services/reading-list.service.ts index 45bc86828..e2c5c6104 100644 --- a/UI/Web/src/app/_services/reading-list.service.ts +++ b/UI/Web/src/app/_services/reading-list.service.ts @@ -47,7 +47,7 @@ export class ReadingListService { } updateByMultiple(readingListId: number, seriesId: number, volumeIds: Array, chapterIds?: Array) { - return this.httpClient.post(this.baseUrl + 'readinglist/update-by-multiple', {readingListId, seriesId, volumeIds, chapterIds}); + return this.httpClient.post(this.baseUrl + 'readinglist/update-by-multiple', {readingListId, seriesId, volumeIds, chapterIds}, { responseType: 'text' as 'json' }); } updateByMultipleSeries(readingListId: number, seriesIds: Array) { diff --git a/UI/Web/src/app/_services/series.service.ts b/UI/Web/src/app/_services/series.service.ts index ed6be1b89..e6c8e1cf2 100644 --- a/UI/Web/src/app/_services/series.service.ts +++ b/UI/Web/src/app/_services/series.service.ts @@ -155,7 +155,6 @@ export class SeriesService { } scan(libraryId: number, seriesId: number) { - // TODO: Pipe and put a toaster up: this.toastr.info('Scan queued for ' + series.name); return this.httpClient.post(this.baseUrl + 'series/scan', {libraryId: libraryId, seriesId: seriesId}); } diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.html b/UI/Web/src/app/admin/manage-library/manage-library.component.html index 2fb30357a..7cde9794d 100644 --- a/UI/Web/src/app/admin/manage-library/manage-library.component.html +++ b/UI/Web/src/app/admin/manage-library/manage-library.component.html @@ -1,16 +1,16 @@

Libraries

-
+
  • {{library.name}}  -
    +
    diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.ts b/UI/Web/src/app/admin/manage-library/manage-library.component.ts index 698013e2f..19a314396 100644 --- a/UI/Web/src/app/admin/manage-library/manage-library.component.ts +++ b/UI/Web/src/app/admin/manage-library/manage-library.component.ts @@ -2,10 +2,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ToastrService } from 'ngx-toastr'; import { Subject } from 'rxjs'; -import { take, takeUntil, takeWhile } from 'rxjs/operators'; +import { distinctUntilChanged, filter, take, takeLast, takeUntil } from 'rxjs/operators'; import { ConfirmService } from 'src/app/shared/confirm.service'; import { NotificationProgressEvent } from 'src/app/_models/events/notification-progress-event'; -import { ProgressEvent } from 'src/app/_models/events/progress-event'; +import { ScanSeriesEvent } from 'src/app/_models/events/scan-series-event'; import { Library, LibraryType } from 'src/app/_models/library'; import { LibraryService } from 'src/app/_services/library.service'; import { EVENTS, Message, MessageHubService } from 'src/app/_services/message-hub.service'; @@ -25,7 +25,6 @@ export class ManageLibraryComponent implements OnInit, OnDestroy { * If a deletion is in progress for a library */ deletionInProgress: boolean = false; - scanInProgress: {[key: number]: {progress: boolean, timestamp?: string}} = {}; libraryTrackBy = (index: number, item: Library) => `${item.name}_${item.lastScanned}_${item.type}_${item.folders.length}`; private readonly onDestroy = new Subject(); @@ -38,29 +37,29 @@ export class ManageLibraryComponent implements OnInit, OnDestroy { this.getLibraries(); // when a progress event comes in, show it on the UI next to library - this.hubService.messages$.pipe(takeUntil(this.onDestroy), takeWhile(event => event.event === EVENTS.NotificationProgress)) - .subscribe((event: Message) => { - if (event.event !== EVENTS.NotificationProgress && (event.payload as NotificationProgressEvent).name === EVENTS.ScanSeries) return; + this.hubService.messages$.pipe(takeUntil(this.onDestroy), + filter(event => event.event === EVENTS.ScanSeries || event.event === EVENTS.NotificationProgress), + distinctUntilChanged((prev: Message, curr: Message) => + this.hasMessageChanged(prev, curr))) + .subscribe((event: Message) => { + console.log('scan event: ', event); + + let libId = 0; + if (event.event === EVENTS.ScanSeries) { + libId = (event.payload as ScanSeriesEvent).libraryId; + } else { + if ((event.payload as NotificationProgressEvent).body.hasOwnProperty('libraryId')) { + libId = (event.payload as NotificationProgressEvent).body.libraryId; + } + } - console.log('scan event: ', event.payload); - // TODO: Refactor this to use EventyType on NotificationProgress interface rather than float comparison - - const scanEvent = event.payload.body as ProgressEvent; - this.scanInProgress[scanEvent.libraryId] = {progress: scanEvent.progress !== 1}; - if (scanEvent.progress === 0) { - this.scanInProgress[scanEvent.libraryId].timestamp = scanEvent.eventTime; - } - - if (this.scanInProgress[scanEvent.libraryId].progress === false && (scanEvent.progress === 1 || event.payload.eventType === 'ended')) { this.libraryService.getLibraries().pipe(take(1)).subscribe(libraries => { - const newLibrary = libraries.find(lib => lib.id === scanEvent.libraryId); - const existingLibrary = this.libraries.find(lib => lib.id === scanEvent.libraryId); + const newLibrary = libraries.find(lib => lib.id === libId); + const existingLibrary = this.libraries.find(lib => lib.id === libId); if (existingLibrary !== undefined) { existingLibrary.lastScanned = newLibrary?.lastScanned || existingLibrary.lastScanned; } }); - } - }); } @@ -69,6 +68,17 @@ export class ManageLibraryComponent implements OnInit, OnDestroy { this.onDestroy.complete(); } + hasMessageChanged(prev: Message, curr: Message) { + if (curr.event !== prev.event) return true; + if (curr.event === EVENTS.ScanSeries) { + return (prev.payload as ScanSeriesEvent).libraryId === (curr.payload as ScanSeriesEvent).libraryId; + } + if (curr.event === EVENTS.NotificationProgress) { + return (prev.payload as NotificationProgressEvent).eventType != (curr.payload as NotificationProgressEvent).eventType; + } + return false; + } + getLibraries() { this.loading = true; this.libraryService.getLibraries().pipe(take(1)).subscribe(libraries => { diff --git a/UI/Web/src/app/admin/manage-users/manage-users.component.ts b/UI/Web/src/app/admin/manage-users/manage-users.component.ts index 454e9786e..acae4b78c 100644 --- a/UI/Web/src/app/admin/manage-users/manage-users.component.ts +++ b/UI/Web/src/app/admin/manage-users/manage-users.component.ts @@ -74,6 +74,7 @@ export class ManageUsersComponent implements OnInit, OnDestroy { } loadPendingInvites() { + this.pendingInvites = []; this.memberService.getPendingInvites().subscribe(members => { this.pendingInvites = members; // Show logged in user at the top of the list @@ -116,9 +117,7 @@ export class ManageUsersComponent implements OnInit, OnDestroy { inviteUser() { const modalRef = this.modalService.open(InviteUserComponent, {size: 'lg'}); modalRef.closed.subscribe((successful: boolean) => { - if (successful) { - this.loadPendingInvites(); - } + this.loadPendingInvites(); }); } diff --git a/UI/Web/src/app/all-series/all-series.component.html b/UI/Web/src/app/all-series/all-series.component.html index e10933be9..b47e3258b 100644 --- a/UI/Web/src/app/all-series/all-series.component.html +++ b/UI/Web/src/app/all-series/all-series.component.html @@ -1,4 +1,4 @@ - +

    All Series diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.html b/UI/Web/src/app/book-reader/book-reader/book-reader.component.html index 75b3fd9dc..22356d552 100644 --- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.html +++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.html @@ -41,7 +41,7 @@

    - +
    @@ -52,7 +52,7 @@ The ability to click the sides of the page to page left and right The ability to click the sides of the page to page left and right - +
    @@ -70,13 +70,13 @@
    - -
    {{pageNum}}
    -
    + +
    {{pageNum}}
    +
    -
    {{maxPages - 1}}
    - +
    {{maxPages - 1}}
    +

    Table of Contents

    @@ -127,11 +127,11 @@ [disabled]="IsPrevDisabled" title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page"> -  {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} +  {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} - - -
    + + +
    Loading book... @@ -142,12 +142,12 @@ (Incognito Mode)
    - +
    diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts index 395ce39fb..e8657e2aa 100644 --- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts +++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts @@ -1013,8 +1013,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { } if (element === null) return; - - this.scrollService.scrollTo(element.getBoundingClientRect().top + window.pageYOffset + TOP_OFFSET, this.reader.nativeElement); + const fromTopOffset = element.getBoundingClientRect().top + window.pageYOffset + TOP_OFFSET; + // We need to use a delay as webkit browsers (aka apple devices) don't always have the document rendered by this point + setTimeout(() => this.scrollService.scrollTo(fromTopOffset, this.reader.nativeElement), 10); } toggleClickToPaginate() { diff --git a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html index a3cb6dfdd..3bab2708c 100644 --- a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html +++ b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html @@ -96,6 +96,9 @@ + + No bookmarks yet +

  • diff --git a/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.html b/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.html index 349f2e746..32d2617d5 100644 --- a/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.html +++ b/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.html @@ -8,7 +8,7 @@ No metadata available
    -
    +
    Writers
    @@ -17,7 +17,7 @@
    -
    +
    Cover Artists
    @@ -26,7 +26,7 @@
    -
    +
    Pencillers
    @@ -35,7 +35,7 @@
    -
    +
    Inkers
    @@ -44,7 +44,7 @@
    -
    +
    Colorists
    @@ -54,7 +54,7 @@
    -
    +
    Letterers
    @@ -63,7 +63,7 @@
    -
    +
    Editors
    @@ -72,7 +72,7 @@
    -
    +
    Publishers
    @@ -81,7 +81,7 @@
    -
    +
    Characters
    @@ -89,7 +89,7 @@
    -
    +
    Translators
    diff --git a/UI/Web/src/app/collections/collection-detail/collection-detail.component.html b/UI/Web/src/app/collections/collection-detail/collection-detail.component.html index 9304abcb0..ad90f0c00 100644 --- a/UI/Web/src/app/collections/collection-detail/collection-detail.component.html +++ b/UI/Web/src/app/collections/collection-detail/collection-detail.component.html @@ -1,17 +1,17 @@ - + -

    +

    - {{collectionTag.title}} + {{collectionTag.title}} ()

    -
    +
    -
    +
    -
    +
    @@ -24,6 +24,7 @@ [items]="series" [pagination]="seriesPagination" [filterSettings]="filterSettings" + [filterOpen]="filterOpen" (pageChange)="onPageChange($event)" (applyFilter)="updateFilter($event)" > diff --git a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts index 27d302bd7..0a8e768d2 100644 --- a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts +++ b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; +import { Component, EventEmitter, HostListener, OnDestroy, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { Router, ActivatedRoute } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -43,6 +43,8 @@ export class CollectionDetailComponent implements OnInit, OnDestroy { summary: string = ''; actionInProgress: boolean = false; + + filterOpen: EventEmitter = new EventEmitter(); private onDestory: Subject = new Subject(); @@ -102,6 +104,7 @@ export class CollectionDetailComponent implements OnInit, OnDestroy { return; } const tagId = parseInt(routeId, 10); + this.seriesPagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1}; [this.filterSettings.presets, this.filterSettings.openByDefault] = this.utilityService.filterPresetsFromUrl(this.route.snapshot, this.seriesService.createSeriesFilter()); this.filterSettings.presets.collectionTags = [tagId]; @@ -161,16 +164,22 @@ export class CollectionDetailComponent implements OnInit, OnDestroy { onPageChange(pagination: Pagination) { this.router.navigate(['collections', this.collectionTag.id], {replaceUrl: true, queryParamsHandling: 'merge', queryParams: {page: this.seriesPagination.currentPage} }); + this.loadPage(); } loadPage() { - const page = this.route.snapshot.queryParamMap.get('page'); + const page = this.getPage(); if (page != null) { - if (this.seriesPagination === undefined || this.seriesPagination === null) { - this.seriesPagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1}; - } this.seriesPagination.currentPage = parseInt(page, 10); } + + // The filter is out of sync with the presets from typeaheads on first load but syncs afterwards + if (this.filter == undefined) { + this.filter = this.seriesService.createSeriesFilter(); + this.filter.collectionTags.push(this.collectionTag.id); + } + + // TODO: Add ability to filter series for a collection // Reload page after a series is updated or first load this.seriesService.getSeriesForTag(this.collectionTag.id, this.seriesPagination?.currentPage, this.seriesPagination?.itemsPerPage).subscribe(tags => { this.series = tags.result; @@ -180,9 +189,10 @@ export class CollectionDetailComponent implements OnInit, OnDestroy { }); } - updateFilter(data: FilterEvent) { - this.filter = data.filter; - if (this.seriesPagination !== undefined && this.seriesPagination !== null && !data.isFirst) { + updateFilter(event: FilterEvent) { + this.filter = event.filter; + const page = this.getPage(); + if (page === undefined || page === null || !event.isFirst) { this.seriesPagination.currentPage = 1; this.onPageChange(this.seriesPagination); } else { @@ -190,6 +200,11 @@ export class CollectionDetailComponent implements OnInit, OnDestroy { } } + getPage() { + const urlParams = new URLSearchParams(window.location.search); + return urlParams.get('page'); + } + handleCollectionActionCallback(action: Action, collectionTag: CollectionTag) { switch (action) { case(Action.Edit): diff --git a/UI/Web/src/app/events-widget/events-widget.component.scss b/UI/Web/src/app/events-widget/events-widget.component.scss index de94c5393..6a2070e78 100644 --- a/UI/Web/src/app/events-widget/events-widget.component.scss +++ b/UI/Web/src/app/events-widget/events-widget.component.scss @@ -1,14 +1,12 @@ -// NOTE: I'm leaving this not fully customized because I'm planning to rewrite the whole design in v0.5.2/3 -// These are customizations for events nav .dark-menu { background-color: var(--navbar-bg-color); - border-color: rgba(1, 4, 9, 0.5); + border-color: var(--event-widget-border-color); // rgba(1, 4, 9, 0.5); } .dark-menu-item { - color: var(--body-text-color); - background-color: rgb(1, 4, 9); - border-color: rgba(53, 53, 53, 0.5); + color: var(--event-widget-text-color); + background-color: var(--event-widget-item-bg-color); // rgb(1, 4, 9) + border-color: var(--event-widget-item-border-color); // rgba(53, 53, 53, 0.5) } // Popovers need to be their own component diff --git a/UI/Web/src/app/library-detail/library-detail.component.html b/UI/Web/src/app/library-detail/library-detail.component.html index 621312adf..6b3771c0d 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.html +++ b/UI/Web/src/app/library-detail/library-detail.component.html @@ -1,4 +1,4 @@ - +

    {{libraryName}} diff --git a/UI/Web/src/app/recently-added/recently-added.component.html b/UI/Web/src/app/recently-added/recently-added.component.html index 6e707fd7d..06b744c22 100644 --- a/UI/Web/src/app/recently-added/recently-added.component.html +++ b/UI/Web/src/app/recently-added/recently-added.component.html @@ -1,4 +1,4 @@ - +

    Recently Added

    diff --git a/UI/Web/src/app/registration/confirm-email/confirm-email.component.html b/UI/Web/src/app/registration/confirm-email/confirm-email.component.html index c829fad68..635756b35 100644 --- a/UI/Web/src/app/registration/confirm-email/confirm-email.component.html +++ b/UI/Web/src/app/registration/confirm-email/confirm-email.component.html @@ -22,7 +22,7 @@
    - +
    This field is required diff --git a/UI/Web/src/app/registration/confirm-email/confirm-email.component.scss b/UI/Web/src/app/registration/confirm-email/confirm-email.component.scss index b3a96fcce..4d637eb32 100644 --- a/UI/Web/src/app/registration/confirm-email/confirm-email.component.scss +++ b/UI/Web/src/app/registration/confirm-email/confirm-email.component.scss @@ -1,4 +1,4 @@ input { background-color: #fff !important; - color: black; + color: black !important; } \ No newline at end of file diff --git a/UI/Web/src/app/registration/confirm-email/confirm-email.component.ts b/UI/Web/src/app/registration/confirm-email/confirm-email.component.ts index f6e385e7a..3ea2c501e 100644 --- a/UI/Web/src/app/registration/confirm-email/confirm-email.component.ts +++ b/UI/Web/src/app/registration/confirm-email/confirm-email.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; import { ThemeService } from 'src/app/theme.service'; import { AccountService } from 'src/app/_services/account.service'; +import { NavService } from 'src/app/_services/nav.service'; @Component({ selector: 'app-confirm-email', @@ -29,8 +30,9 @@ export class ConfirmEmailComponent implements OnInit { constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService, - private toastr: ToastrService, private themeService: ThemeService) { - this.themeService.setTheme(this.themeService.defaultTheme); + private toastr: ToastrService, private themeService: ThemeService, private navService: NavService) { + this.navService.hideSideNav(); + this.themeService.setTheme(this.themeService.defaultTheme); const token = this.route.snapshot.queryParamMap.get('token'); const email = this.route.snapshot.queryParamMap.get('email'); if (token == undefined || token === '' || token === null) { diff --git a/UI/Web/src/app/registration/register/register.component.scss b/UI/Web/src/app/registration/register/register.component.scss index b3a96fcce..e09652950 100644 --- a/UI/Web/src/app/registration/register/register.component.scss +++ b/UI/Web/src/app/registration/register/register.component.scss @@ -1,4 +1,4 @@ input { background-color: #fff !important; - color: black; -} \ No newline at end of file + color: black !important; +} diff --git a/UI/Web/src/app/registration/register/register.component.ts b/UI/Web/src/app/registration/register/register.component.ts index 76ac3d87d..bc5f0454e 100644 --- a/UI/Web/src/app/registration/register/register.component.ts +++ b/UI/Web/src/app/registration/register/register.component.ts @@ -5,6 +5,7 @@ import { ToastrService } from 'ngx-toastr'; import { take } from 'rxjs/operators'; import { AccountService } from 'src/app/_services/account.service'; import { MemberService } from 'src/app/_services/member.service'; +import { NavService } from 'src/app/_services/nav.service'; /** * This is exclusivly used to register the first user on the server and nothing else @@ -22,10 +23,12 @@ export class RegisterComponent implements OnInit { password: new FormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]), }); - constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService, private toastr: ToastrService, private memberService: MemberService) { + constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService, + private toastr: ToastrService, private memberService: MemberService) { this.memberService.adminExists().pipe(take(1)).subscribe(adminExists => { if (adminExists) { this.router.navigateByUrl('login'); + return; } }); } diff --git a/UI/Web/src/app/registration/reset-password/reset-password.component.ts b/UI/Web/src/app/registration/reset-password/reset-password.component.ts index 4080b4f63..06e9e775e 100644 --- a/UI/Web/src/app/registration/reset-password/reset-password.component.ts +++ b/UI/Web/src/app/registration/reset-password/reset-password.component.ts @@ -25,6 +25,8 @@ export class ResetPasswordComponent implements OnInit { this.accountService.requestResetPasswordEmail(model).subscribe((resp: string) => { this.toastr.info(resp); this.router.navigateByUrl('login'); + }, err => { + this.toastr.error(err.error); }); } 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 c9749f5f7..8fee5c5c8 100644 --- a/UI/Web/src/app/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/series-detail.component.html @@ -9,10 +9,10 @@
    {{series?.localizedName}}
    -
    +
    -
    - +
    +
    @@ -21,7 +21,7 @@ -  {{(hasReadingProgress) ? 'Continue' : 'Read'}} +  {{(hasReadingProgress) ? 'Continue' : 'Read'}}
    @@ -31,7 +31,7 @@
    -
    +
    diff --git a/UI/Web/src/app/series-detail/series-detail.component.scss b/UI/Web/src/app/series-detail/series-detail.component.scss index 4c204f40a..472579f96 100644 --- a/UI/Web/src/app/series-detail/series-detail.component.scss +++ b/UI/Web/src/app/series-detail/series-detail.component.scss @@ -5,21 +5,4 @@ .rating-star { margin-top: 2px; font-size: 1.5rem; -} - -.poster { - width: 100%; - max-width: 300px; -} - -@media(max-width: var(--grid-breakpoints-sm)) { - .poster { - display: none; - } -} - -@media(max-width: var(--grid-breakpoints-sm)) { - .read-btn--text { - display: none; - } -} +} \ No newline at end of file diff --git a/UI/Web/src/app/sidenav/side-nav-companion-bar/side-nav-companion-bar.component.ts b/UI/Web/src/app/sidenav/side-nav-companion-bar/side-nav-companion-bar.component.ts index fd6f7fb02..bdd7a411e 100644 --- a/UI/Web/src/app/sidenav/side-nav-companion-bar/side-nav-companion-bar.component.ts +++ b/UI/Web/src/app/sidenav/side-nav-companion-bar/side-nav-companion-bar.component.ts @@ -15,6 +15,11 @@ export class SideNavCompanionBarComponent implements OnInit { */ @Input() hasFilter: boolean = false; + /** + * Is the input open by default + */ + @Input() filterOpenByDefault: boolean = false; + /** * Should be passed through from Filter component. */ @@ -27,14 +32,10 @@ export class SideNavCompanionBarComponent implements OnInit { constructor() { } ngOnInit(): void { - } - - goBack() { - + this.isFilterOpen = this.filterOpenByDefault; } toggleFilter() { - //collapse.toggle() this.isFilterOpen = !this.isFilterOpen; this.filterOpen.emit(this.isFilterOpen); } diff --git a/UI/Web/src/app/sidenav/side-nav-item/side-nav-item.component.scss b/UI/Web/src/app/sidenav/side-nav-item/side-nav-item.component.scss index 30a29baed..621f64623 100644 --- a/UI/Web/src/app/sidenav/side-nav-item/side-nav-item.component.scss +++ b/UI/Web/src/app/sidenav/side-nav-item/side-nav-item.component.scss @@ -59,8 +59,13 @@ } &:hover { - color: var(--side-nav-hover-color); + color: var(--side-nav-hover-text-color); background-color: var(--side-nav-hover-bg-color); + + .card-actions i.fa { + // TODO: The override to white does not work, please fix for light themes + color: var(--side-nav-hover-text-color) !important; + } } &.active { @@ -76,13 +81,20 @@ } .side-nav-text, i { - color: var(--side-nav-item-active-text-color); + + color: var(--side-nav-item-active-text-color) !important; } &:hover { - color: var(--side-nav-hover-color); + color: var(--side-nav-hover-text-color); background-color: var(--side-nav-hover-bg-color); + + .card-actions i.fa { + // TODO: The override to white does not work, please fix for light themes + color: var(--side-nav-hover-text-color) !important; + } } + } } diff --git a/UI/Web/src/app/sidenav/side-nav/side-nav.component.ts b/UI/Web/src/app/sidenav/side-nav/side-nav.component.ts index 5ce818d87..feebe6f26 100644 --- a/UI/Web/src/app/sidenav/side-nav/side-nav.component.ts +++ b/UI/Web/src/app/sidenav/side-nav/side-nav.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, Subject } from 'rxjs'; -import { take, takeUntil, takeWhile } from 'rxjs/operators'; +import { filter, take, takeUntil, takeWhile } from 'rxjs/operators'; import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service'; import { UtilityService } from '../../shared/_services/utility.service'; import { Library } from '../../_models/library'; @@ -48,7 +48,8 @@ export class SideNavComponent implements OnInit, OnDestroy { this.actions = this.actionFactoryService.getLibraryActions(this.handleAction.bind(this)); }); - this.messageHub.messages$.pipe(takeUntil(this.onDestory), takeWhile(event => event.event === EVENTS.LibraryModified)).subscribe(event => { + this.messageHub.messages$.pipe(takeUntil(this.onDestory), filter(event => event.event === EVENTS.LibraryModified)).subscribe(event => { + console.log('Received library modfied event'); this.libraryService.getLibrariesForMember().pipe(take(1)).subscribe((libraries: Library[]) => { this.libraries = libraries; }); diff --git a/UI/Web/src/app/typeahead/typeahead.component.ts b/UI/Web/src/app/typeahead/typeahead.component.ts index 967469c40..43c8ab681 100644 --- a/UI/Web/src/app/typeahead/typeahead.component.ts +++ b/UI/Web/src/app/typeahead/typeahead.component.ts @@ -223,7 +223,6 @@ export class TypeaheadComponent implements OnInit, OnDestroy { switchMap(val => { this.isLoadingOptions = true; let results: Observable; - console.log('val: ', val); if (Array.isArray(this.settings.fetchFn)) { const filteredArray = this.settings.compareFn(this.settings.fetchFn, val.trim()); results = of(filteredArray).pipe(takeUntil(this.onDestroy), map((items: any[]) => items.filter(item => this.filterSelected(item)))); @@ -457,9 +456,6 @@ export class TypeaheadComponent implements OnInit, OnDestroy { if (this.showAddItem) { this.hasFocus = true; } - console.log('show Add item: ', this.showAddItem); - console.log('compare func: ', this.settings.compareFn(options, this.typeaheadControl.value.trim())); - } unlock(event: any) { diff --git a/UI/Web/src/app/user-login/user-login.component.html b/UI/Web/src/app/user-login/user-login.component.html index 966ecf7c1..93354e7dc 100644 --- a/UI/Web/src/app/user-login/user-login.component.html +++ b/UI/Web/src/app/user-login/user-login.component.html @@ -15,7 +15,7 @@
    diff --git a/UI/Web/src/theme/themes/dark.scss b/UI/Web/src/theme/themes/dark.scss index e781019f2..82e41590b 100644 --- a/UI/Web/src/theme/themes/dark.scss +++ b/UI/Web/src/theme/themes/dark.scss @@ -94,7 +94,7 @@ --side-nav-openclose-transition: 0.15s ease-in-out; --side-nav-box-shadow: rgba(0,0,0,0.5); --side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%); - --side-nav-hover-color: white; + --side-nav-hover-text-color: white; --side-nav-hover-bg-color: black; --side-nav-color: white; --side-nav-border-radius: 5px; @@ -210,5 +210,12 @@ --carousel-hover-header-text-decoration: none; /** Drawer */ - --drawer-background-color: black; + --drawer-background-color: black; + + /** Event Widget */ + --event-widget-bg-color: rgb(1, 4, 9); + --event-widget-item-bg-color: rgb(1, 4, 9); + --event-widget-text-color: var(--body-text-color); + --event-widget-item-border-color: rgba(53, 53, 53, 0.5); + --event-widget-border-color: rgba(1, 4, 9, 0.5); } diff --git a/UI/Web/src/theme/themes/e-ink.scss b/UI/Web/src/theme/themes/e-ink.scss index 82e2b7151..51080dc29 100644 --- a/UI/Web/src/theme/themes/e-ink.scss +++ b/UI/Web/src/theme/themes/e-ink.scss @@ -37,6 +37,7 @@ --btn-disabled-bg-color: #020202; --btn-disabled-text-color: white; --btn-disabled-border-color: #6c757d; + --btn-fa-icon-color: black; /* Nav */ @@ -65,7 +66,7 @@ --side-nav-openclose-transition: 1ms; --side-nav-box-shadow: none; --side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%); - --side-nav-hover-color: white; + --side-nav-hover-text-color: white; --side-nav-hover-bg-color: black; --side-nav-color: black; --side-nav-border-radius: 5px; @@ -78,7 +79,7 @@ --side-nav-item-active-color: var(--primary-color); --side-nav-active-bg-color: rgba(0,0,0,0.5); --side-nav-overlay-color: rgba(0,0,0,1); - --side-nav-item-active-text-color: black; + --side-nav-item-active-text-color: white; /* Toasts */ --toast-success-bg-color: rgba(74, 198, 148, 0.9); @@ -156,5 +157,19 @@ --pagination-focus-border-color: var(--primary-color); --pagination-link-hover-color: var(--primary-color); + /** Event Widget */ + --event-widget-bg-color: white; + --event-widget-item-bg-color: lightgrey; + --event-widget-text-color: black; + --event-widget-item-border-color: lightgrey; + --event-widget-border-color: lightgrey; + + /* Popover */ + --popover-body-bg-color: var(--navbar-bg-color); + --popover-body-text-color: var(--navbar-text-color); + --popover-outerarrow-color: lightgrey; + --popover-arrow-color: lightgrey; + --popover-bg-color: lightgrey; + --popover-border-color: lightgrey; } \ No newline at end of file diff --git a/UI/Web/src/theme/themes/light.scss b/UI/Web/src/theme/themes/light.scss index 734bc6e5f..d8b040e16 100644 --- a/UI/Web/src/theme/themes/light.scss +++ b/UI/Web/src/theme/themes/light.scss @@ -23,11 +23,20 @@ /* Buttons */ --btn-primary-text-color: black; + --btn-primary-bg-color: white; + --btn-primary-border-color: black; + --btn-primary-hover-text-color: white; + --btn-primary-hover-bg-color: black; + --btn-primary-hover-border-color: black; --btn-alt-bg-color: #424c72; --btn-alt-border-color: #444f75; --btn-alt-hover-bg-color: #3b4466; --btn-alt-focus-bg-color: #343c59; --btn-alt-focus-boxshadow-color: rgb(68 79 117 / 50%); + --btn-fa-icon-color: black; + --btn-disabled-bg-color: #020202; + --btn-disabled-text-color: white; + --btn-disabled-border-color: #6c757d; /* Nav */ --nav-link-active-text-color: white; @@ -46,7 +55,7 @@ --side-nav-openclose-transition: 0.15s ease-in-out; --side-nav-box-shadow: none; --side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%); - --side-nav-hover-color: white; + --side-nav-hover-text-color: white; --side-nav-hover-bg-color: black; --side-nav-color: black; --side-nav-border-radius: 5px; @@ -58,12 +67,12 @@ --side-nav-closed-bg-color: transparent; --side-nav-item-active-color: var(--primary-color); --side-nav-active-bg-color: rgba(0,0,0,0.5); - --side-nav-item-active-text-color: black; + --side-nav-item-active-text-color: white; --side-nav-overlay-color: rgba(0,0,0,0.5); /* Checkboxes */ - --checkbox-checked-bg-color: black; + --checkbox-checked-bg-color: var(--primary-color); --checkbox-bg-color: white; --checkbox-border-color: var(--primary-color); --checkbox-focus-border-color: var(--input-border-color); @@ -148,4 +157,19 @@ --pagination-link-bg-color: white; --pagination-focus-border-color: var(--primary-color); --pagination-link-hover-color: var(--primary-color); + + /** Event Widget */ + --event-widget-bg-color: white; + --event-widget-item-bg-color: lightgrey; + --event-widget-text-color: black; + --event-widget-item-border-color: lightgrey; + --event-widget-border-color: lightgrey; + + /* Popover */ + --popover-body-bg-color: var(--navbar-bg-color); + --popover-body-text-color: var(--navbar-text-color); + --popover-outerarrow-color: lightgrey; + --popover-arrow-color: lightgrey; + --popover-bg-color: lightgrey; + --popover-border-color: lightgrey; } \ No newline at end of file