mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
The last push before release (#2696)
Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
2227dc354b
commit
033dc61d54
@ -1687,6 +1687,35 @@ public class ReaderServiceTests
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task GetContinuePoint_ShouldReturnFirstChapter_WhenHasSpecial()
|
||||
{
|
||||
await ResetDb();
|
||||
var series = new SeriesBuilder("Test")
|
||||
// Loose chapters
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithChapter(new ChapterBuilder("1").WithPages(1).Build())
|
||||
.WithChapter(new ChapterBuilder("2").WithPages(1).Build())
|
||||
.WithChapter(new ChapterBuilder("Prologue").WithIsSpecial(true).WithPages(1).Build())
|
||||
.Build())
|
||||
.Build();
|
||||
series.Library = new LibraryBuilder("Test LIb", LibraryType.Manga).Build();
|
||||
|
||||
_context.Series.Add(series);
|
||||
|
||||
_context.AppUser.Add(new AppUser()
|
||||
{
|
||||
UserName = "majora2007"
|
||||
});
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var nextChapter = await _readerService.GetContinuePoint(1, 1);
|
||||
|
||||
Assert.Equal("1", nextChapter.Range);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetContinuePoint_ShouldReturnFirstSpecial()
|
||||
{
|
||||
|
@ -31,7 +31,9 @@ public class LicenseController(
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.LicenseCache)]
|
||||
public async Task<ActionResult<bool>> HasValidLicense(bool forceCheck = false)
|
||||
{
|
||||
return Ok(await licenseService.HasActiveLicense(forceCheck));
|
||||
var result = await licenseService.HasActiveLicense(forceCheck);
|
||||
await taskScheduler.ScheduleKavitaPlusTasks();
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -66,7 +68,11 @@ public class LicenseController(
|
||||
public async Task<ActionResult> ResetLicense(UpdateLicenseDto dto)
|
||||
{
|
||||
logger.LogInformation("Resetting license on file for Server");
|
||||
if (await licenseService.ResetLicense(dto.License, dto.Email)) return Ok();
|
||||
if (await licenseService.ResetLicense(dto.License, dto.Email))
|
||||
{
|
||||
await taskScheduler.ScheduleKavitaPlusTasks();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return BadRequest(localizationService.Translate(User.GetUserId(), "unable-to-reset-k+"));
|
||||
}
|
||||
|
@ -1,36 +1,33 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Email Change Update</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Your account's email has been updated on {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to validate your email change. The email is not changed until you complete validation.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> CONFIRM EMAIL </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<style>
|
||||
.small {
|
||||
font-size: 10px;
|
||||
color: lightgrey;
|
||||
}
|
||||
</style>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>Email Change Update</h1>
|
||||
<p>Your account's email has been updated on {{InvitingUser}}'s Kavita instance.</p>
|
||||
<p>Please click the following link to validate your email change. The email is not changed until you complete validation.</p>
|
||||
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a href="{{Link}}" target="_blank">CONFIRM EMAIL</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="small">If the button above does not work, please find the link here: {{Link}}</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,36 +1,32 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You've Been Invited</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">You have been invited to {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to setup an account for yourself and start reading.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> ACCEPT INVITE </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<style>
|
||||
.small {
|
||||
font-size: 10px;
|
||||
color: lightgrey;
|
||||
}
|
||||
</style>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>You've Been Invited!</h1>
|
||||
<p>You have been invited to {{InvitingUser}}'s Kavita instance.</p>
|
||||
<p>Please click the following link to setup an account for yourself and start reading.</p>
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a href="{{Link}}" target="_blank">ACCEPT INVITE</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="small">If the button above does not work, please find the link here: {{Link}}</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,36 +1,33 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Forgot your password?</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">That's okay, it happens! Click on the button below to reset your password.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> RESET YOUR PASSWORD </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 0px 20px 0px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you did not perform this action, ignore this email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<style>
|
||||
.small {
|
||||
font-size: 10px;
|
||||
color: lightgrey;
|
||||
}
|
||||
</style>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>Forgot your password?</h1>
|
||||
<p>That's okay, it happens! Click on the button below to reset your password.</p>
|
||||
|
||||
<p>If you did not perform this action, ignore this email. Your account is safe.</p>
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a href="{{Link}}" target="_blank">RESET YOUR PASSWORD</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="small">If the button above does not work, please find the link here: {{Link}}</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,11 +1,8 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">This is a Test Email</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you've received this email, your KavitaEmail is setup</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>This is a Test Email</h1>
|
||||
<p>Congrats! Your instance of Kavita is setup to email correctly!</p>
|
||||
|
||||
<p>If you did not perform this action, ignore this email. Your account is safe.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,11 +1,6 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You sent a file from Kavita</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please find attached the file(s) you've sent.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>You sent file(s) from Kavita</h1>
|
||||
<p>Please find attached the file(s) you've sent.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,320 +1,523 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Kavita - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="supported-color-schemes" content="light dark">
|
||||
<!-- Supports dark mode -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<title>Kavita - {{Preheader}}</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap');
|
||||
/* What it does: Remove spaces around the email design added by some email clients. */
|
||||
/* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0 auto !important;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
/*All the styling goes here*/
|
||||
|
||||
:root {
|
||||
Color-scheme: light dark;
|
||||
supported-color-schemes: light dark;
|
||||
}
|
||||
/* What it does: Stops email clients resizing small text. */
|
||||
|
||||
* {
|
||||
.dark-img { display: none !important; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark-img {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.light-img {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
[data-ogsc] .dark-img {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
[data-ogsc] .light-img {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #eaebed;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
/* What it does: Centers email on Android 4.4 */
|
||||
|
||||
div[style*="margin: 16px 0"] {
|
||||
margin: 0 !important;
|
||||
}
|
||||
/* What it does: Stops Outlook from adding extra spacing to tables. */
|
||||
|
||||
table,
|
||||
td {
|
||||
mso-table-lspace: 0pt !important;
|
||||
mso-table-rspace: 0pt !important;
|
||||
}
|
||||
/* What it does: Fixes webkit padding issue. Fix for Yahoo mail table alignment bug. Applies table-layout to the first 2 tables then removes for anything nested deeper. */
|
||||
|
||||
table {
|
||||
border-spacing: 0 !important;
|
||||
border-collapse: collapse !important;
|
||||
table-layout: fixed !important;
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
min-width: 100%;
|
||||
width: 100%; }
|
||||
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
|
||||
.body {
|
||||
background-color: #eaebed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 680px;
|
||||
width: 680px;
|
||||
}
|
||||
|
||||
table table table {
|
||||
table-layout: auto;
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 680px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
i {
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 10px 40px;
|
||||
background-color: #4AC694;
|
||||
background-image: linear-gradient(#4AC694, #4AC694);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
u + .body .gmail-blend-screen {
|
||||
background:#000;
|
||||
mix-blend-mode:screen;
|
||||
}
|
||||
u + .body .gmail-blend-difference {
|
||||
background:#000;
|
||||
mix-blend-mode:difference;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark ) {
|
||||
.header {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
color: #fffffe !important;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
color: #fffffe !important;
|
||||
}
|
||||
}
|
||||
|
||||
.header-text, .logo-text {
|
||||
font-weight: 700;
|
||||
font-family: 'Spartan', sans-serif;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
font-size: 2rem;
|
||||
vertical-align: middle;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
color: #fff;
|
||||
font-size: 26px;
|
||||
}
|
||||
/* What it does: Uses a better rendering method when resizing images in IE. */
|
||||
|
||||
img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
.header-text img {
|
||||
vertical-align: middle;
|
||||
width: 200px;
|
||||
}
|
||||
/* What it does: A work-around for email clients meddling in triggered links. */
|
||||
|
||||
*[x-apple-data-detectors],
|
||||
/* iOS */
|
||||
|
||||
.x-gmail-data-detectors,
|
||||
/* Gmail */
|
||||
|
||||
.x-gmail-data-detectors *,
|
||||
.aBn {
|
||||
border-bottom: 0 !important;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
.header-text div, .header-text p {
|
||||
vertical-align: middle;
|
||||
min-height: 75px;
|
||||
line-height: 75px;
|
||||
display: inline-block;
|
||||
}
|
||||
/* What it does: Prevents Gmail from displaying an download button on large, non-linked images. */
|
||||
|
||||
.a6S {
|
||||
display: none !important;
|
||||
opacity: 0.01 !important;
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
}
|
||||
/* If the above doesn't work, add a .g-img class to any image in question. */
|
||||
|
||||
img.g-img + div {
|
||||
display: none !important;
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
/* What it does: Prevents underlining the button text in Windows 10 */
|
||||
|
||||
.button-link {
|
||||
text-decoration: none !important;
|
||||
.content-block > * {
|
||||
padding-right: 10px;
|
||||
}
|
||||
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
|
||||
/* Create one of these media queries for each additional viewport size you'd like to fix */
|
||||
/* Thanks to Eric Lepetit @ericlepetitsf) for help troubleshooting */
|
||||
|
||||
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
|
||||
/* iPhone 6 and 6+ */
|
||||
.email-container {
|
||||
min-width: 375px !important;
|
||||
.footer {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background-color: #222;
|
||||
background-image: linear-gradient(#222, #222);
|
||||
color: #fffffe;
|
||||
}
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #fffffe;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer .icon {
|
||||
margin:0;
|
||||
padding:0 10px;
|
||||
border:none;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark ) {
|
||||
.footer {
|
||||
background-color: rgba(0,0,0,0) !important;
|
||||
background-image: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0)) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #06090f;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4AC694;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
min-width: auto;
|
||||
width: auto;
|
||||
}
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #4AC694;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #4AC694;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.btn-primary table td {
|
||||
background-color: #4AC694;
|
||||
background-image: linear-gradient(#4AC694, #4AC694);
|
||||
}
|
||||
|
||||
.btn-primary a {
|
||||
background-color: #4AC694;
|
||||
background-image: linear-gradient(#4AC694, #4AC694);
|
||||
border-color: #4AC694;
|
||||
color: #fffffe;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.mt0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mb0 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.powered-by a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important;
|
||||
}
|
||||
table[class=body] .content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
width: auto !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
.btn-primary table td:hover {
|
||||
background-color: #d5075d !important;
|
||||
}
|
||||
.btn-primary a:hover {
|
||||
background-color: #d5075d !important;
|
||||
border-color: #d5075d !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body class="">
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> Your account's email has been updated on {{InvitingUser}}'s Kavita instance. Click the button to validate your email.</div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<!-- Visually Hidden Preheader Text-->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;">{{Preheader}}</div>
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td class="container">
|
||||
<div class="header">
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
{{Body}}
|
||||
<td class="align-center" width="100%">
|
||||
<a class="header-text" href="https://www.kavitareader.com" target="_blank">
|
||||
<img class="light-img" src="https://www.kavitareader.com/img/email/email-logo.png" alt="Kavita" width="40" style="vertical-align:middle;" />
|
||||
<!--[if !mso]><! -->
|
||||
<img class="dark-img" src="https://www.kavitareader.com/img/email/email-logo.png" alt="Kavita" width="40" style="vertical-align:middle;" />
|
||||
<!--<![endif]-->
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="content">
|
||||
<table role="presentation" class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
{{Body}}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="gmail-blend-screen">
|
||||
<div class="gmail-blend-difference">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="content-block" style="text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<a href="https://discord.gg/b52wT37kt7" target="_blank">
|
||||
Discord
|
||||
</a>
|
||||
</td>
|
||||
<td class="icon">
|
||||
<a href="https://github.com/Kareadita/Kavita/">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
<td class="icon">
|
||||
<a href="https://wiki.kavitareader.com/en">
|
||||
Wiki
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -20,10 +20,11 @@ namespace API.Services;
|
||||
|
||||
internal class EmailOptionsDto
|
||||
{
|
||||
public IList<string> ToEmails { get; set; }
|
||||
public string Subject { get; set; }
|
||||
public string Body { get; set; }
|
||||
public IList<KeyValuePair<string, string>> PlaceHolders { get; set; }
|
||||
public required IList<string> ToEmails { get; set; }
|
||||
public required string Subject { get; set; }
|
||||
public required string Body { get; set; }
|
||||
public required string Preheader { get; set; }
|
||||
public IList<KeyValuePair<string, string>>? PlaceHolders { get; set; }
|
||||
/// <summary>
|
||||
/// Filenames to attach
|
||||
/// </summary>
|
||||
@ -81,7 +82,6 @@ public class EmailService : IEmailService
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Come back and update the template. We can't do it with the v0.8.0 release
|
||||
var placeholders = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("{{Host}}", settings.HostName),
|
||||
@ -91,8 +91,9 @@ public class EmailService : IEmailService
|
||||
{
|
||||
var emailOptions = new EmailOptionsDto()
|
||||
{
|
||||
Subject = "KavitaEmail Test",
|
||||
Subject = "Kavita - Email Test",
|
||||
Body = UpdatePlaceHolders(await GetEmailBody("EmailTest"), placeholders),
|
||||
Preheader = "Kavita - Email Test",
|
||||
ToEmails = new List<string>()
|
||||
{
|
||||
adminEmail
|
||||
@ -127,6 +128,7 @@ public class EmailService : IEmailService
|
||||
{
|
||||
Subject = UpdatePlaceHolders("Your email has been changed on {{InvitingUser}}'s Server", placeholders),
|
||||
Body = UpdatePlaceHolders(await GetEmailBody("EmailChange"), placeholders),
|
||||
Preheader = UpdatePlaceHolders("Your email has been changed on {{InvitingUser}}'s Server", placeholders),
|
||||
ToEmails = new List<string>()
|
||||
{
|
||||
data.EmailAddress
|
||||
@ -182,6 +184,7 @@ public class EmailService : IEmailService
|
||||
{
|
||||
Subject = UpdatePlaceHolders("You've been invited to join {{InvitingUser}}'s Server", placeholders),
|
||||
Body = UpdatePlaceHolders(await GetEmailBody("EmailConfirm"), placeholders),
|
||||
Preheader = UpdatePlaceHolders("You've been invited to join {{InvitingUser}}'s Server", placeholders),
|
||||
ToEmails = new List<string>()
|
||||
{
|
||||
data.EmailAddress
|
||||
@ -207,6 +210,7 @@ public class EmailService : IEmailService
|
||||
{
|
||||
Subject = UpdatePlaceHolders("A password reset has been requested", placeholders),
|
||||
Body = UpdatePlaceHolders(await GetEmailBody("EmailPasswordReset"), placeholders),
|
||||
Preheader = "A password reset has been requested",
|
||||
ToEmails = new List<string>()
|
||||
{
|
||||
dto.EmailAddress
|
||||
@ -225,6 +229,7 @@ public class EmailService : IEmailService
|
||||
var emailOptions = new EmailOptionsDto()
|
||||
{
|
||||
Subject = "Send file from Kavita",
|
||||
Preheader = "File(s) sent from Kavita",
|
||||
ToEmails = new List<string>()
|
||||
{
|
||||
data.DestinationEmail
|
||||
@ -249,7 +254,8 @@ public class EmailService : IEmailService
|
||||
// Inject the body into the base template
|
||||
var fullBody = UpdatePlaceHolders(await GetEmailBody("base"), new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new ("{{Body}}", userEmailOptions.Body)
|
||||
new ("{{Body}}", userEmailOptions.Body),
|
||||
new ("{{Preheader}}", userEmailOptions.Preheader),
|
||||
});
|
||||
|
||||
var body = new BodyBuilder
|
||||
@ -320,7 +326,7 @@ public class EmailService : IEmailService
|
||||
return body;
|
||||
}
|
||||
|
||||
private static string UpdatePlaceHolders(string text, IList<KeyValuePair<string, string>> keyValuePairs)
|
||||
private static string UpdatePlaceHolders(string text, IList<KeyValuePair<string, string>>? keyValuePairs)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || keyValuePairs == null) return text;
|
||||
|
||||
|
@ -531,8 +531,24 @@ public class ReaderService : IReaderService
|
||||
if (!await _unitOfWork.AppUserProgressRepository.AnyUserProgressForSeriesAsync(seriesId, userId))
|
||||
{
|
||||
// I think i need a way to sort volumes last
|
||||
return volumes.OrderBy(v => v.MinNumber.ToString(CultureInfo.InvariantCulture).AsDouble(), _chapterSortComparer).First().Chapters
|
||||
.OrderBy(c => c.Number.AsFloat()).First();
|
||||
var chapters = volumes.OrderBy(v => v.MinNumber, _chapterSortComparer).First().Chapters
|
||||
.OrderBy(c => c.Number.AsFloat())
|
||||
.ToList();
|
||||
|
||||
// If there are specials, then return the first Non-special
|
||||
if (chapters.Exists(c => c.IsSpecial))
|
||||
{
|
||||
var firstChapter = chapters.FirstOrDefault(c => !c.IsSpecial);
|
||||
if (firstChapter == null)
|
||||
{
|
||||
// If there is no non-special chapter, then return first chapter
|
||||
return chapters[0];
|
||||
}
|
||||
|
||||
return firstChapter;
|
||||
}
|
||||
// Else use normal logic
|
||||
return chapters[0];
|
||||
}
|
||||
|
||||
// Loop through all chapters that are not in volume 0
|
||||
|
@ -130,7 +130,7 @@ public class VersionUpdaterService : IVersionUpdaterService
|
||||
{
|
||||
if (update == null) return;
|
||||
|
||||
var updateVersion = new Version(update.CurrentVersion);
|
||||
var updateVersion = new Version(update.UpdateVersion);
|
||||
|
||||
if (BuildInfo.Version < updateVersion)
|
||||
{
|
||||
@ -138,12 +138,6 @@ public class VersionUpdaterService : IVersionUpdaterService
|
||||
await _eventHub.SendMessageAsync(MessageFactory.UpdateAvailable, MessageFactory.UpdateVersionEvent(update),
|
||||
true);
|
||||
}
|
||||
else if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == Environments.Development)
|
||||
{
|
||||
_logger.LogWarning("Server is up to date. Current: {CurrentVersion}", BuildInfo.Version);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.UpdateAvailable, MessageFactory.UpdateVersionEvent(update),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
24
UI/Web/package-lock.json
generated
24
UI/Web/package-lock.json
generated
@ -615,6 +615,7 @@
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.1.0.tgz",
|
||||
"integrity": "sha512-WDpO4WvC5ItjaRexnpFpKPpT+cu+5GYkWF8h74iHhfxOgU+gaQiMWERHylWCqF25AzmhKu0iI3ZZtaIJ6qqwog==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "7.23.2",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14",
|
||||
@ -5542,6 +5543,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@ -5787,6 +5789,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -6092,6 +6095,7 @@
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@ -6340,7 +6344,8 @@
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
@ -7230,6 +7235,7 @@
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "^0.6.2"
|
||||
@ -7239,6 +7245,7 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
@ -8318,6 +8325,7 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -9092,6 +9100,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
@ -10868,6 +10877,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -12258,6 +12268,7 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
@ -12268,7 +12279,8 @@
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
@ -12720,7 +12732,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.69.7",
|
||||
@ -12836,6 +12848,7 @@
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@ -12850,6 +12863,7 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@ -12860,7 +12874,8 @@
|
||||
"node_modules/semver/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
@ -13967,6 +13982,7 @@
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {DOCUMENT, NgStyle, NgIf, NgFor, NgSwitch, NgSwitchCase, PercentPipe, NgClass, AsyncPipe} from '@angular/common';
|
||||
import {AsyncPipe, DOCUMENT, NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchCase, PercentPipe} from '@angular/common';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@ -29,9 +29,9 @@ import {
|
||||
take,
|
||||
tap
|
||||
} from 'rxjs';
|
||||
import { ChangeContext, LabelType, Options, NgxSliderModule } from 'ngx-slider-v2';
|
||||
import {ChangeContext, LabelType, NgxSliderModule, Options} from 'ngx-slider-v2';
|
||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
import {NgbModal, NgbProgressbar} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {ShortcutsModalComponent} from 'src/app/reader-shared/_modals/shortcuts-modal/shortcuts-modal.component';
|
||||
@ -61,12 +61,12 @@ import {ChapterInfo} from '../../_models/chapter-info';
|
||||
import {DoubleNoCoverRendererComponent} from '../double-renderer-no-cover/double-no-cover-renderer.component';
|
||||
import {SwipeEvent} from 'src/app/ng-swipe/ag-swipe.core';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { FullscreenIconPipe } from '../../../_pipes/fullscreen-icon.pipe';
|
||||
import { ReaderModeIconPipe } from '../../../_pipes/reader-mode-icon.pipe';
|
||||
import { FittingIconPipe } from '../../../_pipes/fitting-icon.pipe';
|
||||
import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scroller.component';
|
||||
import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive';
|
||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||
import {FullscreenIconPipe} from '../../../_pipes/fullscreen-icon.pipe';
|
||||
import {ReaderModeIconPipe} from '../../../_pipes/reader-mode-icon.pipe';
|
||||
import {FittingIconPipe} from '../../../_pipes/fitting-icon.pipe';
|
||||
import {InfiniteScrollerComponent} from '../infinite-scroller/infinite-scroller.component';
|
||||
import {SwipeDirective} from '../../../ng-swipe/ng-swipe.directive';
|
||||
import {LoadingComponent} from '../../../shared/loading/loading.component';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {shareReplay} from "rxjs/operators";
|
||||
|
||||
@ -676,11 +676,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
break;
|
||||
case ReaderMode.Webtoon:
|
||||
if (event.key === KEY_CODES.DOWN_ARROW) {
|
||||
this.nextPage()
|
||||
} else if (event.key === KEY_CODES.UP_ARROW) {
|
||||
this.prevPage()
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1220,14 +1215,14 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.pagingDirectionSubject.next(PAGING_DIRECTION.BACKWARDS);
|
||||
|
||||
|
||||
const pageAmount = Math.max(this.canvasRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS),
|
||||
const pageAmount = this.readerMode === ReaderMode.Webtoon ? 1 : Math.max(this.canvasRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS),
|
||||
this.singleRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS),
|
||||
this.doubleRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS),
|
||||
this.doubleNoCoverRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS),
|
||||
this.doubleReverseRenderer.getPageAmount(PAGING_DIRECTION.BACKWARDS)
|
||||
);
|
||||
|
||||
const notInSplit = this.canvasRenderer.shouldMovePrev();
|
||||
const notInSplit = this.readerMode === ReaderMode.Webtoon ? true : this.canvasRenderer.shouldMovePrev();
|
||||
|
||||
if ((this.pageNum - 1 < 0 && notInSplit)) {
|
||||
// Move to next volume/chapter automatically
|
||||
|
@ -32,7 +32,8 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="library-type" class="form-label">{{t('type-label')}}</label><i class="fa fa-info-circle ms-1" placement="right" [ngbTooltip]="typeTooltip" role="button" tabindex="0"></i>
|
||||
<label for="library-type" class="form-label">{{t('type-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" placement="right" [ngbTooltip]="typeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #typeTooltip>{{t('type-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="library-type-help">
|
||||
<ng-container [ngTemplateOutlet]="typeTooltip"></ng-container>
|
||||
@ -233,7 +234,7 @@
|
||||
</div>
|
||||
</form>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" (click)="forceScan()" position="above"
|
||||
<button type="button" class="btn btn-light" (click)="forceScan()" [placement]="'above'"
|
||||
[ngbTooltip]="t('force-scan-tooltip')">{{t('force-scan')}}</button>
|
||||
<button type="button" class="btn btn-light" (click)="reset()">{{t('reset')}}</button>
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">{{t('cancel')}}</button>
|
||||
|
@ -7,7 +7,7 @@
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.7.13.20"
|
||||
"version": "0.7.13.21"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user