mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
[Manga Reader] Swipe Support (#1735)
* Fixed a loading indicator that is always on * Started to add swipe directive * Implemented the ability to swipe to navigate pages in manga reader. * Swipe to paginate seems to be working reliably * Removed a bunch of junk from csproj and added a debug menu for testing on phone to smooth out experience. * Fixed a bug where reading list detail wouldn't render the set image of the reading list. * Added some instructions and code to allow connecting to dev instance easier. * Fixed up paging with keyboard where to ensure that when we hit the end of the scroll, we don't go to the next page instantly, but rather make the user press the key once more. * Fixed reading list image not properly renderering on reading list detail page. * Solved the swiping bug, need to play with threshold again. * Swipe is now working. Need to decide if I'm going to support reversing the direction with reading direction. * Hooked up swipe with reading direction code * Cleaned up some direction code to align to a new enum * Feature complete
This commit is contained in:
parent
e90ea4758a
commit
45b854e1d0
180
API/API.csproj
180
API/API.csproj
@ -7,6 +7,7 @@
|
|||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<TieredPGO>true</TieredPGO>
|
<TieredPGO>true</TieredPGO>
|
||||||
<TieredCompilation>true</TieredCompilation>
|
<TieredCompilation>true</TieredCompilation>
|
||||||
|
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="Build" Condition=" '$(Configuration)' == 'Debug' ">
|
<Target Name="PostBuild" AfterTargets="Build" Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
@ -20,7 +21,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).xml</DocumentationFile>
|
||||||
<NoWarn>1701;1702;1591</NoWarn>
|
<NoWarn>1701;1702;1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@ -116,6 +117,7 @@
|
|||||||
<None Remove="kavita.log" />
|
<None Remove="kavita.log" />
|
||||||
<None Remove="kavita.db" />
|
<None Remove="kavita.db" />
|
||||||
<None Remove="covers\**" />
|
<None Remove="covers\**" />
|
||||||
|
<None Remove="wwwroot\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -125,6 +127,7 @@
|
|||||||
<Compile Remove="logs\**" />
|
<Compile Remove="logs\**" />
|
||||||
<Compile Remove="temp\**" />
|
<Compile Remove="temp\**" />
|
||||||
<Compile Remove="covers\**" />
|
<Compile Remove="covers\**" />
|
||||||
|
<Compile Remove="wwwroot\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -139,6 +142,7 @@
|
|||||||
<EmbeddedResource Remove="config\logs\**" />
|
<EmbeddedResource Remove="config\logs\**" />
|
||||||
<EmbeddedResource Remove="config\temp\**" />
|
<EmbeddedResource Remove="config\temp\**" />
|
||||||
<EmbeddedResource Remove="config\stats\**" />
|
<EmbeddedResource Remove="config\stats\**" />
|
||||||
|
<EmbeddedResource Remove="wwwroot\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -164,178 +168,12 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<_ContentIncludedByDefault Remove="logs\kavita.json" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\3rdpartylicenses.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\6.d9925ea83359bb4c7278.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\6.d9925ea83359bb4c7278.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\7.860cdd6fd9d758e6c210.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\7.860cdd6fd9d758e6c210.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\8.028f6737a2f0621d40c7.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\8.028f6737a2f0621d40c7.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\EBGaramond-Italic-VariableFont_wght.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\EBGaramond-VariableFont_wght.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Black.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-BlackItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-BoldItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraBold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraBoldItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraLight.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraLightItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Italic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Light.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-LightItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Medium.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-MediumItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-SemiBold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-SemiBoldItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Thin.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ThinItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Black.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-BlackItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-BoldItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Italic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Light.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-LightItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Thin.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-ThinItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Italic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Italic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Black.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-BlackItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-BoldItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Italic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Light.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-LightItalic.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-ExtraBold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\Oswald-VariableFont_wght.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\README.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Bold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-ExtraLight.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Light.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Medium.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-SemiBold.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\RocknRoll_One\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\RocknRoll_One\RocknRollOne-Regular.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.dark-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.dark.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.dark-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.dark.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\preset-light.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\themes\dark.scss" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\common.ad975892146299f80adb.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\common.ad975892146299f80adb.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\EBGaramond-VariableFont_wght.2a1da2dbe7a28d63f8cb.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.0fea24969112a781acd2.eot" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.c967a94cfbe2b06627ff.woff2" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.dc2cbadd690e1d4b2c9c.woff" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.e33e2cf6e02cac2ccb77.svg" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.ec82f282c7f54b637098.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.06b9d19ced8d17f3d5cb.svg" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.08f9891a6f44d9546678.eot" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1008b5226941c24f4468.woff2" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1069ea55beaa01060302.woff" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1495f578452eb676f730.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.10ecefc282f2761808bf.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.371dbce0dd46bd4d2033.svg" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.3a24a60e7f9c6574864a.eot" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.3ceb50e7bcafb577367c.woff2" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.46fdbd2d897f8824e63c.woff" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\favicon.ico" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\FiraSans-Regular.1c0bf0728b51cb9f2ddc.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\index.html" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\Lato-Regular.9919edff6283018571ad.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\LibreBaskerville-Regular.a27f99ca45522bb3d56d.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\main.44f5c0973044295d8be0.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\main.44f5c0973044295d8be0.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\Merriweather-Regular.55c73e48e04ec926ebfe.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\NanumGothic-Regular.6c84540de7730f833d6c.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\polyfills.348e08e9d0e910a15938.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\polyfills.348e08e9d0e910a15938.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\RocknRollOne-Regular.c75da4712d1e65ed1f69.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\runtime.ea545c6916f85411478f.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\runtime.ea545c6916f85411478f.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\styles.4bd902bb3037f36f2c64.css" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\styles.4bd902bb3037f36f2c64.css.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\10.b727db78581442412e9a.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\10.b727db78581442412e9a.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\2.fcc031071e80d6837012.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\2.fcc031071e80d6837012.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\7.c30da7d2e809fa05d1e3.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\7.c30da7d2e809fa05d1e3.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\8.d4c77a90c95e9861656a.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\8.d4c77a90c95e9861656a.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\9.489b177dd1a6beeb35ad.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\9.489b177dd1a6beeb35ad.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Spartan\OFL.txt" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Spartan\Spartan-VariableFont_wght.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\android-chrome-192x192.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\android-chrome-256x256.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\apple-touch-icon.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\browserconfig.xml" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\favicon-16x16.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\favicon-32x32.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\favicon.ico" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\icons\mstile-150x150.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-reset-cover-min.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\image-reset-cover.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\kavita-book-cropped.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\login-bg.jpg" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\assets\images\logo.png" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\common.fbf71de364f5a1f37413.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\common.fbf71de364f5a1f37413.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\login-bg.8860e6ff9d2a3598539c.jpg" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\main.a3a1e647a39145accff3.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\main.a3a1e647a39145accff3.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\polyfills.3dda3bf3d087e5d131ba.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\polyfills.3dda3bf3d087e5d131ba.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\runtime.b9818dfc90f418b3f0a7.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\runtime.b9818dfc90f418b3f0a7.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\scripts.7d1c78b2763c483bb699.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\scripts.7d1c78b2763c483bb699.js.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\site.webmanifest" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\Spartan-VariableFont_wght.0427aac0d980a12ae8ba.ttf" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\styles.85a58cb3e4a4b1add864.css" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\styles.85a58cb3e4a4b1add864.css.map" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\vendor.54bf44a9aa720ff8881d.js" />
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\vendor.54bf44a9aa720ff8881d.js.map" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System.Drawing.Common" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="config\themes" />
|
<Folder Include="config\themes" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_DeploymentManifestIconFile Remove="favicon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -54,7 +54,7 @@ public class CollectionController : BaseApiController
|
|||||||
[HttpGet("search")]
|
[HttpGet("search")]
|
||||||
public async Task<IEnumerable<CollectionTagDto>> SearchTags(string queryString)
|
public async Task<IEnumerable<CollectionTagDto>> SearchTags(string queryString)
|
||||||
{
|
{
|
||||||
queryString ??= "";
|
queryString ??= string.Empty;
|
||||||
queryString = queryString.Replace(@"%", string.Empty);
|
queryString = queryString.Replace(@"%", string.Empty);
|
||||||
if (queryString.Length == 0) return await GetAllTags();
|
if (queryString.Length == 0) return await GetAllTags();
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.ChapterRepository.GetChapterCoverImageAsync(chapterId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.ChapterRepository.GetChapterCoverImageAsync(chapterId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.LibraryRepository.GetLibraryCoverImageAsync(libraryId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.LibraryRepository.GetLibraryCoverImageAsync(libraryId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.VolumeRepository.GetVolumeCoverImageAsync(volumeId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.VolumeRepository.GetVolumeCoverImageAsync(volumeId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.SeriesRepository.GetSeriesCoverImageAsync(seriesId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.SeriesRepository.GetSeriesCoverImageAsync(seriesId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
Response.AddCacheHeader(path);
|
Response.AddCacheHeader(path);
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.CollectionTagRepository.GetCoverImageAsync(collectionTagId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.CollectionTagRepository.GetCoverImageAsync(collectionTagId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ public class ImageController : BaseApiController
|
|||||||
{
|
{
|
||||||
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.ReadingListRepository.GetCoverImageAsync(readingListId));
|
var path = Path.Join(_directoryService.CoverImageDirectory, await _unitOfWork.ReadingListRepository.GetCoverImageAsync(readingListId));
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"No cover image");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ public class ImageController : BaseApiController
|
|||||||
var bookmarkDirectory =
|
var bookmarkDirectory =
|
||||||
(await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.BookmarkDirectory)).Value;
|
(await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.BookmarkDirectory)).Value;
|
||||||
var file = new FileInfo(Path.Join(bookmarkDirectory, bookmark.FileName));
|
var file = new FileInfo(Path.Join(bookmarkDirectory, bookmark.FileName));
|
||||||
var format = Path.GetExtension(file.FullName).Replace(".", "");
|
var format = Path.GetExtension(file.FullName).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(file.FullName, "image/" + format, Path.GetFileName(file.FullName));
|
return PhysicalFile(file.FullName, "image/" + format, Path.GetFileName(file.FullName));
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ public class ImageController : BaseApiController
|
|||||||
|
|
||||||
var path = Path.Join(_directoryService.TempDirectory, filename);
|
var path = Path.Join(_directoryService.TempDirectory, filename);
|
||||||
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"File does not exist");
|
if (string.IsNullOrEmpty(path) || !_directoryService.FileSystem.File.Exists(path)) return BadRequest($"File does not exist");
|
||||||
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", "");
|
var format = _directoryService.FileSystem.Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
return PhysicalFile(path, "image/" + format, _directoryService.FileSystem.Path.GetFileName(path));
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ public class OpdsController : BaseApiController
|
|||||||
{
|
{
|
||||||
return BadRequest("You must pass a query parameter");
|
return BadRequest("You must pass a query parameter");
|
||||||
}
|
}
|
||||||
query = query.Replace(@"%", "");
|
query = query.Replace(@"%", string.Empty);
|
||||||
// Get libraries user has access to
|
// Get libraries user has access to
|
||||||
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(userId)).ToList();
|
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(userId)).ToList();
|
||||||
|
|
||||||
@ -828,7 +828,7 @@ public class OpdsController : BaseApiController
|
|||||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) return BadRequest($"No such image for page {pageNumber}");
|
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) return BadRequest($"No such image for page {pageNumber}");
|
||||||
|
|
||||||
var content = await _directoryService.ReadFileAsync(path);
|
var content = await _directoryService.ReadFileAsync(path);
|
||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
// Calculates SHA1 Hash for byte[]
|
// Calculates SHA1 Hash for byte[]
|
||||||
Response.AddCacheHeader(content);
|
Response.AddCacheHeader(content);
|
||||||
@ -860,7 +860,7 @@ public class OpdsController : BaseApiController
|
|||||||
if (files.Length == 0) return BadRequest("Cannot find icon");
|
if (files.Length == 0) return BadRequest("Cannot find icon");
|
||||||
var path = files[0];
|
var path = files[0];
|
||||||
var content = await _directoryService.ReadFileAsync(path);
|
var content = await _directoryService.ReadFileAsync(path);
|
||||||
var format = Path.GetExtension(path).Replace(".", "");
|
var format = Path.GetExtension(path).Replace(".", string.Empty);
|
||||||
|
|
||||||
return File(content, "image/" + format);
|
return File(content, "image/" + format);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ public class CacheService : ICacheService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("There was an error calculating image dimensions for {ChapterId}", chapterId);
|
_logger.LogError(ex, "There was an error calculating image dimensions for {ChapterId}", chapterId);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -164,7 +164,7 @@ public class CacheService : ICacheService
|
|||||||
{
|
{
|
||||||
var removeNonImages = true;
|
var removeNonImages = true;
|
||||||
var fileCount = files.Count;
|
var fileCount = files.Count;
|
||||||
var extraPath = "";
|
var extraPath = string.Empty;
|
||||||
var extractDi = _directoryService.FileSystem.DirectoryInfo.FromDirectoryName(extractPath);
|
var extractDi = _directoryService.FileSystem.DirectoryInfo.FromDirectoryName(extractPath);
|
||||||
|
|
||||||
if (files.Count > 0 && files[0].Format == MangaFormat.Image)
|
if (files.Count > 0 && files[0].Format == MangaFormat.Image)
|
||||||
|
@ -273,7 +273,7 @@ public class Startup
|
|||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowCredentials() // For SignalR token query param
|
.AllowCredentials() // For SignalR token query param
|
||||||
.WithOrigins("http://localhost:4200", $"http://{GetLocalIpAddress()}:4200")
|
.WithOrigins("http://localhost:4200", $"http://{GetLocalIpAddress()}:4200", $"http://{GetLocalIpAddress()}:5000")
|
||||||
.WithExposedHeaders("Content-Disposition", "Pagination"));
|
.WithExposedHeaders("Content-Disposition", "Pagination"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ The easiest way to get started is to visit our Wiki which has up-to-date informa
|
|||||||
install methods and platforms.
|
install methods and platforms.
|
||||||
[https://wiki.kavitareader.com/en/install](https://wiki.kavitareader.com/en/install)
|
[https://wiki.kavitareader.com/en/install](https://wiki.kavitareader.com/en/install)
|
||||||
|
|
||||||
**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 bleeding edge builds is `:nightly`. The `:latest` tag will be the latest stable release.**
|
||||||
|
|
||||||
## Feature Requests
|
## Feature Requests
|
||||||
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 before you submit an idea.
|
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 before you submit an idea.
|
||||||
@ -100,5 +100,5 @@ being paid to help secure Kavita, please give them a try.
|
|||||||
### License
|
### License
|
||||||
|
|
||||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||||
* Copyright 2020-2022
|
* Copyright 2020-2023
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ This project was generated with [Angular CLI](https://github.com/angular/angular
|
|||||||
## Development server
|
## Development server
|
||||||
|
|
||||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||||
|
Your backend must be served on port 5000.
|
||||||
|
|
||||||
## Code scaffolding
|
## Code scaffolding
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
|
|||||||
|
|
||||||
Run `npx playwright test --reporter=line` or `npx playwright test` to run e2e tests.
|
Run `npx playwright test --reporter=line` or `npx playwright test` to run e2e tests.
|
||||||
|
|
||||||
## Further help
|
## Connecting to your dev server via your phone
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
ng serve --host 0.0.0.0
|
||||||
|
and update environment.ts to your local ip.
|
||||||
|
27
UI/Web/package-lock.json
generated
27
UI/Web/package-lock.json
generated
@ -6735,6 +6735,24 @@
|
|||||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ag-swipe-core": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ag-swipe-core/-/ag-swipe-core-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NNONbrEbsmu6wsl7E07eGYVZw8Wx7hOok2TlhQLU/50EUhmI3Vpg8EDz0rWhV/HrfUAoEd4LxBvLAeT9DswQDw==",
|
||||||
|
"requires": {
|
||||||
|
"rxjs": "^7.5.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"rxjs": {
|
||||||
|
"version": "7.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz",
|
||||||
|
"integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
||||||
@ -12788,6 +12806,15 @@
|
|||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ng-swipe": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ng-swipe/-/ng-swipe-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-y4w2d719VK1u6KUlNqhHVevzT+yR30bnTTLkFNEsVG3Gp5+oZhUnflVNWfzIw+O8GCjZqVLelwla/jOkqUclmQ==",
|
||||||
|
"requires": {
|
||||||
|
"ag-swipe-core": "^1.0.0",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-color-picker": {
|
"ngx-color-picker": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"lazysizes": "^5.3.2",
|
"lazysizes": "^5.3.2",
|
||||||
"ng-circle-progress": "^1.6.0",
|
"ng-circle-progress": "^1.6.0",
|
||||||
|
"ng-swipe": "^2.0.1",
|
||||||
"ngx-color-picker": "^12.0.0",
|
"ngx-color-picker": "^12.0.0",
|
||||||
"ngx-extended-pdf-viewer": "^15.0.0",
|
"ngx-extended-pdf-viewer": "^15.0.0",
|
||||||
"ngx-file-drop": "^14.0.1",
|
"ngx-file-drop": "^14.0.1",
|
||||||
|
@ -993,7 +993,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
getPageWidth() {
|
getPageWidth() {
|
||||||
if (this.readingSectionElemRef == null) return 0;
|
if (this.readingSectionElemRef == null) return 0;
|
||||||
|
|
||||||
const margin = (this.readingSectionElemRef.nativeElement.clientWidth*(parseInt(this.pageStyles['margin-left'], 10) / 100))*2;
|
const margin = (this.readingSectionElemRef.nativeElement.clientWidth * (parseInt(this.pageStyles['margin-left'], 10) / 100)) * 2;
|
||||||
const columnGap = 20;
|
const columnGap = 20;
|
||||||
return this.readingSectionElemRef.nativeElement.clientWidth - margin + columnGap;
|
return this.readingSectionElemRef.nativeElement.clientWidth - margin + columnGap;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<app-loading [loading]="true"></app-loading>
|
<app-loading [loading]="isLoading"></app-loading>
|
||||||
|
|
||||||
<ng-template #jumpBar>
|
<ng-template #jumpBar>
|
||||||
<div class="jump-bar">
|
<div class="jump-bar">
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<app-loading [loading]="isLoading"></app-loading>
|
<app-loading [loading]="isLoading"></app-loading>
|
||||||
<div class="reading-area"
|
<div class="reading-area"
|
||||||
appSwipe (swipeEvent)="onSwipeEvent($event)"
|
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
|
||||||
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
|
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
|
||||||
|
|
||||||
<ng-container *ngIf="readerMode !== ReaderMode.Webtoon; else webtoon">
|
<ng-container *ngIf="readerMode !== ReaderMode.Webtoon; else webtoon">
|
||||||
@ -43,14 +43,14 @@
|
|||||||
|
|
||||||
<!-- Pagination controls and screen hints-->
|
<!-- Pagination controls and screen hints-->
|
||||||
<div class="pagination-area">
|
<div class="pagination-area">
|
||||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, 'left')"
|
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, KeyDirection.Left)"
|
||||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: '25%')}">
|
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: '25%')}">
|
||||||
<div *ngIf="showClickOverlay">
|
<div *ngIf="showClickOverlay">
|
||||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
||||||
title="Previous Page" aria-hidden="true"></i>
|
title="Previous Page" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, 'right')"
|
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, KeyDirection.Left)"
|
||||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: '25%'),
|
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: '25%'),
|
||||||
'left': 'inherit',
|
'left': 'inherit',
|
||||||
'right': RightPaginationOffset + 'px'}">
|
'right': RightPaginationOffset + 'px'}">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, Renderer2, SimpleChanges, ViewChild } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { BehaviorSubject, combineLatest, debounceTime, distinctUntilChanged, filter, forkJoin, fromEvent, map, merge, Observable, of, ReplaySubject, Subject, take, takeUntil, tap } from 'rxjs';
|
import { BehaviorSubject, debounceTime, distinctUntilChanged, forkJoin, fromEvent, map, merge, Observable, ReplaySubject, Subject, take, takeUntil, tap } from 'rxjs';
|
||||||
import { LabelType, ChangeContext, Options } from '@angular-slider/ngx-slider';
|
import { LabelType, ChangeContext, 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 { FormGroup, FormBuilder, FormControl } from '@angular/forms';
|
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
|
||||||
@ -31,6 +31,7 @@ import { DoubleRendererComponent } from '../double-renderer/double-renderer.comp
|
|||||||
import { DoubleReverseRendererComponent } from '../double-reverse-renderer/double-reverse-renderer.component';
|
import { DoubleReverseRendererComponent } from '../double-reverse-renderer/double-reverse-renderer.component';
|
||||||
import { SingleRendererComponent } from '../single-renderer/single-renderer.component';
|
import { SingleRendererComponent } from '../single-renderer/single-renderer.component';
|
||||||
import { ChapterInfo } from '../../_models/chapter-info';
|
import { ChapterInfo } from '../../_models/chapter-info';
|
||||||
|
import { SwipeEvent } from 'ng-swipe';
|
||||||
|
|
||||||
|
|
||||||
const PREFETCH_PAGES = 10;
|
const PREFETCH_PAGES = 10;
|
||||||
@ -298,6 +299,43 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
rightPaginationOffset = 0;
|
rightPaginationOffset = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous amount of scroll left. Used for swipe to paginate functionaliy.
|
||||||
|
*/
|
||||||
|
prevScrollLeft = 0;
|
||||||
|
/**
|
||||||
|
* Previous amount of scroll top. Used for swipe to paginate functionaliy.
|
||||||
|
*/
|
||||||
|
prevScrollTop = 0;
|
||||||
|
|
||||||
|
prevIsHorizontalScrollLeft = true;
|
||||||
|
prevIsVerticalScrollLeft = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has the user scrolled to the far right side. This is used for swipe to next page and must ensure user is at end of scroll then on next swipe, will move pages.
|
||||||
|
*/
|
||||||
|
hasHitRightScroll = false;
|
||||||
|
/**
|
||||||
|
* Has the user scrolled once for the current page
|
||||||
|
*/
|
||||||
|
hasScrolledX: boolean = false;
|
||||||
|
/**
|
||||||
|
* Has the user scrolled once in the Y axis for the current page
|
||||||
|
*/
|
||||||
|
hasScrolledY: boolean = false;
|
||||||
|
/**
|
||||||
|
* Has the user scrolled to far left size. This doesn't include starting from no scroll
|
||||||
|
*/
|
||||||
|
hasHitZeroScroll: boolean = false;
|
||||||
|
/**
|
||||||
|
* Has the user scrolled to the far top of the screen
|
||||||
|
*/
|
||||||
|
hasHitZeroTopScroll: boolean = false;
|
||||||
|
/**
|
||||||
|
* Has the user scrolled to the far bottom of the screen
|
||||||
|
*/
|
||||||
|
hasHitBottomTopScroll: boolean = false;
|
||||||
|
|
||||||
// Renderer interaction
|
// Renderer interaction
|
||||||
readerSettings$!: Observable<ReaderSetting>;
|
readerSettings$!: Observable<ReaderSetting>;
|
||||||
private currentImage: Subject<HTMLImageElement | null> = new ReplaySubject(1);
|
private currentImage: Subject<HTMLImageElement | null> = new ReplaySubject(1);
|
||||||
@ -352,6 +390,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get KeyDirection() { return KeyDirection; }
|
||||||
get ReaderMode() { return ReaderMode; }
|
get ReaderMode() { return ReaderMode; }
|
||||||
get LayoutMode() { return LayoutMode; }
|
get LayoutMode() { return LayoutMode; }
|
||||||
get ReadingDirection() { return ReadingDirection; }
|
get ReadingDirection() { return ReadingDirection; }
|
||||||
@ -359,6 +398,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
get Breakpoint() { return Breakpoint; }
|
get Breakpoint() { return Breakpoint; }
|
||||||
get FITTING_OPTION() { return FITTING_OPTION; }
|
get FITTING_OPTION() { return FITTING_OPTION; }
|
||||||
get FittingOption() { return this.generalSettingsForm.get('fittingOption')?.value || FITTING_OPTION.HEIGHT; }
|
get FittingOption() { return this.generalSettingsForm.get('fittingOption')?.value || FITTING_OPTION.HEIGHT; }
|
||||||
|
get ReadingAreaWidth() {
|
||||||
|
return this.readingArea?.nativeElement.scrollWidth - this.readingArea?.nativeElement.clientWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ReadingAreaHeight() {
|
||||||
|
return this.readingArea?.nativeElement.scrollHeight - this.readingArea?.nativeElement.clientHeight;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
||||||
public readerService: ReaderService, private formBuilder: FormBuilder, private navService: NavService,
|
public readerService: ReaderService, private formBuilder: FormBuilder, private navService: NavService,
|
||||||
@ -515,10 +561,17 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
fromEvent(this.readingArea.nativeElement, 'click').pipe(debounceTime(200)).subscribe((event: MouseEvent | any) => {
|
fromEvent(this.readingArea.nativeElement, 'click').pipe(debounceTime(200), takeUntil(this.onDestroy)).subscribe((event: MouseEvent | any) => {
|
||||||
if (event.detail > 1) return;
|
if (event.detail > 1) return;
|
||||||
this.toggleMenu();
|
this.toggleMenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fromEvent(this.readingArea.nativeElement, 'scroll').pipe(debounceTime(200), takeUntil(this.onDestroy)).subscribe((event: MouseEvent | any) => {
|
||||||
|
this.prevScrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
||||||
|
this.prevScrollTop = this.readingArea?.nativeElement?.scrollTop || 0;
|
||||||
|
this.hasScrolledX = true;
|
||||||
|
this.hasScrolledY = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -531,6 +584,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (this.goToPageEvent !== undefined) this.goToPageEvent.complete();
|
if (this.goToPageEvent !== undefined) this.goToPageEvent.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
@HostListener('window:orientationchange', ['$event'])
|
@HostListener('window:orientationchange', ['$event'])
|
||||||
onResize() {
|
onResize() {
|
||||||
@ -647,9 +701,28 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is scroll room and on original, then don't paginate
|
|
||||||
|
|
||||||
|
isHorizontalScrollLeft() {
|
||||||
|
const scrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
||||||
|
// if scrollLeft is 0 and this.ReadingAreaWidth is 0, then there is no scroll needed
|
||||||
|
// if they equal each other, it means we are at the end of the scroll area
|
||||||
|
if (scrollLeft === 0 && this.ReadingAreaWidth === 0) return false;
|
||||||
|
if (scrollLeft === this.ReadingAreaWidth) return false;
|
||||||
|
return scrollLeft < this.ReadingAreaWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
isVerticalScrollLeft() {
|
||||||
|
const scrollTop = this.readingArea?.nativeElement?.scrollTop || 0;
|
||||||
|
return scrollTop < this.ReadingAreaHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there any room to scroll in the direction we are giving? If so, return false. Otherwise return true.
|
||||||
|
* @param direction
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
checkIfPaginationAllowed(direction: KeyDirection) {
|
checkIfPaginationAllowed(direction: KeyDirection) {
|
||||||
// This is not used atm due to the complexity it adds with keyboard.
|
|
||||||
if (this.readingArea === undefined || this.readingArea.nativeElement === undefined) return true;
|
if (this.readingArea === undefined || this.readingArea.nativeElement === undefined) return true;
|
||||||
|
|
||||||
const scrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
const scrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
||||||
@ -657,22 +730,30 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case KeyDirection.Right:
|
case KeyDirection.Right:
|
||||||
if (this.FittingOption === FITTING_OPTION.ORIGINAL && scrollLeft < this.readingArea?.nativeElement.scrollWidth - this.readingArea?.nativeElement.clientWidth) {
|
if (this.prevIsHorizontalScrollLeft && !this.isHorizontalScrollLeft()) { return true; }
|
||||||
|
this.prevIsHorizontalScrollLeft = this.isHorizontalScrollLeft();
|
||||||
|
|
||||||
|
if (this.isHorizontalScrollLeft()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyDirection.Left:
|
case KeyDirection.Left:
|
||||||
if (this.FittingOption === FITTING_OPTION.ORIGINAL && scrollLeft > 0) {
|
this.prevIsHorizontalScrollLeft = this.isHorizontalScrollLeft();
|
||||||
|
if (scrollLeft > 0 || this.prevScrollLeft > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyDirection.Up:
|
case KeyDirection.Up:
|
||||||
if (this.FittingOption === FITTING_OPTION.ORIGINAL && scrollTop > 0) {
|
this.prevIsVerticalScrollLeft = this.isVerticalScrollLeft();
|
||||||
|
if (scrollTop > 0|| this.prevScrollTop > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyDirection.Down:
|
case KeyDirection.Down:
|
||||||
if (this.FittingOption === FITTING_OPTION.ORIGINAL && scrollTop < this.readingArea?.nativeElement.scrollHeight - this.readingArea?.nativeElement.clientHeight) {
|
if (this.prevIsVerticalScrollLeft && !this.isVerticalScrollLeft()) { return true; }
|
||||||
|
this.prevIsVerticalScrollLeft = this.isVerticalScrollLeft();
|
||||||
|
|
||||||
|
if (this.isVerticalScrollLeft()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -681,17 +762,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is menu code
|
|
||||||
clickOverlayClass(side: 'right' | 'left') {
|
|
||||||
if (!this.showClickOverlay) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
|
||||||
return side === 'right' ? 'highlight' : 'highlight-2';
|
|
||||||
}
|
|
||||||
return side === 'right' ? 'highlight-2' : 'highlight';
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.nextChapterId = CHAPTER_ID_NOT_FETCHED;
|
this.nextChapterId = CHAPTER_ID_NOT_FETCHED;
|
||||||
@ -878,21 +949,169 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSwipeEvent(event: any) {
|
resetSwipeModifiers() {
|
||||||
|
this.prevScrollLeft = 0;
|
||||||
|
this.prevScrollTop = 0;
|
||||||
|
this.hasScrolledX = false;
|
||||||
|
this.hasScrolledY = false;
|
||||||
|
this.hasHitRightScroll = false;
|
||||||
|
this.hasHitZeroScroll = false;
|
||||||
|
this.hasHitBottomTopScroll = false;
|
||||||
|
this.hasHitZeroTopScroll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This executes BEFORE fromEvent('scroll')
|
||||||
|
* @param event
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
onSwipeMove(_: SwipeEvent) {
|
||||||
|
this.prevScrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
||||||
|
this.prevScrollTop = this.readingArea?.nativeElement?.scrollTop || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(event: any, direction: string) {
|
triggerSwipePagination(direction: KeyDirection) {
|
||||||
|
|
||||||
|
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
||||||
|
if (direction === KeyDirection.Right)
|
||||||
|
this.readingDirection === ReadingDirection.LeftToRight ? this.nextPage() : this.prevPage();
|
||||||
|
}
|
||||||
|
switch(direction) {
|
||||||
|
case KeyDirection.Down:
|
||||||
|
this.nextPage();
|
||||||
|
break;
|
||||||
|
case KeyDirection.Right:
|
||||||
|
this.readingDirection === ReadingDirection.LeftToRight ? this.nextPage() : this.prevPage();
|
||||||
|
break;
|
||||||
|
case KeyDirection.Up:
|
||||||
|
this.prevPage();
|
||||||
|
break;
|
||||||
|
case KeyDirection.Left:
|
||||||
|
this.readingDirection === ReadingDirection.LeftToRight ? this.prevPage() : this.nextPage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSwipeEnd(event: SwipeEvent) {
|
||||||
|
const threshold = .12;
|
||||||
|
|
||||||
|
// Positive number means swiping right/down, negative means left
|
||||||
|
switch (this.readerMode) {
|
||||||
|
case ReaderMode.Webtoon: break;
|
||||||
|
case ReaderMode.LeftRight:
|
||||||
|
{
|
||||||
|
if (event.direction !== 'x') return;
|
||||||
|
const scrollLeft = this.readingArea?.nativeElement?.scrollLeft || 0;
|
||||||
|
const direction = event.distance < 0 ? KeyDirection.Right : KeyDirection.Left;
|
||||||
|
if (!this.checkIfPaginationAllowed(direction)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We just came from a swipe where pagination was required and we are now at the end of the swipe, so make the user do it once more
|
||||||
|
if (direction === KeyDirection.Right) {
|
||||||
|
this.hasHitZeroScroll = false;
|
||||||
|
if (!this.hasHitRightScroll && this.checkIfPaginationAllowed(direction)) {
|
||||||
|
this.hasHitRightScroll = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (direction === KeyDirection.Left) {
|
||||||
|
this.hasHitRightScroll = false;
|
||||||
|
|
||||||
|
// If we have not scrolled then let the user page back
|
||||||
|
if (scrollLeft === 0 && this.prevScrollLeft === 0) {
|
||||||
|
if (!this.hasScrolledX || this.hasHitZeroScroll) {
|
||||||
|
this.triggerSwipePagination(direction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hasHitZeroScroll = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasHitRightScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Next page triggered');
|
||||||
|
this.triggerSwipePagination(direction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReaderMode.UpDown:
|
||||||
|
{
|
||||||
|
if (event.direction !== 'y') return;
|
||||||
|
const direction = event.distance < 0 ? KeyDirection.Down : KeyDirection.Up;
|
||||||
|
const scrollTop = this.readingArea?.nativeElement?.scrollTop || 0;
|
||||||
|
if (!this.checkIfPaginationAllowed(direction)) return;
|
||||||
|
|
||||||
|
|
||||||
|
if (direction === KeyDirection.Down) {
|
||||||
|
this.hasHitZeroTopScroll = false;
|
||||||
|
if (!this.hasHitBottomTopScroll && this.checkIfPaginationAllowed(direction)) {
|
||||||
|
this.hasHitBottomTopScroll = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (direction === KeyDirection.Up) {
|
||||||
|
this.hasHitBottomTopScroll = false;
|
||||||
|
|
||||||
|
// If we have not scrolled then let the user page back
|
||||||
|
if (scrollTop === 0 && this.prevScrollTop === 0) {
|
||||||
|
if (!this.hasScrolledY || this.hasHitZeroTopScroll) {
|
||||||
|
this.triggerSwipePagination(direction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hasHitZeroTopScroll = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasHitBottomTopScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Next page triggered');
|
||||||
|
this.triggerSwipePagination(direction);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const height = (this.readingArea?.nativeElement.scrollHeight === this.readingArea?.nativeElement.clientHeight)
|
||||||
|
? this.readingArea?.nativeElement.clientHeight : this.ReadingAreaHeight;
|
||||||
|
|
||||||
|
if (direction === KeyDirection.Down && this.readingArea?.nativeElement?.scrollTop === height && this.prevScrollTop != 0) {
|
||||||
|
this.prevScrollTop = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction === KeyDirection.Up && this.readingArea?.nativeElement?.scrollTop === 0 && this.prevScrollTop != 0) {
|
||||||
|
this.prevScrollTop = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const thresholdMet = Math.abs(event.distance) >= height * threshold;
|
||||||
|
if (!thresholdMet) return;
|
||||||
|
|
||||||
|
this.triggerSwipePagination(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePageChange(event: any, direction: KeyDirection) {
|
||||||
if (this.readerMode === ReaderMode.Webtoon) {
|
if (this.readerMode === ReaderMode.Webtoon) {
|
||||||
if (direction === 'right') {
|
if (direction === KeyDirection.Right) {
|
||||||
this.nextPage(event);
|
this.nextPage(event);
|
||||||
} else {
|
} else {
|
||||||
this.prevPage(event);
|
this.prevPage(event);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (direction === 'right') {
|
if (direction === KeyDirection.Right) {
|
||||||
this.readingDirection === ReadingDirection.LeftToRight ? this.nextPage(event) : this.prevPage(event);
|
this.readingDirection === ReadingDirection.LeftToRight ? this.nextPage(event) : this.prevPage(event);
|
||||||
} else if (direction === 'left') {
|
} else if (direction === KeyDirection.Left) {
|
||||||
this.readingDirection === ReadingDirection.LeftToRight ? this.prevPage(event) : this.nextPage(event);
|
this.readingDirection === ReadingDirection.LeftToRight ? this.prevPage(event) : this.nextPage(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -903,6 +1122,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resetSwipeModifiers();
|
||||||
|
|
||||||
this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD);
|
this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD);
|
||||||
|
|
||||||
const pageAmount = Math.max(this.canvasRenderer.getPageAmount(PAGING_DIRECTION.FORWARD), this.singleRenderer.getPageAmount(PAGING_DIRECTION.FORWARD),
|
const pageAmount = Math.max(this.canvasRenderer.getPageAmount(PAGING_DIRECTION.FORWARD), this.singleRenderer.getPageAmount(PAGING_DIRECTION.FORWARD),
|
||||||
@ -925,6 +1146,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resetSwipeModifiers();
|
||||||
|
|
||||||
this.pagingDirectionSubject.next(PAGING_DIRECTION.BACKWARDS);
|
this.pagingDirectionSubject.next(PAGING_DIRECTION.BACKWARDS);
|
||||||
|
|
||||||
|
|
||||||
@ -1030,16 +1254,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
renderPage() {
|
renderPage() {
|
||||||
const page = [this.canvasImage];
|
const page = [this.canvasImage];
|
||||||
|
|
||||||
// After switching from webtoon mode, these are all undefined
|
|
||||||
|
|
||||||
this.canvasRenderer?.renderPage(page);
|
this.canvasRenderer?.renderPage(page);
|
||||||
this.singleRenderer?.renderPage(page);
|
this.singleRenderer?.renderPage(page);
|
||||||
this.doubleRenderer?.renderPage(page);
|
this.doubleRenderer?.renderPage(page);
|
||||||
this.doubleReverseRenderer?.renderPage(page);
|
this.doubleReverseRenderer?.renderPage(page);
|
||||||
|
|
||||||
if (this.FittingOption !== FITTING_OPTION.HEIGHT) {
|
// Originally this was only for fit to height, but when swiping was introduced, it made more sense to do it always to reset to the same view
|
||||||
this.readingArea.nativeElement.scroll(0,0);
|
this.readingArea.nativeElement.scroll(0,0);
|
||||||
}
|
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
@ -1244,6 +1465,18 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is menu code
|
||||||
|
clickOverlayClass(side: 'right' | 'left') {
|
||||||
|
if (!this.showClickOverlay) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
||||||
|
return side === 'right' ? 'highlight' : 'highlight-2';
|
||||||
|
}
|
||||||
|
return side === 'right' ? 'highlight-2' : 'highlight';
|
||||||
|
}
|
||||||
|
|
||||||
// This is menu only code
|
// This is menu only code
|
||||||
promptForPage() {
|
promptForPage() {
|
||||||
const goToPageNum = window.prompt('There are ' + this.maxPages + ' pages. What page would you like to go to?', '');
|
const goToPageNum = window.prompt('There are ' + this.maxPages + ' pages. What page would you like to go to?', '');
|
||||||
|
@ -11,13 +11,13 @@ import { PipeModule } from '../pipe/pipe.module';
|
|||||||
import { FullscreenIconPipe } from './_pipes/fullscreen-icon.pipe';
|
import { FullscreenIconPipe } from './_pipes/fullscreen-icon.pipe';
|
||||||
import { LayoutModeIconPipe } from './_pipes/layout-mode-icon.pipe';
|
import { LayoutModeIconPipe } from './_pipes/layout-mode-icon.pipe';
|
||||||
import { ReaderModeIconPipe } from './_pipes/reader-mode-icon.pipe';
|
import { ReaderModeIconPipe } from './_pipes/reader-mode-icon.pipe';
|
||||||
import { SwipeDirective } from './swipe.directive';
|
|
||||||
import { CanvasRendererComponent } from './_components/canvas-renderer/canvas-renderer.component';
|
import { CanvasRendererComponent } from './_components/canvas-renderer/canvas-renderer.component';
|
||||||
import { SingleRendererComponent } from './_components/single-renderer/single-renderer.component';
|
import { SingleRendererComponent } from './_components/single-renderer/single-renderer.component';
|
||||||
import { DoubleRendererComponent } from './_components/double-renderer/double-renderer.component';
|
import { DoubleRendererComponent } from './_components/double-renderer/double-renderer.component';
|
||||||
import { DoubleReverseRendererComponent } from './_components/double-reverse-renderer/double-reverse-renderer.component';
|
import { DoubleReverseRendererComponent } from './_components/double-reverse-renderer/double-reverse-renderer.component';
|
||||||
import { MangaReaderComponent } from './_components/manga-reader/manga-reader.component';
|
import { MangaReaderComponent } from './_components/manga-reader/manga-reader.component';
|
||||||
import { FittingIconPipe } from './_pipes/fitting-icon.pipe';
|
import { FittingIconPipe } from './_pipes/fitting-icon.pipe';
|
||||||
|
import { SwipeModule } from 'ng-swipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -26,7 +26,6 @@ import { FittingIconPipe } from './_pipes/fitting-icon.pipe';
|
|||||||
FullscreenIconPipe,
|
FullscreenIconPipe,
|
||||||
ReaderModeIconPipe,
|
ReaderModeIconPipe,
|
||||||
LayoutModeIconPipe,
|
LayoutModeIconPipe,
|
||||||
SwipeDirective,
|
|
||||||
CanvasRendererComponent,
|
CanvasRendererComponent,
|
||||||
SingleRendererComponent,
|
SingleRendererComponent,
|
||||||
DoubleRendererComponent,
|
DoubleRendererComponent,
|
||||||
@ -43,6 +42,8 @@ import { FittingIconPipe } from './_pipes/fitting-icon.pipe';
|
|||||||
NgxSliderModule,
|
NgxSliderModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
ReaderSharedModule,
|
ReaderSharedModule,
|
||||||
|
|
||||||
|
SwipeModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
MangaReaderComponent
|
MangaReaderComponent
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
|
||||||
import { fromEvent, map, Observable } from 'rxjs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repsonsible for triggering a swipe event
|
|
||||||
*/
|
|
||||||
@Directive({
|
|
||||||
selector: '[appSwipe]'
|
|
||||||
})
|
|
||||||
export class SwipeDirective {
|
|
||||||
|
|
||||||
@Input() threshold: number = 10;
|
|
||||||
@Output() swipeEvent: EventEmitter<any> = new EventEmitter<any>();
|
|
||||||
|
|
||||||
touchStarts$!: Observable<void>;
|
|
||||||
touchMoves$!: Observable<void>;
|
|
||||||
touchEnds$!: Observable<void>;
|
|
||||||
touchCancels$!: Observable<TouchEvent>;
|
|
||||||
|
|
||||||
@HostListener('touchstart') onTouchStart(event: TouchEvent) {
|
|
||||||
//console.log('Touch Start: ', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('touchend') onTouchEnd(event: TouchEvent) {
|
|
||||||
//console.log('Touch End: ', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private el: ElementRef) {
|
|
||||||
this.touchStarts$ = fromEvent<TouchEvent>(el.nativeElement, 'touchstart').pipe(map(this.getTouchCoordinates));
|
|
||||||
this.touchMoves$ = fromEvent<TouchEvent>(el.nativeElement, 'touchmove').pipe(map(this.getTouchCoordinates));
|
|
||||||
this.touchEnds$ = fromEvent<TouchEvent>(el.nativeElement, 'touchend').pipe(map(this.getTouchCoordinates));
|
|
||||||
this.touchCancels$ = fromEvent<TouchEvent>(el.nativeElement, 'touchcancel');
|
|
||||||
}
|
|
||||||
|
|
||||||
getTouchCoordinates(event: TouchEvent) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -37,11 +37,10 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
downloadInProgress: boolean = false;
|
downloadInProgress: boolean = false;
|
||||||
|
|
||||||
readingListSummary: string = '';
|
readingListSummary: string = '';
|
||||||
|
readingListImage: string = '';
|
||||||
|
|
||||||
libraryTypes: {[key: number]: LibraryType} = {};
|
libraryTypes: {[key: number]: LibraryType} = {};
|
||||||
|
|
||||||
readingListImage: string = '';
|
|
||||||
|
|
||||||
get MangaFormat(): typeof MangaFormat {
|
get MangaFormat(): typeof MangaFormat {
|
||||||
return MangaFormat;
|
return MangaFormat;
|
||||||
}
|
}
|
||||||
@ -60,6 +59,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.listId = parseInt(listId, 10);
|
this.listId = parseInt(listId, 10);
|
||||||
|
this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
|
||||||
|
|
||||||
forkJoin([
|
forkJoin([
|
||||||
this.libraryService.getLibraries(),
|
this.libraryService.getLibraries(),
|
||||||
@ -134,6 +134,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
// Reload information around list
|
// Reload information around list
|
||||||
this.readingList = readingList;
|
this.readingList = readingList;
|
||||||
this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '<br>');
|
this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '<br>');
|
||||||
|
this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"name": "GPL-3.0",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.6.1.22"
|
"version": "0.6.1.24"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user