Theme Cleanup (#1089)
* Fixed e-ink theme not properly applying correctly * Fixed some seed changes. Changed card checkboxes to use our themed ones * Fixed recently added carousel not going to recently-added page * Fixed an issue where no results found would show when searching for a library name * Cleaned up list a bit, typeahead dropdown still needs work * Added a TODO to streamline series-card component * Removed ng-lazyload-image module since we don't use it. We use lazysizes * Darken card on hover * Fixing accordion focus style * ux pass updates - Fixed typeahead width - Fixed changelog download buttons - Fixed a select - Fixed various input box-shadows - Fixed all anchors to only have underline on hover - Added navtab hover and active effects * more ux pass - Fixed spacing on theme cards - Fixed some light theme issues - Exposed text-muted-color for theme card subtitle color * UX pass fixes - Changed back to bright green for primary on dark theme - Changed fa icon to black on e-ink * Merged changelog component * Fixed anchor buttons text decoration * Changed nav tabs to have a background color instead of open active state * When user is not authenticated, make sure we set default theme (dark) * Cleanup on carousel * Updated Users tab to use small buttons with icons to align with Library tab * Cleaned up brand to not underline, removed default link underline on hover in dropdown and pill tabs * Fixed collection detail posters not rendering Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
70b85e0668
commit
4bd9f243f2
51 changed files with 279 additions and 163 deletions
|
|
@ -1,4 +0,0 @@
|
|||
export interface RefreshMetadataEvent {
|
||||
libraryId: number;
|
||||
seriesId: number;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { PersonRole } from './_models/person';
|
||||
import { PersonRole } from '../_models/person';
|
||||
|
||||
@Pipe({
|
||||
name: 'personRole'
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { PublicationStatus } from './_models/metadata/publication-status';
|
||||
import { PublicationStatus } from '../_models/metadata/publication-status';
|
||||
|
||||
@Pipe({
|
||||
name: 'publicationStatus'
|
||||
|
|
@ -84,6 +84,8 @@ export class AccountService implements OnDestroy {
|
|||
} else {
|
||||
this.themeService.setTheme(this.themeService.defaultTheme);
|
||||
}
|
||||
} else {
|
||||
this.themeService.setTheme(this.themeService.defaultTheme);
|
||||
}
|
||||
|
||||
this.currentUserSource.next(user);
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ 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});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,12 @@
|
|||
</h4>
|
||||
<h6 class="card-subtitle mb-2 text-muted">Published: {{update.publishDate | date: 'short'}}</h6>
|
||||
|
||||
<pre class="card-text update-body" [innerHtml]="update.updateBody | safeHtml"></pre>
|
||||
<a *ngIf="!update.isDocker" href="{{update.updateUrl}}" class="btn btn-{{indx === 0 ? 'primary' : 'secondary'}} float-end" target="_blank">Download</a>
|
||||
|
||||
<pre class="card-text update-body">
|
||||
<app-read-more class="float-end" [text]="update.updateBody" [maxLength]="500"></app-read-more>
|
||||
</pre>
|
||||
<a *ngIf="!update.isDocker && update.updateVersion === installedVersion" href="{{update.updateUrl}}" class="btn disabled btn-{{indx === 0 ? 'primary' : 'secondary'}} float-end" target="_blank">Installed</a>
|
||||
<a *ngIf="!update.isDocker && update.updateVersion !== installedVersion" href="{{update.updateUrl}}" class="btn btn-{{indx === 0 ? 'primary' : 'secondary'}} float-end" target="_blank">Download</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@
|
|||
<span class="visually-hidden">(You)</span>
|
||||
</span>
|
||||
<div class="float-end" *ngIf="canEditMember(member)">
|
||||
<button class="btn btn-danger me-2" (click)="deleteUser(member)" placement="top" ngbTooltip="Delete User" attr.aria-label="Delete User {{member.username | titlecase}}"><i class="fa fa-trash" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-secondary me-2" (click)="updatePassword(member)" placement="top" ngbTooltip="Change Password" attr.aria-label="Change Password for {{member.username | titlecase}}"><i class="fa fa-key" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-primary" (click)="openEditUser(member)" placement="top" ngbTooltip="Edit" attr.aria-label="Edit {{member.username | titlecase}}"><i class="fa fa-pen" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-danger btn-sm me-2" (click)="deleteUser(member)" placement="top" ngbTooltip="Delete User" attr.aria-label="Delete User {{member.username | titlecase}}"><i class="fa fa-trash" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-secondary btn-sm me-2" (click)="updatePassword(member)" placement="top" ngbTooltip="Change Password" attr.aria-label="Change Password for {{member.username | titlecase}}"><i class="fa fa-key" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-primary btn-sm" (click)="openEditUser(member)" placement="top" ngbTooltip="Edit" attr.aria-label="Edit {{member.username | titlecase}}"><i class="fa fa-pen" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</h4>
|
||||
<div>Last Active:
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ import { ReadingListModule } from './reading-list/reading-list.module';
|
|||
import { SAVER, getSaver } from './shared/_providers/saver.provider';
|
||||
import { ConfigData } from './_models/config-data';
|
||||
import { NavEventsToggleComponent } from './nav-events-toggle/nav-events-toggle.component';
|
||||
import { PersonRolePipe } from './person-role.pipe';
|
||||
import { PersonRolePipe } from './_pipes/person-role.pipe';
|
||||
import { SeriesMetadataDetailComponent } from './series-metadata-detail/series-metadata-detail.component';
|
||||
import { AllSeriesComponent } from './all-series/all-series.component';
|
||||
import { PublicationStatusPipe } from './publication-status.pipe';
|
||||
import { RegistrationModule } from './registration/registration.module';
|
||||
import { GroupedTypeaheadComponent } from './grouped-typeahead/grouped-typeahead.component';
|
||||
import { PublicationStatusPipe } from './_pipes/publication-status.pipe';
|
||||
import { ThemeTestComponent } from './theme-test/theme-test.component';
|
||||
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ import { ThemeTestComponent } from './theme-test/theme-test.component';
|
|||
|
||||
NgbDropdownModule, // Nav
|
||||
NgbPopoverModule, // Nav Events toggle
|
||||
NgbRatingModule, // Series Detail
|
||||
NgbRatingModule, // Series Detail & Filter
|
||||
NgbNavModule,
|
||||
NgbPaginationModule,
|
||||
|
||||
|
|
@ -85,6 +85,7 @@ import { ThemeTestComponent } from './theme-test/theme-test.component';
|
|||
ReadingListModule,
|
||||
RegistrationModule,
|
||||
|
||||
|
||||
ToastrModule.forRoot({
|
||||
positionClass: 'toast-bottom-right',
|
||||
preventDuplicates: true,
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@
|
|||
<i class="fa fa-arrow-down" title="Descending"></i>
|
||||
</ng-template>
|
||||
</button>
|
||||
<select id="sort-options" class="form-control" formControlName="sortField" style="height: 38px;">
|
||||
<select id="sort-options" class="form-select" formControlName="sortField" style="height: 38px;">
|
||||
<option [value]="SortField.SortName">Sort Name</option>
|
||||
<option [value]="SortField.Created">Created</option>
|
||||
<option [value]="SortField.LastModified">Last Modified</option>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<div class="not-read-badge" *ngIf="read === 0 && total > 0"></div>
|
||||
<div class="bulk-mode {{bulkSelectionService.hasSelections() ? 'always-show' : ''}}" (click)="handleSelection($event)" *ngIf="allowSelection">
|
||||
<input type="checkbox" attr.aria-labelledby="{{title}}_{{entity?.id}}" [ngModel]="selected" [ngModelOptions]="{standalone: true}">
|
||||
<input type="checkbox" class="form-check-input" attr.aria-labelledby="{{title}}_{{entity?.id}}" [ngModel]="selected" [ngModelOptions]="{standalone: true}">
|
||||
</div>
|
||||
|
||||
<div class="count" *ngIf="count > 1">
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<div class="card-body" *ngIf="title.length > 0 || actions.length > 0">
|
||||
<div>
|
||||
<span class="card-title" placement="top" id="{{title}}_{{entity?.id}}" [ngbTooltip]="tooltipTitle" (click)="handleClick()" tabindex="0">
|
||||
<span class="card-title" placement="top" id="{{title}}_{{entity?.id}}" [ngbTooltip]="tooltipTitle" (click)="handleClick($event)" tabindex="0">
|
||||
<span *ngIf="isPromoted()">
|
||||
<i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">(promoted)</span>
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ $image-width: 160px;
|
|||
input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--checkbox-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,9 +158,5 @@ $image-width: 160px;
|
|||
width: 100%;
|
||||
height: 230px;
|
||||
z-index: 10;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.card-overlay:hover {
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { EditSeriesModalComponent } from './_modals/edit-series-modal/edit-serie
|
|||
import { EditCollectionTagsComponent } from './_modals/edit-collection-tags/edit-collection-tags.component';
|
||||
import { ChangeCoverImageModalComponent } from './_modals/change-cover-image/change-cover-image-modal.component';
|
||||
import { BookmarksModalComponent } from './_modals/bookmarks-modal/bookmarks-modal.component';
|
||||
import { LazyLoadImageModule } from 'ng-lazyload-image';
|
||||
import { NgbTooltipModule, NgbCollapseModule, NgbPaginationModule, NgbDropdownModule, NgbProgressbarModule, NgbNavModule, NgbRatingModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CardActionablesComponent } from './card-item/card-actionables/card-actionables.component';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
|
@ -61,7 +60,6 @@ import { BookmarkComponent } from './bookmark/bookmark.component';
|
|||
NgbRatingModule,
|
||||
|
||||
NgbNavModule, //Series Detail
|
||||
LazyLoadImageModule,
|
||||
NgbPaginationModule, // CardDetailLayoutComponent
|
||||
NgbDropdownModule,
|
||||
NgbProgressbarModule,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<ng-container *ngIf="data !== undefined">
|
||||
<app-card-item [title]="data.name" [actions]="actions" [supressLibraryLink]="suppressLibraryLink" [imageUrl]="imageUrl"
|
||||
[entity]="data" [total]="data.pages" [read]="data.pagesRead" (clicked)="handleClick()"
|
||||
[entity]="data" [total]="data.pages" [read]="data.pagesRead" (clicked)="handleClick()"
|
||||
[allowSelection]="allowSelection" (selection)="selection.emit(selected)" [selected]="selected"
|
||||
></app-card-item>
|
||||
</ng-container>
|
||||
|
|
@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output }
|
|||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take, takeUntil, takeWhile } from 'rxjs/operators';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
|
|
@ -11,7 +11,6 @@ import { SeriesService } from 'src/app/_services/series.service';
|
|||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||
import { ActionService } from 'src/app/_services/action.service';
|
||||
import { EditSeriesModalComponent } from '../_modals/edit-series-modal/edit-series-modal.component';
|
||||
import { RefreshMetadataEvent } from 'src/app/_models/events/refresh-metadata-event';
|
||||
import { MessageHubService } from 'src/app/_services/message-hub.service';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
</div>
|
||||
<div>
|
||||
<swiper
|
||||
#swiper
|
||||
[slidesPerView]="'auto'"
|
||||
(init)="onSwiper($event)"
|
||||
[freeMode]="true">
|
||||
<ng-template *ngFor="let item of items; index as i;" swiperSlide>
|
||||
<ng-container [ngTemplateOutlet]="carouselItemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export class CarouselReelComponent implements OnInit {
|
|||
|
||||
swiper: Swiper | undefined;
|
||||
|
||||
|
||||
trackByIdentity: (index: number, item: any) => string;
|
||||
|
||||
get isEnd() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="container-fluid" *ngIf="collectionTag !== undefined" style="padding-top: 10px">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 col-xs-4 col-sm-6">
|
||||
<app-image class="poster" maxWidth="481px" [imageUrl]="tagImage"></app-image>
|
||||
<div class="col-md-2 col-xs-4 col-sm-6 poster">
|
||||
<app-image maxWidth="481px" [imageUrl]="tagImage"></app-image>
|
||||
</div>
|
||||
<div class="col-md-10 col-xs-8 col-sm-6">
|
||||
<div class="row g-0">
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
|
||||
.poster {
|
||||
width: 100%;
|
||||
max-height: 481px;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
@media(max-width: var(--grid-breakpoints-sm)) {
|
||||
.poster {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
// Including breakpoint has issue with resovling variable, so copying gridpoints here
|
||||
@include media-breakpoint-down(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
|
||||
.poster {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: var(--grid-breakpoints-sm)) {
|
||||
.read-btn--text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
|
||||
.read-btn--text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,8 @@
|
|||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="noResultsTemplate != undefined && searchTerm.length > 0 && !grouppedData.persons.length && !grouppedData.collections.length && !grouppedData.series.length && !grouppedData.persons.length && !grouppedData.tags.length && !grouppedData.genres.length">
|
||||
<ng-container *ngIf="noResultsTemplate != undefined && searchTerm.length > 0 && !grouppedData.persons.length && !grouppedData.collections.length
|
||||
&& !grouppedData.series.length && !grouppedData.persons.length && !grouppedData.tags.length && !grouppedData.genres.length && !grouppedData.libraries.length">
|
||||
<ul class="list-group results">
|
||||
<li class="list-group-item">
|
||||
<ng-container [ngTemplateOutlet]="noResultsTemplate"></ng-container>
|
||||
|
|
|
|||
|
|
@ -42,20 +42,6 @@ form {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
// .close {
|
||||
// cursor: pointer;
|
||||
// position: absolute;
|
||||
// top: 7px;
|
||||
// right: 10px;
|
||||
// }
|
||||
|
||||
// @media only screen and (max-width:650px) {
|
||||
// .close {
|
||||
// top: 50%;
|
||||
// transform: translate(0, -60%);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
input {
|
||||
outline: 0 !important;
|
||||
|
|
@ -67,7 +53,7 @@ form {
|
|||
text-indent: 0 !important;
|
||||
line-height: inherit !important;
|
||||
box-shadow: none !important;
|
||||
width: 200px;
|
||||
width: 300px;
|
||||
transition-property: all;
|
||||
transition-duration: 0.3s;
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
|
||||
<app-carousel-reel [items]="recentlyAddedChapters" title="Recently Added">
|
||||
<app-carousel-reel [items]="recentlyAddedChapters" title="Recently Added" (sectionClick)="handleSectionClick($event)">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-card-item [entity]="item" [title]="item.title" [subtitle]="item.seriesName" [imageUrl]="imageService.getRecentlyAddedItem(item)"
|
||||
[supressArchiveWarning]="true" (clicked)="handleRecentlyAddedChapterClick(item)"></app-card-item>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { Title } from '@angular/platform-browser';
|
|||
import { Router } from '@angular/router';
|
||||
import { ReplaySubject, Subject } from 'rxjs';
|
||||
import { debounceTime, take, takeUntil } from 'rxjs/operators';
|
||||
import { RefreshMetadataEvent } from '../_models/events/refresh-metadata-event';
|
||||
import { SeriesAddedEvent } from '../_models/events/series-added-event';
|
||||
import { SeriesRemovedEvent } from '../_models/events/series-removed-event';
|
||||
import { Library } from '../_models/library';
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
font-family: var(--brand-font-family);
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
max-height: 28px;
|
||||
vertical-align: top;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { DownloadService } from '../shared/_services/download.service';
|
|||
import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
|
||||
import { ReviewSeriesModalComponent } from '../_modals/review-series-modal/review-series-modal.component';
|
||||
import { Chapter } from '../_models/chapter';
|
||||
import { RefreshMetadataEvent } from '../_models/events/refresh-metadata-event';
|
||||
import { ScanSeriesEvent } from '../_models/events/scan-series-event';
|
||||
import { SeriesRemovedEvent } from '../_models/events/series-removed-event';
|
||||
import { LibraryType } from '../_models/library';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
<img #img class="lazyload" [src]="imageService.placeholderImage" [attr.data-src]="imageUrl"
|
||||
(error)="imageService.updateErroredImage($event)"
|
||||
aria-hidden="true">
|
||||
|
||||
<!-- <img #img [defaultImage]="imageService.placeholderImage" [lazyload]="imageUrl"
|
||||
(error)="imageService.updateErroredImage($event)"
|
||||
aria-hidden="true"> -->
|
||||
|
|
|
|||
|
|
@ -45,20 +45,18 @@ import { ImageComponent } from './image/image.component';
|
|||
NgCircleProgressModule.forRoot(),
|
||||
],
|
||||
exports: [
|
||||
SafeHtmlPipe,
|
||||
SentenceCasePipe,
|
||||
ReadMoreComponent,
|
||||
DrawerComponent,
|
||||
TagBadgeComponent,
|
||||
ShowIfScrollbarDirective,
|
||||
A11yClickDirective,
|
||||
SeriesFormatComponent,
|
||||
SeriesFormatComponent,
|
||||
TagBadgeComponent,
|
||||
CircularLoaderComponent,
|
||||
PersonBadgeComponent,
|
||||
BadgeExpanderComponent,
|
||||
ImageComponent
|
||||
SafeHtmlPipe, // Used globally
|
||||
SentenceCasePipe, // Used globablly
|
||||
ReadMoreComponent, // Used globably
|
||||
DrawerComponent, // Can be replaced with boostrap offscreen canvas (v5)
|
||||
ShowIfScrollbarDirective, // Used book reader only?
|
||||
A11yClickDirective, // Used globally
|
||||
SeriesFormatComponent, // Used globally
|
||||
TagBadgeComponent, // Used globally
|
||||
CircularLoaderComponent, // Used in Cards only
|
||||
PersonBadgeComponent, // Used Series Detail
|
||||
BadgeExpanderComponent, // Used globally
|
||||
ImageComponent // Used globally
|
||||
],
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 15px;
|
||||
opacity: 1px;
|
||||
|
|
@ -42,6 +46,7 @@ input {
|
|||
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
min-width: 10rem;
|
||||
background: var(--input-bg-color);
|
||||
z-index:1000;
|
||||
margin: 2px 0 0;
|
||||
|
|
@ -56,6 +61,7 @@ input {
|
|||
|
||||
.list-group-item {
|
||||
padding: 5px 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -64,9 +70,6 @@ input {
|
|||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import { ThemeProvider } from '../../_models/preferences/site-theme';
|
||||
import { SiteThemeProviderPipe } from './site-theme-provider.pipe';
|
||||
|
||||
describe('SiteThemeProviderPipe', () => {
|
||||
let siteThemeProviderPipe: SiteThemeProviderPipe;
|
||||
|
||||
beforeEach(() => {
|
||||
siteThemeProviderPipe = new SiteThemeProviderPipe();
|
||||
})
|
||||
|
||||
it('translates system to System', () => {
|
||||
expect(siteThemeProviderPipe.transform(ThemeProvider.System)).toBe('System');
|
||||
});
|
||||
|
||||
it('translates user to User', () => {
|
||||
expect(siteThemeProviderPipe.transform(ThemeProvider.User)).toBe('User');
|
||||
});
|
||||
|
||||
it('translates null to empty string', () => {
|
||||
expect(siteThemeProviderPipe.transform(null)).toBe('');
|
||||
});
|
||||
|
||||
it('translates undefined to empty string', () => {
|
||||
expect(siteThemeProviderPipe.transform(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { ThemeProvider } from 'src/app/_models/preferences/site-theme';
|
||||
|
||||
|
||||
@Pipe({
|
||||
name: 'siteThemeProvider'
|
||||
})
|
||||
export class SiteThemeProviderPipe implements PipeTransform {
|
||||
|
||||
transform(provider: ThemeProvider | undefined | null): string {
|
||||
if (provider === null || provider === undefined) return '';
|
||||
switch(provider) {
|
||||
case ThemeProvider.System:
|
||||
return 'System';
|
||||
case ThemeProvider.User:
|
||||
return 'User';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,13 +10,10 @@
|
|||
|
||||
<div class="row g-0">
|
||||
<ng-container *ngFor="let theme of (themeService.themes$ | async)">
|
||||
<div class="card col-auto me-3" style="width: 18rem;">
|
||||
<div class="card col-auto me-3 mb-3" style="width: 18rem;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{theme.name | sentenceCase}}</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">{{theme.provider === ThemeProvider.System ? 'System' : 'User'}}</h6>
|
||||
<!-- <p class="card-text">
|
||||
<i class="far fa-file-code" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</p> -->
|
||||
<h6 class="card-subtitle mb-2 text-muted">{{theme.provider | siteThemeProvider}}</h6>
|
||||
<button class="btn btn-secondary me-2" [disabled]="theme.isDefault" *ngIf="isAdmin" (click)="updateDefault(theme)">Set Default</button>
|
||||
<button class="btn btn-primary" (click)="applyTheme(theme)" [disabled]="currentTheme?.id === theme.id">{{currentTheme?.id === theme.id ? 'Applied' : 'Apply'}}</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import { UserSettingsRoutingModule } from './user-settings-routing.module';
|
|||
import { ApiKeyComponent } from './api-key/api-key.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { ThemeManagerComponent } from './theme-manager/theme-manager.component';
|
||||
import { SiteThemeProviderPipe } from './_pipes/site-theme-provider.pipe';
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -17,7 +19,8 @@ import { ThemeManagerComponent } from './theme-manager/theme-manager.component';
|
|||
SeriesBookmarksComponent,
|
||||
UserPreferencesComponent,
|
||||
ApiKeyComponent,
|
||||
ThemeManagerComponent
|
||||
ThemeManagerComponent,
|
||||
SiteThemeProviderPipe,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
@ -28,6 +31,9 @@ import { ThemeManagerComponent } from './theme-manager/theme-manager.component';
|
|||
NgxSliderModule,
|
||||
UserSettingsRoutingModule,
|
||||
SharedModule // SentenceCase pipe
|
||||
],
|
||||
exports: [
|
||||
SiteThemeProviderPipe
|
||||
]
|
||||
})
|
||||
export class UserSettingsModule { }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue