mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Fit Split to Screen (#769)
* Updated readme with new host information and new feature site. * Implemented basic fit to screen splitting option for manga reader such that the reader will try to fit the whole cover on the screen via scaling it. Updated a bunch of defaults in the preferences to give a better experience for first installs. * Refactored the stat scheduling code slightly to clean it up and have better logging. * Replaced @import with @use to lower css bundling. * Changed up the defaults for the reading preferences to give a better experience. Fixed a duplicate render on automatic scaling due to emitting a valuechange with automatic scaling changing fit. Implemented basic form of fit to screen. Still needs some tweaking and optimization. * Update link to new feature server and update kavita homepage to use www. * Updated the serverInfo to match backend. Tweaked some of the css for the changelog * Added publish date for changelog * First page works except for tablet * I'm stumped, taking a break * Hide the arrow for nav events * Ensure specials in reading lists don't have their extensions visible * Testing out removing no-connection * Fixed a bug in infinite scroller where next chapter spacer when clicked would emit for prev chapter load. Fixed an issue where next/prev chapter loaders would execute when they shouldn't. * Fit Split is working in all cases as of this code. New optimization is still needed. * Fit to screen is now working well * Updated the bookmark effect to look much better * Updated new issue template to inform users to request features on our site. * Removed an empty migration
This commit is contained in:
parent
199398df95
commit
3bfbd042a8
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -7,6 +7,10 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**If this is a feature request, request [here](https://feats.kavitareader.com/) instead. Feature requests will be deleted from Github.**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace API.DTOs.Update
|
using System;
|
||||||
|
|
||||||
|
namespace API.DTOs.Update
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update Notification denoting a new release available for user to update to
|
/// Update Notification denoting a new release available for user to update to
|
||||||
@ -34,5 +36,9 @@
|
|||||||
/// Is this a pre-release
|
/// Is this a pre-release
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsPrerelease { get; init; }
|
public bool IsPrerelease { get; init; }
|
||||||
|
/// <summary>
|
||||||
|
/// Date of the publish
|
||||||
|
/// </summary>
|
||||||
|
public string PublishDate { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace API.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manga Reader Option: Which side of a split image should we show first
|
/// Manga Reader Option: Which side of a split image should we show first
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.SplitRightToLeft;
|
public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.FitSplit;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manga Reader Option: How the manga reader should perform paging or reading of the file
|
/// Manga Reader Option: How the manga reader should perform paging or reading of the file
|
||||||
/// <example>
|
/// <example>
|
||||||
@ -25,14 +25,15 @@ namespace API.Entities
|
|||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReaderMode ReaderMode { get; set; }
|
public ReaderMode ReaderMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manga Reader Option: Allow the menu to close after 6 seconds without interaction
|
/// Manga Reader Option: Allow the menu to close after 6 seconds without interaction
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoCloseMenu { get; set; }
|
public bool AutoCloseMenu { get; set; } = true;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Book Reader Option: Should the background color be dark
|
/// Book Reader Option: Should the background color be dark
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool BookReaderDarkMode { get; set; } = false;
|
public bool BookReaderDarkMode { get; set; } = true;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Book Reader Option: Override extra Margin
|
/// Book Reader Option: Override extra Margin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
{
|
{
|
||||||
SplitLeftToRight = 0,
|
SplitLeftToRight = 0,
|
||||||
SplitRightToLeft = 1,
|
SplitRightToLeft = 1,
|
||||||
NoSplit = 2
|
NoSplit = 2,
|
||||||
|
FitSplit = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,6 +17,6 @@ namespace API.Interfaces
|
|||||||
void RefreshSeriesMetadata(int libraryId, int seriesId, bool forceUpdate = false);
|
void RefreshSeriesMetadata(int libraryId, int seriesId, bool forceUpdate = false);
|
||||||
void ScanSeries(int libraryId, int seriesId, bool forceUpdate = false);
|
void ScanSeries(int libraryId, int seriesId, bool forceUpdate = false);
|
||||||
void CancelStatsTasks();
|
void CancelStatsTasks();
|
||||||
void RunStatCollection();
|
Task RunStatCollection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace API.Services.HostedServices
|
|||||||
// These methods will automatically check if stat collection is disabled to prevent sending any data regardless
|
// These methods will automatically check if stat collection is disabled to prevent sending any data regardless
|
||||||
// of when setting was changed
|
// of when setting was changed
|
||||||
await taskScheduler.ScheduleStatsTasks();
|
await taskScheduler.ScheduleStatsTasks();
|
||||||
taskScheduler.RunStatCollection();
|
await taskScheduler.RunStatCollection();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,6 @@ namespace API.Services
|
|||||||
|
|
||||||
private readonly IStatsService _statsService;
|
private readonly IStatsService _statsService;
|
||||||
private readonly IVersionUpdaterService _versionUpdaterService;
|
private readonly IVersionUpdaterService _versionUpdaterService;
|
||||||
private const string SendDataTask = "finalize-stats";
|
|
||||||
|
|
||||||
public static BackgroundJobServer Client => new BackgroundJobServer();
|
public static BackgroundJobServer Client => new BackgroundJobServer();
|
||||||
private static readonly Random Rnd = new Random();
|
private static readonly Random Rnd = new Random();
|
||||||
@ -89,19 +88,27 @@ namespace API.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Scheduling stat collection daily");
|
_logger.LogDebug("Scheduling stat collection daily");
|
||||||
RecurringJob.AddOrUpdate(SendDataTask, () => _statsService.Send(), Cron.Daily(Rnd.Next(0, 22)), TimeZoneInfo.Local);
|
RecurringJob.AddOrUpdate("report-stats", () => _statsService.Send(), Cron.Daily(Rnd.Next(0, 22)), TimeZoneInfo.Local);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelStatsTasks()
|
public void CancelStatsTasks()
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Cancelling/Removing StatsTasks");
|
_logger.LogDebug("Cancelling/Removing StatsTasks");
|
||||||
|
|
||||||
RecurringJob.RemoveIfExists(SendDataTask);
|
RecurringJob.RemoveIfExists("report-stats");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunStatCollection()
|
/// <summary>
|
||||||
|
/// First time run stat collection. Executes immediately on a background thread. Does not block.
|
||||||
|
/// </summary>
|
||||||
|
public async Task RunStatCollection()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Enqueuing stat collection");
|
var allowStatCollection = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).AllowStatCollection;
|
||||||
|
if (!allowStatCollection)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("User has opted out of stat collection, not sending stats");
|
||||||
|
return;
|
||||||
|
}
|
||||||
BackgroundJob.Enqueue(() => _statsService.Send());
|
BackgroundJob.Enqueue(() => _statsService.Send());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,11 @@ namespace API.Services.Tasks
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public string Html_Url { get; init; }
|
public string Html_Url { get; init; }
|
||||||
|
/// <summary>
|
||||||
|
/// Date Release was Published
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public string Published_At { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UntrustedCertClientFactory : DefaultHttpClientFactory
|
public class UntrustedCertClientFactory : DefaultHttpClientFactory
|
||||||
@ -109,7 +114,8 @@ namespace API.Services.Tasks
|
|||||||
UpdateBody = _markdown.Transform(update.Body.Trim()),
|
UpdateBody = _markdown.Transform(update.Body.Trim()),
|
||||||
UpdateTitle = update.Name,
|
UpdateTitle = update.Name,
|
||||||
UpdateUrl = update.Html_Url,
|
UpdateUrl = update.Html_Url,
|
||||||
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker
|
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker,
|
||||||
|
PublishDate = update.Published_At
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
We're always looking for people to help make Kavita even better, there are a number of ways to contribute.
|
We're always looking for people to help make Kavita even better, there are a number of ways to contribute.
|
||||||
|
|
||||||
## Documentation ##
|
## Documentation ##
|
||||||
Setup guides, FAQ, the more information we have on the [wiki](https://github.com/Kareadita/Kavita/wiki) the better.
|
Setup guides, FAQ, the more information we have on the [wiki](https://wiki.kavitareader.com/) the better.
|
||||||
|
|
||||||
## Development ##
|
## Development ##
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@ -80,15 +80,15 @@ services:
|
|||||||
**Note: Kavita is under heavy development and is being updated all the time, so the tag for current builds is `:nightly`. The `:latest` tag will be the latest stable release.**
|
**Note: Kavita is under heavy development and is being updated all the time, so the tag for current builds is `:nightly`. The `:latest` tag will be the latest stable release.**
|
||||||
|
|
||||||
## Feature Requests
|
## Feature Requests
|
||||||
Got a great idea? Throw it up on the FeatHub or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features.
|
Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features.
|
||||||
|
|
||||||
[](https://feathub.com/Kareadita/Kavita)
|
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).
|
This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).
|
||||||
<a href="https://github.com/Kareadita/Kavita/graphs/contributors"><img src="https://opencollective.com/kavita/contributors.svg?width=890&button=false" /></a>
|
<a href="https://github.com/Kareadita/Kavita/graphs/contributors">
|
||||||
|
<img src="https://opencollective.com/kavita/contributors.svg?width=890&button=false&avatarHeight=42" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
@ -99,7 +99,7 @@ expenses related to Kavita. Back us through [OpenCollective](https://opencollect
|
|||||||
|
|
||||||
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Kavita#backer)
|
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Kavita#backer)
|
||||||
|
|
||||||
<img src="https://opencollective.com/Kavita/backers.svg?width=890"></a>
|
<img src="https://opencollective.com/kavita/backers.svg?width=890&avatarHeight=42"></a>
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
@ -116,9 +116,6 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="" width="32"> JetBrains](http:
|
|||||||
* [<img src="/Logo/rider.svg" alt="" width="32"> Rider](http://www.jetbrains.com/rider/)
|
* [<img src="/Logo/rider.svg" alt="" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||||
* [<img src="/Logo/dottrace.svg" alt="" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
* [<img src="/Logo/dottrace.svg" alt="" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
||||||
|
|
||||||
## Sentry
|
|
||||||
Thank you to [<img src="/Logo/sentry.svg" alt="" width="64">](https://sentry.io/welcome/) for providing us with free license to their software.
|
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||||
|
@ -46,10 +46,10 @@ export class ErrorInterceptor implements HttpInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we are not on no-connection, redirect there and save current url so when we refersh, we redirect back there
|
// If we are not on no-connection, redirect there and save current url so when we refersh, we redirect back there
|
||||||
if (this.router.url !== '/no-connection') {
|
// if (this.router.url !== '/no-connection') {
|
||||||
localStorage.setItem(this.urlKey, this.router.url);
|
// localStorage.setItem(this.urlKey, this.router.url);
|
||||||
this.router.navigateByUrl('/no-connection');
|
// this.router.navigateByUrl('/no-connection');
|
||||||
}
|
// }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return throwError(error);
|
return throwError(error);
|
||||||
|
@ -5,4 +5,5 @@ export interface UpdateVersionEvent {
|
|||||||
updateTitle: string;
|
updateTitle: string;
|
||||||
updateUrl: string;
|
updateUrl: string;
|
||||||
isDocker: boolean;
|
isDocker: boolean;
|
||||||
|
publishDate: string;
|
||||||
}
|
}
|
@ -1,5 +1,18 @@
|
|||||||
export enum PageSplitOption {
|
export enum PageSplitOption {
|
||||||
|
/**
|
||||||
|
* Renders the left side of the image then the right side
|
||||||
|
*/
|
||||||
SplitLeftToRight = 0,
|
SplitLeftToRight = 0,
|
||||||
|
/**
|
||||||
|
* Renders the right side of the image then the left side
|
||||||
|
*/
|
||||||
SplitRightToLeft = 1,
|
SplitRightToLeft = 1,
|
||||||
NoSplit = 2
|
/**
|
||||||
|
* Don't split and show the image in original size
|
||||||
|
*/
|
||||||
|
NoSplit = 2,
|
||||||
|
/**
|
||||||
|
* Don't split and scale the image to fit screen space
|
||||||
|
*/
|
||||||
|
FitSplit = 3
|
||||||
}
|
}
|
||||||
|
@ -26,5 +26,5 @@ export interface Preferences {
|
|||||||
|
|
||||||
export const readingDirections = [{text: 'Left to Right', value: ReadingDirection.LeftToRight}, {text: 'Right to Left', value: ReadingDirection.RightToLeft}];
|
export const readingDirections = [{text: 'Left to Right', value: ReadingDirection.LeftToRight}, {text: 'Right to Left', value: ReadingDirection.RightToLeft}];
|
||||||
export const scalingOptions = [{text: 'Automatic', value: ScalingOption.Automatic}, {text: 'Fit to Height', value: ScalingOption.FitToHeight}, {text: 'Fit to Width', value: ScalingOption.FitToWidth}, {text: 'Original', value: ScalingOption.Original}];
|
export const scalingOptions = [{text: 'Automatic', value: ScalingOption.Automatic}, {text: 'Fit to Height', value: ScalingOption.FitToHeight}, {text: 'Fit to Width', value: ScalingOption.FitToWidth}, {text: 'Original', value: ScalingOption.Original}];
|
||||||
export const pageSplitOptions = [{text: 'Right to Left', value: PageSplitOption.SplitRightToLeft}, {text: 'Left to Right', value: PageSplitOption.SplitLeftToRight}, {text: 'No Split', value: PageSplitOption.NoSplit}];
|
export const pageSplitOptions = [{text: 'Fit to Screen', value: PageSplitOption.FitSplit}, {text: 'Right to Left', value: PageSplitOption.SplitRightToLeft}, {text: 'Left to Right', value: PageSplitOption.SplitLeftToRight}, {text: 'No Split', value: PageSplitOption.NoSplit}];
|
||||||
export const readingModes = [{text: 'Left to Right', value: READER_MODE.MANGA_LR}, {text: 'Up to Down', value: READER_MODE.MANGA_UD}, {text: 'Webtoon', value: READER_MODE.WEBTOON}];
|
export const readingModes = [{text: 'Left to Right', value: READER_MODE.MANGA_LR}, {text: 'Up to Down', value: READER_MODE.MANGA_UD}, {text: 'Webtoon', value: READER_MODE.WEBTOON}];
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
export interface ServerInfo {
|
export interface ServerInfo {
|
||||||
os: string;
|
os: string;
|
||||||
dotNetVersion: string;
|
dotnetVersion: string;
|
||||||
runTimeVersion: string;
|
runTimeVersion: string;
|
||||||
kavitaVersion: string;
|
kavitaVersion: string;
|
||||||
buildBranch: string;
|
NumOfCores: number;
|
||||||
culture: string;
|
installId: string;
|
||||||
|
isDocker: boolean;
|
||||||
}
|
}
|
@ -1,15 +1,19 @@
|
|||||||
|
<div class="changelog">
|
||||||
<ng-container *ngFor="let update of updates; let indx = index;">
|
<ng-container *ngFor="let update of updates; let indx = index;">
|
||||||
<div class="card w-100 mb-2" style="width: 18rem;">
|
<div class="card w-100 mb-2" style="width: 18rem;">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">{{update.updateTitle}}
|
<h4 class="card-title">{{update.updateTitle}}
|
||||||
<span class="badge badge-secondary" *ngIf="update.updateVersion === update.currentVersion">Installed</span>
|
<span class="badge badge-secondary" *ngIf="update.updateVersion === update.currentVersion">Installed</span>
|
||||||
<span class="badge badge-secondary" *ngIf="update.updateVersion > update.currentVersion">Available</span>
|
<span class="badge badge-secondary" *ngIf="update.updateVersion > update.currentVersion">Available</span>
|
||||||
</h5>
|
</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>
|
<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-right" target="_blank">Download</a>
|
<a *ngIf="!update.isDocker" href="{{update.updateUrl}}" class="btn btn-{{indx === 0 ? 'primary' : 'secondary'}} float-right" target="_blank">Download</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="spinner-border text-secondary" *ngIf="isLoading" role="status">
|
<div class="spinner-border text-secondary" *ngIf="isLoading" role="status">
|
||||||
|
@ -3,3 +3,19 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .changelog {
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ul {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@
|
|||||||
<dt>Version</dt>
|
<dt>Version</dt>
|
||||||
<dd>{{serverInfo.kavitaVersion}}</dd>
|
<dd>{{serverInfo.kavitaVersion}}</dd>
|
||||||
|
|
||||||
<dt>.NET Version</dt>
|
<dt>Install ID</dt>
|
||||||
<dd>{{serverInfo.dotNetVersion}}</dd>
|
<dd>{{serverInfo.installId}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -43,7 +43,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">Home page:</div>
|
<div class="col-4">Home page:</div>
|
||||||
<div class="col"><a href="https://kavitareader.com" target="_blank">kavitareader.com</a></div>
|
<div class="col"><a href="https://www.kavitareader.com" target="_blank">kavitareader.com</a></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">Wiki:</div>
|
<div class="col-4">Wiki:</div>
|
||||||
@ -63,7 +63,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">Feature Requests:</div>
|
<div class="col-4">Feature Requests:</div>
|
||||||
<div class="col"><a href="https://feathub.com/Kareadita/Kavita" target="_blank">Feathub</a><br/>
|
<div class="col"><a href="https://feats.kavitareader.com" target="_blank">https://feats.kavitareader.com</a><br/>
|
||||||
<a href="https://github.com/Kareadita/Kavita/issues" target="_blank">Github issues</a></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,8 +1,8 @@
|
|||||||
@import "../../../theme/colors";
|
@use "../../../theme/colors";
|
||||||
|
|
||||||
.bulk-select {
|
.bulk-select {
|
||||||
background-color: $dark-form-background-no-opacity;
|
background-color: colors.$dark-form-background-no-opacity;
|
||||||
border-bottom: 2px solid $primary-color;
|
border-bottom: 2px solid colors.$primary-color;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,5 +11,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
color: $primary-color !important;
|
color: colors.$primary-color !important;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
@import '../../../theme/colors';
|
@use '../../../theme/colors';
|
||||||
$image-height: 230px;
|
$image-height: 230px;
|
||||||
$image-width: 160px;
|
$image-width: 160px;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ $image-width: 160px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
outline: 5px solid $primary-color;
|
outline: 5px solid colors.$primary-color;
|
||||||
outline-width: medium;
|
outline-width: medium;
|
||||||
outline-offset: -1px;
|
outline-offset: -1px;
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ $image-width: 160px;
|
|||||||
ngx-file-drop ::ng-deep > div {
|
ngx-file-drop ::ng-deep > div {
|
||||||
// styling for the outer drop box
|
// styling for the outer drop box
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 2px solid $primary-color;
|
border: 2px solid colors.$primary-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<ng-container *ngFor="let item of webtoonImages | async; let index = index;">
|
<ng-container *ngFor="let item of webtoonImages | async; let index = index;">
|
||||||
<img src="{{item.src}}" style="display: block" class="mx-auto {{pageNum === item.page && showDebugOutline() ? 'active': ''}} {{areImagesWiderThanWindow ? 'full-width' : ''}}" *ngIf="pageNum >= pageNum - bufferPages && pageNum <= pageNum + bufferPages" rel="nofollow" alt="image" (load)="onImageLoad($event)" id="page-{{item.page}}" [attr.page]="item.page" ondragstart="return false;" onselectstart="return false;">
|
<img src="{{item.src}}" style="display: block" class="mx-auto {{pageNum === item.page && showDebugOutline() ? 'active': ''}} {{areImagesWiderThanWindow ? 'full-width' : ''}}" *ngIf="pageNum >= pageNum - bufferPages && pageNum <= pageNum + bufferPages" rel="nofollow" alt="image" (load)="onImageLoad($event)" id="page-{{item.page}}" [attr.page]="item.page" ondragstart="return false;" onselectstart="return false;">
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div *ngIf="atBottom" class="spacer bottom" role="alert" (click)="loadPrevChapter.emit()">
|
<div *ngIf="atBottom" class="spacer bottom" role="alert" (click)="loadNextChapter.emit()">
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-icon mx-auto">
|
<button class="btn btn-icon mx-auto">
|
||||||
<i class="fa fa-angle-double-down animate" aria-hidden="true"></i>
|
<i class="fa fa-angle-double-down animate" aria-hidden="true"></i>
|
||||||
|
@ -102,9 +102,7 @@
|
|||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<select class="form-control" id="page-splitting" formControlName="pageSplitOption">
|
<select class="form-control" id="page-splitting" formControlName="pageSplitOption">
|
||||||
<option [value]="1">Right to Left</option>
|
<option *ngFor="let opt of pageSplitOptions" [value]="opt.value">{{opt.text}}</option>
|
||||||
<option [value]="0">Left to Right</option>
|
|
||||||
<option [value]="2">None</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import '../../theme/colors';
|
@use '../../theme/colors';
|
||||||
|
|
||||||
$center-width: 50%;
|
$center-width: 50%;
|
||||||
$side-width: 25%;
|
$side-width: 25%;
|
||||||
@ -178,7 +178,7 @@ canvas {
|
|||||||
height: 2px;
|
height: 2px;
|
||||||
}
|
}
|
||||||
.custom-slider .ngx-slider .ngx-slider-selection {
|
.custom-slider .ngx-slider .ngx-slider-selection {
|
||||||
background: $primary-color;
|
background: colors.$primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-slider .ngx-slider .ngx-slider-pointer {
|
.custom-slider .ngx-slider .ngx-slider-pointer {
|
||||||
@ -186,7 +186,7 @@ canvas {
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
top: auto; /* to remove the default positioning */
|
top: auto; /* to remove the default positioning */
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: $primary-color; // #333;
|
background-color: colors.$primary-color; // #333;
|
||||||
border-top-left-radius: 3px;
|
border-top-left-radius: 3px;
|
||||||
border-top-right-radius: 3px;
|
border-top-right-radius: 3px;
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-slider .ngx-slider .ngx-slider-tick.ngx-slider-selected {
|
.custom-slider .ngx-slider .ngx-slider-tick.ngx-slider-selected {
|
||||||
background: $primary-color;
|
background: colors.$primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,19 +237,14 @@ canvas {
|
|||||||
|
|
||||||
|
|
||||||
.bookmark-effect {
|
.bookmark-effect {
|
||||||
animation: bookmark 1s cubic-bezier(0.165, 0.84, 0.44, 1);
|
animation: bookmark 0.7s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes bookmark {
|
@keyframes bookmark {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
filter: opacity(1);
|
border: 0px;
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
filter: opacity(0.25);
|
border: 5px solid colors.$primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG
|
|
||||||
.active-image {
|
|
||||||
border: 5px solid red;
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ import { ScalingOption } from '../_models/preferences/scaling-option';
|
|||||||
import { PageSplitOption } from '../_models/preferences/page-split-option';
|
import { PageSplitOption } from '../_models/preferences/page-split-option';
|
||||||
import { forkJoin, ReplaySubject, Subject } from 'rxjs';
|
import { forkJoin, ReplaySubject, Subject } from 'rxjs';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
|
import { KEY_CODES, UtilityService, Breakpoint } from '../shared/_services/utility.service';
|
||||||
import { CircularArray } from '../shared/data-structures/circular-array';
|
import { CircularArray } from '../shared/data-structures/circular-array';
|
||||||
import { MemberService } from '../_services/member.service';
|
import { MemberService } from '../_services/member.service';
|
||||||
import { Stack } from '../shared/data-structures/stack';
|
import { Stack } from '../shared/data-structures/stack';
|
||||||
@ -20,7 +20,7 @@ import { ChangeContext, LabelType, Options } from '@angular-slider/ngx-slider';
|
|||||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||||
import { ChapterInfo } from './_models/chapter-info';
|
import { ChapterInfo } from './_models/chapter-info';
|
||||||
import { COLOR_FILTER, FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from './_models/reader-enums';
|
import { COLOR_FILTER, FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from './_models/reader-enums';
|
||||||
import { scalingOptions } from '../_models/preferences/preferences';
|
import { pageSplitOptions, scalingOptions } from '../_models/preferences/preferences';
|
||||||
import { READER_MODE } from '../_models/preferences/reader-mode';
|
import { READER_MODE } from '../_models/preferences/reader-mode';
|
||||||
import { MangaFormat } from '../_models/manga-format';
|
import { MangaFormat } from '../_models/manga-format';
|
||||||
import { LibraryService } from '../_services/library.service';
|
import { LibraryService } from '../_services/library.service';
|
||||||
@ -96,13 +96,15 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
scalingOptions = scalingOptions;
|
scalingOptions = scalingOptions;
|
||||||
readingDirection = ReadingDirection.LeftToRight;
|
readingDirection = ReadingDirection.LeftToRight;
|
||||||
scalingOption = ScalingOption.FitToHeight;
|
scalingOption = ScalingOption.FitToHeight;
|
||||||
pageSplitOption = PageSplitOption.SplitRightToLeft;
|
pageSplitOption = PageSplitOption.FitSplit;
|
||||||
currentImageSplitPart: SPLIT_PAGE_PART = SPLIT_PAGE_PART.NO_SPLIT;
|
currentImageSplitPart: SPLIT_PAGE_PART = SPLIT_PAGE_PART.NO_SPLIT;
|
||||||
pagingDirection: PAGING_DIRECTION = PAGING_DIRECTION.FORWARD;
|
pagingDirection: PAGING_DIRECTION = PAGING_DIRECTION.FORWARD;
|
||||||
colorMode: COLOR_FILTER = COLOR_FILTER.NONE;
|
colorMode: COLOR_FILTER = COLOR_FILTER.NONE;
|
||||||
autoCloseMenu: boolean = true;
|
autoCloseMenu: boolean = true;
|
||||||
readerMode: READER_MODE = READER_MODE.MANGA_LR;
|
readerMode: READER_MODE = READER_MODE.MANGA_LR;
|
||||||
|
|
||||||
|
pageSplitOptions = pageSplitOptions;
|
||||||
|
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
@ViewChild('content') canvas: ElementRef | undefined;
|
@ViewChild('content') canvas: ElementRef | undefined;
|
||||||
@ -266,6 +268,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
return ReadingDirection;
|
return ReadingDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get PageSplitOption(): typeof PageSplitOption {
|
||||||
|
return PageSplitOption;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
||||||
public readerService: ReaderService, private location: Location,
|
public readerService: ReaderService, private location: Location,
|
||||||
private formBuilder: FormBuilder, private navService: NavService,
|
private formBuilder: FormBuilder, private navService: NavService,
|
||||||
@ -313,7 +319,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
this.generalSettingsForm = this.formBuilder.group({
|
this.generalSettingsForm = this.formBuilder.group({
|
||||||
autoCloseMenu: this.autoCloseMenu,
|
autoCloseMenu: this.autoCloseMenu,
|
||||||
pageSplitOption: this.pageSplitOption + '',
|
pageSplitOption: this.pageSplitOption,
|
||||||
fittingOption: this.translateScalingOption(this.scalingOption)
|
fittingOption: this.translateScalingOption(this.scalingOption)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -321,8 +327,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
this.generalSettingsForm.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((changes: SimpleChanges) => {
|
this.generalSettingsForm.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((changes: SimpleChanges) => {
|
||||||
this.autoCloseMenu = this.generalSettingsForm.get('autoCloseMenu')?.value;
|
this.autoCloseMenu = this.generalSettingsForm.get('autoCloseMenu')?.value;
|
||||||
// On change of splitting, re-render the page if the page is already split
|
const needsSplitting = this.isCoverImage();
|
||||||
const needsSplitting = this.canvasImage.width > this.canvasImage.height;
|
// If we need to split on a menu change, then we need to re-render.
|
||||||
if (needsSplitting) {
|
if (needsSplitting) {
|
||||||
this.loadPage();
|
this.loadPage();
|
||||||
}
|
}
|
||||||
@ -619,15 +625,20 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSplitLeftToRight() {
|
isSplitLeftToRight() {
|
||||||
return (this.generalSettingsForm?.get('pageSplitOption')?.value + '') === (PageSplitOption.SplitLeftToRight + '');
|
return parseInt(this.generalSettingsForm?.get('pageSplitOption')?.value, 10) === PageSplitOption.SplitLeftToRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns If the current model reflects no split of fit split
|
||||||
|
*/
|
||||||
isNoSplit() {
|
isNoSplit() {
|
||||||
return (this.generalSettingsForm?.get('pageSplitOption')?.value + '') === (PageSplitOption.NoSplit + '');
|
const splitValue = parseInt(this.generalSettingsForm?.get('pageSplitOption')?.value, 10);
|
||||||
|
return splitValue === PageSplitOption.NoSplit || splitValue === PageSplitOption.FitSplit;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSplitPage() {
|
updateSplitPage() {
|
||||||
const needsSplitting = this.canvasImage.width > this.canvasImage.height;
|
const needsSplitting = this.isCoverImage();
|
||||||
if (!needsSplitting || this.isNoSplit()) {
|
if (!needsSplitting || this.isNoSplit()) {
|
||||||
this.currentImageSplitPart = SPLIT_PAGE_PART.NO_SPLIT;
|
this.currentImageSplitPart = SPLIT_PAGE_PART.NO_SPLIT;
|
||||||
return;
|
return;
|
||||||
@ -739,6 +750,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
loadNextChapter() {
|
loadNextChapter() {
|
||||||
if (this.nextPageDisabled) { return; }
|
if (this.nextPageDisabled) { return; }
|
||||||
|
if (this.nextChapterDisabled) { return; }
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
if (this.nextChapterId === CHAPTER_ID_NOT_FETCHED || this.nextChapterId === this.chapterId) {
|
if (this.nextChapterId === CHAPTER_ID_NOT_FETCHED || this.nextChapterId === this.chapterId) {
|
||||||
this.readerService.getNextChapter(this.seriesId, this.volumeId, this.chapterId, this.readingListId).pipe(take(1)).subscribe(chapterId => {
|
this.readerService.getNextChapter(this.seriesId, this.volumeId, this.chapterId, this.readingListId).pipe(take(1)).subscribe(chapterId => {
|
||||||
@ -752,6 +764,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
loadPrevChapter() {
|
loadPrevChapter() {
|
||||||
if (this.prevPageDisabled) { return; }
|
if (this.prevPageDisabled) { return; }
|
||||||
|
if (this.prevChapterDisabled) { return; }
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.continuousChaptersStack.pop();
|
this.continuousChaptersStack.pop();
|
||||||
const prevChapter = this.continuousChaptersStack.peek();
|
const prevChapter = this.continuousChaptersStack.peek();
|
||||||
@ -819,21 +832,23 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (needsScaling) {
|
if (needsScaling) {
|
||||||
this.canvas.nativeElement.width = isSafari ? 4_096 : 16_384;
|
this.canvas.nativeElement.width = isSafari ? 4_096 : 16_384;
|
||||||
this.canvas.nativeElement.height = isSafari ? 4_096 : 16_384;
|
this.canvas.nativeElement.height = isSafari ? 4_096 : 16_384;
|
||||||
|
} else if (this.isCoverImage()) {
|
||||||
|
//this.canvas.nativeElement.width = this.canvasImage.width / 2;
|
||||||
|
//this.canvas.nativeElement.height = this.canvasImage.height;
|
||||||
} else {
|
} else {
|
||||||
this.canvas.nativeElement.width = this.canvasImage.width;
|
this.canvas.nativeElement.width = this.canvasImage.width;
|
||||||
this.canvas.nativeElement.height = this.canvasImage.height;
|
this.canvas.nativeElement.height = this.canvasImage.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage() {
|
renderPage() {
|
||||||
if (this.ctx && this.canvas) {
|
if (this.ctx && this.canvas) {
|
||||||
this.canvasImage.onload = null;
|
this.canvasImage.onload = null;
|
||||||
|
|
||||||
if (!this.setCanvasSize()) return;
|
this.setCanvasSize();
|
||||||
|
|
||||||
const needsSplitting = this.canvasImage.width > this.canvasImage.height;
|
const needsSplitting = this.isCoverImage();
|
||||||
this.updateSplitPage();
|
this.updateSplitPage();
|
||||||
|
|
||||||
if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.LEFT_PART) {
|
if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.LEFT_PART) {
|
||||||
@ -844,8 +859,49 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, -this.canvasImage.width / 2, 0, this.canvasImage.width, this.canvasImage.height);
|
this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, -this.canvasImage.width / 2, 0, this.canvasImage.width, this.canvasImage.height);
|
||||||
} else {
|
} else {
|
||||||
if (!this.firstPageRendered && this.scalingOption === ScalingOption.Automatic) {
|
if (!this.firstPageRendered && this.scalingOption === ScalingOption.Automatic) {
|
||||||
|
this.updateScalingForFirstPageRender();
|
||||||
|
}
|
||||||
|
|
||||||
let newScale = this.generalSettingsForm.get('fittingOption')?.value;
|
// Fit Split on a page that needs splitting
|
||||||
|
if (this.shouldRenderAsFitSplit()) {
|
||||||
|
const windowWidth = window.innerWidth
|
||||||
|
|| document.documentElement.clientWidth
|
||||||
|
|| document.body.clientWidth;
|
||||||
|
const windowHeight = window.innerHeight
|
||||||
|
|| document.documentElement.clientHeight
|
||||||
|
|| document.body.clientHeight;
|
||||||
|
// If the user's screen is wider than the image, just pretend this is no split, as it will render nicer
|
||||||
|
this.canvas.nativeElement.width = windowWidth;
|
||||||
|
this.canvas.nativeElement.height = windowHeight;
|
||||||
|
const ratio = this.canvasImage.width / this.canvasImage.height;
|
||||||
|
let newWidth = windowWidth;
|
||||||
|
let newHeight = newWidth / ratio;
|
||||||
|
if (newHeight > windowHeight) {
|
||||||
|
newHeight = windowHeight;
|
||||||
|
newWidth = newHeight * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization: When the screen is larger than newWidth, allow no split rendering to occur for a better fit
|
||||||
|
if (windowWidth > newWidth) {
|
||||||
|
this.ctx.drawImage(this.canvasImage, 0, 0);
|
||||||
|
} else {
|
||||||
|
this.ctx.drawImage(this.canvasImage, 0, 0, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.ctx.drawImage(this.canvasImage, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset scroll on non HEIGHT Fits
|
||||||
|
if (this.getFit() !== FITTING_OPTION.HEIGHT) {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScalingForFirstPageRender() {
|
||||||
const windowWidth = window.innerWidth
|
const windowWidth = window.innerWidth
|
||||||
|| document.documentElement.clientWidth
|
|| document.documentElement.clientWidth
|
||||||
|| document.body.clientWidth;
|
|| document.body.clientWidth;
|
||||||
@ -853,8 +909,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|| document.documentElement.clientHeight
|
|| document.documentElement.clientHeight
|
||||||
|| document.body.clientHeight;
|
|| document.body.clientHeight;
|
||||||
|
|
||||||
const widthRatio = windowWidth / this.canvasImage.width;
|
const needsSplitting = this.isCoverImage();
|
||||||
const heightRatio = windowHeight / this.canvasImage.height;
|
let newScale = this.generalSettingsForm.get('fittingOption')?.value;
|
||||||
|
const widthRatio = windowWidth / (this.canvasImage.width / (needsSplitting ? 2 : 1));
|
||||||
|
const heightRatio = windowHeight / (this.canvasImage.height);
|
||||||
|
|
||||||
// Given that we now have image dimensions, assuming this isn't a split image,
|
// Given that we now have image dimensions, assuming this isn't a split image,
|
||||||
// Try to reset one time based on who's dimension (width/height) is smaller
|
// Try to reset one time based on who's dimension (width/height) is smaller
|
||||||
@ -864,18 +922,18 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
newScale = FITTING_OPTION.HEIGHT;
|
newScale = FITTING_OPTION.HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.generalSettingsForm.get('fittingOption')?.setValue(newScale);
|
|
||||||
this.firstPageRendered = true;
|
this.firstPageRendered = true;
|
||||||
}
|
this.generalSettingsForm.get('fittingOption')?.setValue(newScale, {emitEvent: false});
|
||||||
this.ctx.drawImage(this.canvasImage, 0, 0);
|
|
||||||
}
|
|
||||||
// Reset scroll on non HEIGHT Fits
|
|
||||||
if (this.getFit() !== FITTING_OPTION.HEIGHT) {
|
|
||||||
window.scrollTo(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCoverImage() {
|
||||||
|
return this.canvasImage.width > this.canvasImage.height;
|
||||||
}
|
}
|
||||||
this.isLoading = false;
|
|
||||||
|
|
||||||
|
shouldRenderAsFitSplit() {
|
||||||
|
if (!this.isCoverImage() || parseInt(this.generalSettingsForm?.get('pageSplitOption')?.value, 10) !== PageSplitOption.FitSplit) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ export class NavEventsToggleComponent implements OnInit, OnDestroy {
|
|||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.onDestroy.next();
|
this.onDestroy.next();
|
||||||
this.onDestroy.complete();
|
this.onDestroy.complete();
|
||||||
|
this.progressEventsSource.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import '~bootstrap/scss/mixins/_breakpoints.scss';
|
@import '~bootstrap/scss/mixins/_breakpoints.scss'; // TODO: Use @forwards for this?
|
||||||
|
|
||||||
$primary-color: white;
|
$primary-color: white;
|
||||||
$bg-color: rgb(22, 27, 34);
|
$bg-color: rgb(22, 27, 34);
|
||||||
|
@ -136,7 +136,12 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
return 'Volume ' + this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
return 'Volume ' + this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.utilityService.formatChapterName(this.libraryTypes[item.libraryId], true, true) + item.chapterNumber;
|
let chapterNum = item.chapterNumber;
|
||||||
|
if (!item.chapterNumber.match(/^\d+$/)) {
|
||||||
|
chapterNum = this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.utilityService.formatChapterName(this.libraryTypes[item.libraryId], true, true) + chapterNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
orderUpdated(event: IndexUpdateEvent) {
|
orderUpdated(event: IndexUpdateEvent) {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import '../../../theme/colors';
|
|
||||||
|
|
||||||
$bg-color: #c9c9c9;
|
$bg-color: #c9c9c9;
|
||||||
$bdr-color: #f2f2f2;
|
$bdr-color: #f2f2f2;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import "../../theme/_colors.scss";
|
@use "../../theme/colors";
|
||||||
|
|
||||||
.login {
|
.login {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -36,7 +36,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: $primary-color;
|
background-color: colors.$primary-color;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// All dark style overrides should live here
|
// All dark style overrides should live here
|
||||||
|
|
||||||
@import "../../theme/colors";
|
@use "../../theme/colors";
|
||||||
|
|
||||||
.bg-dark {
|
.bg-dark {
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
@ -32,6 +32,10 @@
|
|||||||
box-shadow: inset 0px 0px 8px 1px $dark-form-background !important;
|
box-shadow: inset 0px 0px 8px 1px $dark-form-background !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: #d7d7d7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
background-color: $dark-item-accent-bg;
|
background-color: $dark-item-accent-bg;
|
||||||
|
|
||||||
@ -181,6 +185,10 @@
|
|||||||
background-color: $dark-form-background-no-opacity;
|
background-color: $dark-form-background-no-opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bs-popover-bottom > .arrow::after, .bs-popover-bottom > .arrow::before {
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Import colors for overrides of bootstrap theme
|
// Import colors for overrides of bootstrap theme
|
||||||
@import './theme/_colors.scss';
|
@import './theme/colors';
|
||||||
@import './theme/_toastr.scss';
|
@import './theme/toastr';
|
||||||
|
|
||||||
// Bootstrap must be after _colors since we define the colors there
|
// Bootstrap must be after _colors since we define the colors there
|
||||||
@import '~bootstrap/scss/bootstrap';
|
@import '~bootstrap/scss/bootstrap';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user