Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
Co-authored-by: Fesaa <77553571+Fesaa@users.noreply.github.com>
This commit is contained in:
Joe Milazzo 2025-02-19 15:06:54 -06:00 committed by GitHub
parent b858729c9e
commit 9565fe7360
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 777 additions and 314 deletions

View file

@ -163,11 +163,15 @@ $image-width: 160px;
align-items: center;
padding: 0 5px;
:first-child {
min-width: 22px;
}
.card-title {
font-size: 0.8rem;
margin: 0;
text-align: center;
max-width: 98px;
max-width: 90px;
a {
overflow: hidden;

View file

@ -17,6 +17,7 @@ export interface UpdateVersionEvent {
theme: Array<string>;
developer: Array<string>;
api: Array<string>;
featureRequests: Array<string>;
/**
* The part above the changelog part
*/

View file

@ -44,6 +44,9 @@ export class ThemeService {
private themesSource = new ReplaySubject<SiteTheme[]>(1);
public themes$ = this.themesSource.asObservable();
private darkModeSource = new ReplaySubject<boolean>(1);
public isDarkMode$ = this.darkModeSource.asObservable();
/**
* Maintain a cache of themes. SignalR will inform us if we need to refresh cache
@ -237,9 +240,11 @@ export class ThemeService {
}
this.currentThemeSource.next(theme);
this.darkModeSource.next(this.isDarkTheme());
});
} else {
this.currentThemeSource.next(theme);
this.darkModeSource.next(this.isDarkTheme());
}
} else {
// Only time themes isn't already loaded is on first load

View file

@ -76,7 +76,7 @@ export class VersionService implements OnDestroy{
this.modalOpen = true;
this.serverService.getChangelog(1).subscribe(changelog => {
const ref = this.modalService.open(NewUpdateModalComponent, {size: 'lg'});
const ref = this.modalService.open(NewUpdateModalComponent, {size: 'lg', keyboard: false});
ref.componentInstance.version = version;
ref.componentInstance.update = changelog[0];

View file

@ -32,7 +32,7 @@
<div class="mb-3" style="width: 100%">
<app-setting-switch [title]="t('dont-match-label')" [subtitle]="t('dont-match-tooltip')">
<ng-template #switch>
<div class="form-check form-switch float-end">
<div class="form-check form-switch">
<input id="dont-match" type="checkbox" class="form-check-input" formControlName="dontMatch" role="switch">
</div>
</ng-template>
@ -50,8 +50,7 @@
@if (!formGroup.get('dontMatch')?.value) {
<app-loading [loading]="isLoading"></app-loading>
@for(item of matches; track item.series.name) {
<app-match-series-result-item [item]="item" (selected)="selectMatch($event)"></app-match-series-result-item>
<div class="setting-section-break"></div>
<app-match-series-result-item [item]="item" [isDarkMode]="(themeService.isDarkMode$ | async)!" (selected)="selectMatch($event)"></app-match-series-result-item>
} @empty {
@if (!isLoading) {
{{t('no-results')}}

View file

@ -0,0 +1,3 @@
.setting-section-break {
margin: 0 !important;
}

View file

@ -10,11 +10,14 @@ import {ExternalSeriesMatch} from "../../_models/series-detail/external-series-m
import {ToastrService} from "ngx-toastr";
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
import { ThemeService } from 'src/app/_services/theme.service';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-match-series-modal',
standalone: true,
imports: [
AsyncPipe,
TranslocoDirective,
MatchSeriesResultItemComponent,
LoadingComponent,
@ -31,6 +34,7 @@ export class MatchSeriesModalComponent implements OnInit {
private readonly seriesService = inject(SeriesService);
private readonly modalService = inject(NgbActiveModal);
private readonly toastr = inject(ToastrService);
protected readonly themeService = inject(ThemeService);
@Input({required: true}) series!: Series;

View file

@ -1,13 +1,13 @@
<ng-container *transloco="let t; read:'match-series-result-item'">
<div class="d-flex p-1 clickable" (click)="selectItem()">
<div style="width: 32px" class="me-1">
<div class="match-item-container p-3 mt-3 {{isDarkMode ? 'dark' : 'light'}}">
<div class="d-flex clickable match-item" (click)="selectItem()">
<div class="me-1">
@if (item.series.coverUrl) {
<app-image class="me-3 search-result" width="32px" [imageUrl]="item.series.coverUrl"></app-image>
<app-image class="me-3 search-result" width="100px" [imageUrl]="item.series.coverUrl"></app-image>
}
</div>
<div class="ms-1">
<div>{{item.series.name}}</div>
<div><span class="title">{{item.series.name}}</span> <span class="me-1 float-end">({{item.matchRating | translocoPercent}})</span></div>
<div class="text-muted">
@for(synm of item.series.synonyms; track synm; let last = $last) {
{{synm}}
@ -19,6 +19,7 @@
@if (item.series.summary) {
<div>
<app-read-more [text]="item.series.summary" [showToggle]="false"></app-read-more>
<span class="me-1"><a (click)="$event.stopPropagation()" [href]="item.series.siteUrl" rel="noreferrer noopener" target="_blank">{{t('details')}}</a></span>
</div>
}
</div>
@ -30,8 +31,7 @@
<span class="ms-2">{{t('updating-metadata-status')}}</span>
</div>
} @else {
<div class="d-flex p-1 justify-content-between">
<span class="me-1"><a (click)="$event.stopPropagation()" [href]="item.series.siteUrl" rel="noreferrer noopener" target="_blank">{{t('details')}}</a></span>
<div class="d-flex pt-3 justify-content-between">
@if ((item.series.volumes || 0) > 0 || (item.series.chapters || 0) > 0) {
<span class="me-1">{{t('volume-count', {num: item.series.volumes})}}</span>
<span class="me-1">{{t('chapter-count', {num: item.series.chapters})}}</span>
@ -40,11 +40,8 @@
}
<span class="me-1">{{item.series.plusMediaFormat | plusMediaFormat}}</span>
<span class="me-1">({{item.matchRating | translocoPercent}})</span>
</div>
}
</div>
</ng-container>

View file

@ -0,0 +1,33 @@
.search-result {
img {
max-width: 100px;
min-width: 100px;
}
}
.title {
font-size: 1.2rem;
font-weight: bold;
margin: 0;
padding: 0;
}
.match-item-container {
&.dark {
background-color: var(--elevation-layer6-dark);
}
&.light {
background-color: var(--elevation-layer6);
}
border-radius: 15px;
&:hover {
&.dark {
background-color: var(--elevation-layer11-dark);
}
&.light {
background-color: var(--elevation-layer11);
}
}
}

View file

@ -37,6 +37,7 @@ export class MatchSeriesResultItemComponent {
private readonly cdRef = inject(ChangeDetectorRef);
@Input({required: true}) item!: ExternalSeriesMatch;
@Input({required: true}) isDarkMode = true;
@Output() selected: EventEmitter<ExternalSeriesMatch> = new EventEmitter();
isSelected = false;

View file

@ -165,7 +165,7 @@
@if(settingsForm.get('blacklist'); as formControl) {
<app-setting-item [title]="t('blacklist-label')" [subtitle]="t('blacklist-tooltip')">
<ng-template #view>
@let val = (formControl.value || '').split(',');
@let val = breakTags(formControl.value);
@for(opt of val; track opt) {
<app-tag-badge>{{opt.trim()}}</app-tag-badge>
@ -184,7 +184,7 @@
@if(settingsForm.get('whitelist'); as formControl) {
<app-setting-item [title]="t('whitelist-label')" [subtitle]="t('whitelist-tooltip')">
<ng-template #view>
@let val = (formControl.value || '').split(',');
@let val = breakTags(formControl.value);
@for(opt of val; track opt) {
<app-tag-badge>{{opt.trim()}}</app-tag-badge>

View file

@ -149,6 +149,15 @@ export class ManageMetadataSettingsComponent implements OnInit {
}
breakTags(csString: string) {
if (csString) {
return csString.split(',');
}
return [];
}
packData(withFieldMappings: boolean = true) {
const model = this.settingsForm.value;

View file

@ -15,6 +15,7 @@
<app-update-section [items]="update.theme" [title]="t('theme')"></app-update-section>
<app-update-section [items]="update.removed" [title]="t('removed')"></app-update-section>
<app-update-section [items]="update.api" [title]="t('api')"></app-update-section>
<app-update-section [items]="update.featureRequests" [title]="t('feature-requests')"></app-update-section>
</div>
@if (showExtras) {

View file

@ -33,7 +33,7 @@ export class ChangelogComponent implements OnInit {
isLoading: boolean = true;
ngOnInit(): void {
this.serverService.getChangelog(10).subscribe(updates => {
this.serverService.getChangelog(30).subscribe(updates => {
this.updates = updates;
this.isLoading = false;
this.cdRef.markForCheck();

View file

@ -1,7 +1,6 @@
<ng-container *transloco="let t; read:'new-version-modal'">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{t('title')}}</h4>
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="close()"></button>
</div>
<div class="modal-body scrollable-modal">
@if (update) {
@ -9,7 +8,6 @@
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="close()">{{t('close')}}</button>
<button type="button" class="btn btn-primary" (click)="refresh()">{{t('refresh')}}</button>
</div>

View file

@ -649,7 +649,7 @@
<h4>{{t('volumes-title')}}</h4>
@if (isLoadingVolumes) {
<div class="spinner-border text-secondary" role="status">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">{{t('loading')}}</span>
</div>
} @else {

View file

@ -18,7 +18,7 @@ import { LayoutMode } from '../../_models/layout-mode';
import { FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from '../../_models/reader-enums';
import { ReaderSetting } from '../../_models/reader-setting';
import { ImageRenderer } from '../../_models/renderer';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
import { NgClass, AsyncPipe } from '@angular/common';
@ -67,7 +67,7 @@ export class CanvasRendererComponent implements OnInit, AfterViewInit, ImageRend
constructor(private readonly cdRef: ChangeDetectorRef, private mangaReaderService: ManagaReaderService, private readerService: ReaderService) { }
constructor(private readonly cdRef: ChangeDetectorRef, private mangaReaderService: MangaReaderService, private readerService: ReaderService) { }
ngOnInit(): void {
this.readerSettings$.pipe(takeUntilDestroyed(this.destroyRef), tap((value: ReaderSetting) => {

View file

@ -18,7 +18,7 @@ import { LayoutMode } from '../../_models/layout-mode';
import { FITTING_OPTION, PAGING_DIRECTION } from '../../_models/reader-enums';
import { ReaderSetting } from '../../_models/reader-setting';
import { DEBUG_MODES } from '../../_models/renderer';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
@ -82,7 +82,7 @@ export class DoubleNoCoverRendererComponent implements OnInit {
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: MangaReaderService,
@Inject(DOCUMENT) private document: Document, public readerService: ReaderService) { }
ngOnInit(): void {

View file

@ -18,7 +18,7 @@ import { LayoutMode } from '../../_models/layout-mode';
import { FITTING_OPTION, PAGING_DIRECTION } from '../../_models/reader-enums';
import { ReaderSetting } from '../../_models/reader-setting';
import { DEBUG_MODES, ImageRenderer } from '../../_models/renderer';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
@ -80,7 +80,7 @@ export class DoubleRendererComponent implements OnInit, ImageRenderer {
protected readonly LayoutMode = LayoutMode;
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: MangaReaderService,
@Inject(DOCUMENT) private document: Document, public readerService: ReaderService) { }
ngOnInit(): void {

View file

@ -18,7 +18,7 @@ import { LayoutMode } from '../../_models/layout-mode';
import { FITTING_OPTION, PAGING_DIRECTION } from '../../_models/reader-enums';
import { ReaderSetting } from '../../_models/reader-setting';
import { DEBUG_MODES, ImageRenderer } from '../../_models/renderer';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
@ -84,7 +84,7 @@ export class DoubleReverseRendererComponent implements OnInit, ImageRenderer {
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: MangaReaderService,
@Inject(DOCUMENT) private document: Document, public readerService: ReaderService) { }
ngOnInit(): void {

View file

@ -22,7 +22,7 @@ import { ScrollService } from 'src/app/_services/scroll.service';
import { ReaderService } from '../../../_services/reader.service';
import { PAGING_DIRECTION } from '../../_models/reader-enums';
import { WebtoonImage } from '../../_models/webtoon-image';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {TranslocoDirective} from "@jsverse/transloco";
import {InfiniteScrollModule} from "ngx-infinite-scroll";
@ -66,7 +66,7 @@ const enum DEBUG_MODES {
})
export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
private readonly mangaReaderService = inject(ManagaReaderService);
private readonly mangaReaderService = inject(MangaReaderService);
private readonly readerService = inject(ReaderService);
private readonly renderer = inject(Renderer2);
private readonly scrollService = inject(ScrollService);

View file

@ -52,7 +52,7 @@ import {ReaderService} from 'src/app/_services/reader.service';
import {LayoutMode} from '../../_models/layout-mode';
import {FITTING_OPTION, PAGING_DIRECTION} from '../../_models/reader-enums';
import {ReaderSetting} from '../../_models/reader-setting';
import {ManagaReaderService} from '../../_service/managa-reader.service';
import {MangaReaderService} from '../../_service/manga-reader.service';
import {CanvasRendererComponent} from '../canvas-renderer/canvas-renderer.component';
import {DoubleRendererComponent} from '../double-renderer/double-renderer.component';
import {DoubleReverseRendererComponent} from '../double-reverse-renderer/double-reverse-renderer.component';
@ -99,7 +99,7 @@ enum KeyDirection {
templateUrl: './manga-reader.component.html',
styleUrls: ['./manga-reader.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ManagaReaderService],
providers: [MangaReaderService],
animations: [
trigger('slideFromTop', [
state('in', style({ transform: 'translateY(0)' })),
@ -153,7 +153,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
private readonly toastr = inject(ToastrService);
public readonly readerService = inject(ReaderService);
public readonly utilityService = inject(UtilityService);
public readonly mangaReaderService = inject(ManagaReaderService);
public readonly mangaReaderService = inject(MangaReaderService);
protected readonly KeyDirection = KeyDirection;
protected readonly ReaderMode = ReaderMode;

View file

@ -17,7 +17,7 @@ import { LayoutMode } from '../../_models/layout-mode';
import { FITTING_OPTION, PAGING_DIRECTION } from '../../_models/reader-enums';
import { ReaderSetting } from '../../_models/reader-setting';
import { ImageRenderer } from '../../_models/renderer';
import { ManagaReaderService } from '../../_service/managa-reader.service';
import { MangaReaderService } from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
@ -61,7 +61,7 @@ export class SingleRendererComponent implements OnInit, ImageRenderer {
get ReaderMode() {return ReaderMode;}
get LayoutMode() {return LayoutMode;}
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: MangaReaderService,
@Inject(DOCUMENT) private document: Document) { }
ngOnInit(): void {

View file

@ -6,12 +6,11 @@ import { ChapterInfo } from '../_models/chapter-info';
import { DimensionMap } from '../_models/file-dimension';
import { FITTING_OPTION } from '../_models/reader-enums';
import { BookmarkInfo } from 'src/app/_models/manga-reader/bookmark-info';
import {ReaderMode} from "../../_models/preferences/reader-mode";
@Injectable({
providedIn: 'root'
})
export class ManagaReaderService {
export class MangaReaderService {
private pageDimensions: DimensionMap = {};
private pairs: {[key: number]: number} = {};
@ -168,7 +167,7 @@ export class ManagaReaderService {
}
// Boost score if width is small (≤ 800px, common in webtoons)
if (info.width <= 800) {
if (info.width <= 750) {
score += 0.5; // Adjust weight as needed
}

View file

@ -1,7 +1,7 @@
<ng-container *transloco="let t;">
<div>
<div class="settings-row g-0 row">
<div class="col-10 setting-title">
<div class="col-auto setting-title edit">
<h6 class="section-title">
@if (labelId) {
<label class="reset-label" [for]="labelId">{{title}}</label>
@ -13,7 +13,7 @@
}
</h6>
</div>
<div class="col-2 text-end align-self-end justify-content-end">
<div class="col-auto text-end align-self-end justify-content-end edit-btn">
@if (showEdit) {
<button type="button" class="btn btn-text btn-sm" (click)="toggleEditMode()" [disabled]="!canEdit">
{{isEditMode ? t('common.close') : (editLabel || t('common.edit'))}}

View file

@ -11,3 +11,24 @@
color: var(--primary-color);
cursor: pointer;
}
.setting-title.edit:hover ~ .edit-btn{
opacity: 1;
transition: opacity 0.3s ease-out;
}
.edit-btn {
opacity: 0;
transition: opacity 0.5s ease-out;
transition-delay: 0.5s;
&:hover {
opacity: 1;
transition: opacity 0.3s ease-out;
}
.btn {
margin-bottom: 0.5em;
line-height: 1.2;
}
}

View file

@ -1,14 +1,15 @@
<ng-container *transloco="let t;">
<div>
<div class="row g-0 mb-2">
<div class="col-11">
<h6 class="section-title" [id]="id || title">{{title}}</h6>
</div>
<div class="col-1">
<div class="col-auto">
@if (switchRef) {
<ng-container [ngTemplateOutlet]="switchRef"></ng-container>
}
</div>
<div class="col-auto">
<h6 class="section-title" [id]="id || title">{{title}}</h6>
</div>
</div>

View file

@ -7,6 +7,8 @@ h2 {
.main-container {
margin-top: 10px;
max-width: 1920px;
padding: 0 20px;
}
::ng-deep .content-wrapper:not(.closed) {

View file

@ -119,7 +119,7 @@ export class ManageUserPreferencesComponent implements OnInit {
get Locale() {
if (!this.settingsForm.get('locale')) return 'English';
console.log(this.locales.filter(l => l.isoCode === this.settingsForm.get('locale')!.value)[0])
return this.locales.filter(l => l.isoCode === this.settingsForm.get('locale')!.value)[0].title;
}

View file

@ -656,7 +656,8 @@
"removed": "Removed",
"api": "API",
"published-label": "Published: ",
"installed": "{{changelog.installed}}"
"installed": "{{changelog.installed}}",
"feature-requests": "Feature Requests"
},
"new-version-modal": {

View file

@ -51,6 +51,7 @@
@import './theme/utilities/utilities';
@import './theme/utilities/animations';
@import './theme/utilities/global';
@import "./theme/utilities/spinners";
// Global Styles

View file

@ -196,7 +196,7 @@
.side-nav-header {
color: #ffffff;
font-size: 0.9375rem;
font-size: 1rem;
&:first-of-type {
margin-top: 0.7rem;
@ -204,7 +204,7 @@
}
.side-nav-item {
font-size: 0.9rem;
font-size: 1rem;
min-height: 1.875rem;
justify-content: unset;
margin-left: 1.125rem;
@ -218,7 +218,7 @@
.side-nav-text {
text-align: unset;
margin-left: 0.75rem;
font-size: 0.8125rem;
font-size: 0.9rem;
color: #999999;
}

View file

@ -0,0 +1,4 @@
.text-primary {
color: var(--primary-color) !important;
}