Feat: ReadMarkers: Read marks on chapter button, jump to last read page

master
masterhc 1 year ago
parent 5ebbae9469
commit 05c2149e47

@ -20,7 +20,7 @@ exports.searchBar = (req, res)=>
exports.search = async (req, res)=>
{
if(!req.body?.searchstring) return res.sendStatus(404)
var data = await new Search(req.body.searchstring).search();
var data = await new Search(req.body.searchstring).searchExtraInfo();
if(!Array.isArray(data)) return res.sendStatus(404);
const favorites = await FavoriteModel.find()
const favoriteScanlators = favorites.map(favorite => favorite.scanlator);
@ -109,13 +109,22 @@ exports.manga = async (req, res)=>
const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
if(!scanlatorHasTitle) return res.sendStatus(404);
let manga;
let chaptersRead;
try
{
manga = await new Manga(scanlator,link, title).get()
manga = await new Manga(scanlator,link, title).get();
chaptersRead = await ChapterModel.find({scanlator,title});
for(let i = 0; i<manga.List.length; i++)
{
for(let j = 0; j<chaptersRead.length; j++)
{
if(manga.List[i].num==chaptersRead[j].chapterNum) manga.List[i]['Read'] = true;
}
}
}
catch (error)
{
res.sendStatus(404);
console.log('There was an error:', error);
}
res.render('mangaPage.ejs', {data:{...manga, scanlator}});
@ -125,13 +134,14 @@ exports.chapter = async (req, res)=>
const {scanlator, title, chapter, link} = req.params;
//IF its already there even with wrong things its there just show it
const fileExists = fs.existsSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`);
if(fileExists)
{
const imgs =fs.readdirSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`);
const latestChap = fs.readdirSync(`./public/Saved/${scanlator}/${title}`).length
const List = imgs.map(filename => {return `./Saved/${scanlator}/${title}/CH_${chapter}/${filename}`});
const mangaLink = await new Chapter(scanlator, link, title, chapter).getMangaLink();
return res.render('display.ejs', {data:{title,latestChap,scanlator, chapterNum:parseInt(chapter), mangaLink, List}});
return res.render('chapterPage.ejs', {data:{title,latestChap,scanlator, chapterNum:parseInt(chapter), mangaLink, List}});
}
//If it isn't make sure there is no bad params being passed
const scanlatorExists = await new Modules(scanlator).exists();
@ -147,7 +157,7 @@ exports.chapter = async (req, res)=>
{
return res.sendStatus(404);
}
res.render('display.ejs', {data:{...manga, scanlator, chapterNum:parseInt(chapter)}});
res.render('chapterPage.ejs', {data:{...manga, scanlator, chapterNum:parseInt(chapter)}});
}
exports.favorites = async (req, res)=>
@ -401,20 +411,73 @@ exports.chapterRead = async (req, res)=>
const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
if(!scanlatorHasTitle) return res.sendStatus(404);
const manga = await new Manga(scanlator, link, title).get();
const {latestChap} = manga;
const {latestChap, List} = manga;
if(chapter > latestChap || chapter < 0) return res.sendStatus(404);
const chap = new ChapterModel();
let chapterNum = chapter;
let chapterLink = List.filter(item=>item.num == chapterNum)[0].link;
if(!chapterLink) return res.sendStatus(404);
const _chapter = await new Chapter(scanlator, chapterLink,title, chapter).get();
let lastImageRead = _chapter.List[_chapter.List.length-1];
chap.lastImageRead = lastImageRead;
const chaps = await ChapterModel.find({scanlator, title, link, chapterNum});
if(chaps.length>0) return res.sendStatus(404);
if(chaps.length>0) return updateChapter(chaps[0],res, lastImageRead, true)
chap.scanlator = scanlator;
chap.title = title;
chap.link = link;
chap.chapterNum = chapter;
chap.lastImageRead = lastImageRead;
chap.completely = true;
chap.save()
res.sendStatus(200);
//Send to db;
}
async function updateChapter(chap, res, lastImageRead, completely=false)
{
await ChapterModel.findOneAndUpdate(chap._id,{completely, lastImageRead:lastImageRead}, {new:true});
res.sendStatus(200);
}
exports.chapterIncompleteRead = async (req,res)=>
{
const {scanlator, title, chapter, link, img} = req.params;
const scanlatorExists = await new Modules(scanlator).exists();
if(!scanlatorExists) return res.sendStatus(404);
const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
if(!scanlatorHasTitle) return res.sendStatus(404);
let chapterNum = chapter;
const manga = await new Manga(scanlator, link, title).get();
const {latestChap, List} = manga;
let chapterLink = List.filter(item=> item.num == chapterNum)[0].link;
if(chapter > latestChap || chapter < 0) return res.sendStatus(404);
const chap = new ChapterModel();
const _chapter = await new Chapter(scanlator, chapterLink,title, chapterNum).get();
let lastImageRead = _chapter.List.filter(item=>
{
let parsedImageURL = item.split('.')[item.split('.').length-2].split('/')[item.split('.')[item.split('.').length-2].split('/').length-1]
return(parsedImageURL==img)
})[0];
const chaps = await ChapterModel.find({scanlator, title, link, chapterNum});
if(chaps.length>0) return updateChapter(chaps[0],res, lastImageRead)
chap.scanlator = scanlator;
chap.title = title;
chap.link = link;
chap.chapterNum = chapter;
chap.lastImageRead = lastImageRead;
chap.completely = false;
chap.save()
res.sendStatus(200);
}
exports.chapterLastRead = async (req, res)=>
{
const {scanlator, title, chapter, link} = req.params;
const scanlatorExists = await new Modules(scanlator).exists();
if(!scanlatorExists) return res.sendStatus(404);
const scanlatorHasTitle = await new Modules(scanlator).titleExists(title);
if(!scanlatorHasTitle) return res.sendStatus(404);
let chapterNum = chapter;
const chaps = await ChapterModel.find({scanlator, title, link, chapterNum});
if(chaps.length == 0) return res.sendStatus(404);
let data = chaps[0].lastImageRead;
res.render('data.ejs', {data});
}
exports.dashboard = async (req, res)=>
{

@ -8,7 +8,7 @@ new Schema(
title: {type: String, required: true, max: 100},
link:{type: String, require:true, max:100},
chapterNum:{type: Number, require:true},
lastImageRead:{type:Number, required:false},
lastImageRead:{type:String, required:false},
completely:{type:Boolean, required:false}
}
);

@ -353,6 +353,7 @@ button:hover
margin-left: 1em;
margin-right: 1em;
margin-top: 1em;
min-height: 3.6em;
}
.card:hover > .card__img
{
@ -463,12 +464,27 @@ button:hover
text-decoration: none;
color: var(--text);
}
.list > .chapterButton > a > h4 , .list > .chapterButton > a > h5
.buttonWrapper
{
display:flex;
flex-wrap: nowrap;
flex-direction: row;
align-items: center;
justify-content: center;
height:100%
}
.list > .chapterButton > a >.buttonWrapper > div > h4 ,
.list > .chapterButton > a >.buttonWrapper > div > h5
{
margin: 0;
}
.readMarker
{
/* margin-left: 0.6em; */
margin-bottom: 0.1em;
margin-top: 0.1em;
height:auto;
width:auto;
color:var(--text);
position: relative;
left: 0.7em;
}
.infoCard > .infoText > .tags
{

@ -23,6 +23,7 @@ module.exports = (worker)=>
router.route('/logout').get(api.logout);
router.route('/chapterRead/:scanlator/:link/:title/:chapter/').post(api.chapterRead);
router.route('/chapterRead/:scanlator/:link/:title/:chapter/:img').post(api.chapterIncompleteRead);
router.route('/chapterReadLastImage/:scanlator/:link/:title/:chapter/').get(api.chapterLastRead);
//Undefined Routes v

@ -1,10 +1,20 @@
<div class="chapterButton">
<a hx-get="/chapter/<%=scanlator %>/<%=encodeURIComponent(chapter.link)%> /<%= title %>/<%= chapter.num %>" hx-trigger="click" hx-target="#container" hx-indicator=".progress">
<h4 class="chapterNum">
Chapter <%= chapter.num %>
</h4>
<h5 class="date">
<%= chapter.date %>
</h5>
<a href="/chapter/<%=scanlator %>/<%=encodeURIComponent(chapter.link)%>/<%= title %>/<%= chapter.num %>" >
<div class="buttonWrapper">
<% if(chapter.Read){ %>
<span class="material-symbols-outlined readMarker">
beenhere
</span>
<% } %>
<div>
<h4 class="chapterNum">
Chapter <%= chapter.num %>
</h4>
<h5 class="date">
<%= chapter.date %>
</h5>
</div>
</div>
</a>
</div>
</div>

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="icon" type="image/ico" href="images/favicon.ico"/>
<title>Manga Reader</title>
<link rel="stylesheet" type="text/css" href="/css/home.css" id="pagesheet"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<script type="module" src="/js/home.js"></script>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
<div class="progress" style="height: 3px; background-color: white;">
<div class="indeterminate" style="background-color: red;"></div>
</div>
<div id="wrapper">
<%- include('navBar.ejs', {data, selected:'none'}) %>
<div class="container" id="container" >
<%- include('display.ejs', {data}) %>
</div>
</div>
</body>
</html>

@ -0,0 +1,3 @@
<data data-src="<%= data %>">
</data>

@ -2,10 +2,10 @@
<div class="chapterOverview" id="chapterOverview">
<div class="flexBox" id="flexBox">
<% for(var [index, img] of data.List.entries()){ %>
<img class="overview" id="img<%= index %>" src=<%= img.replaceAll(' ','%20') %> onclick="_scrollTo('<%= '_img'+index%>')" />
<img class="overview" id="img<%= index %>" src=<%= img.startsWith('.')?img.replace('.',''):img.replaceAll(' ','%20') %> onclick="_scrollTo('<%= '_img'+index%>')" />
<% } %>
</div>
<div class="expand" onclick="toggle()">
<div class="expand" onclick="_toggle()">
<span class="material-symbols-outlined" id="expandChevron">chevron_right</span>
</div>
</div>
@ -24,7 +24,7 @@
<button class="pullBackUp" onclick="pullUp()"> <span class="material-symbols-outlined chevronUp">expand_less</span></button>
<div id="display">
<% for(var [index, img] of data.List.entries()){ %>
<img id="_img<%= index %>" src=<%= img.replaceAll(' ','%20') %> alt="img<%= index %>"/>
<img class="_<%= img.split('.')[img.split('.').length-2].split('/')[img.split('.')[img.split('.').length-2].split('/').length-1]%>" id="_img<%= index %>" src=<%= img.startsWith('.')?img.replace('.',''):img.replaceAll(' ','%20') %> alt="img<%= index %>"/>
<% } %>
<div id="last-image"
hx-post="/chapterRead/<%= data.scanlator %>/<%= encodeURIComponent(data.mangaLink)%>/<%= data.title %>/<%= data.chapterNum %>"
@ -40,64 +40,70 @@
>
</div>
</div>
<div
id="lastImageRecordedAsRead"
hx-get= "/chapterReadLastImage/<%=data.scanlator %>/<%=encodeURIComponent(data.mangaLink)%>/<%=data.title%>/<%=data.chapterNum%>""
hx-trigger="load"
hx-indicator=".progress"
>
</div>
</div>
<script>
clearVariables();
handleInView();
function clearVariables()
checkPreview();
document.getElementById('lastImageRecordedAsRead').addEventListener('htmx:load', (event) =>
{
[ 'lastImage',
'observer',
'flexBox',
'chapterOverview',
'chevron',
'spacer',
'preview',
'elements',
'windowHeight',
'bounding',
'element',
'elementId'
].forEach((variable) =>
{
if (window.hasOwnProperty(variable))
{
delete window[variable];
}
});
}
let link = event.target.outerHTML.split('"')[1];
let aux = link.split('.')[link.split('.').length-2].split('/');
let id = '_'+aux[aux.length-1];
const elements = document.getElementsByClassName(id);
for(let i = 0; i<elements.length; i++)
{
elements[i].scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
handleInView();
function pullUp()
{
window.scrollTo({ top: 0, behavior: 'smooth' })
}
function _scrollTo(elementId)
function _scrollTo(elementId, where='start')
{
document.getElementById(elementId).scrollIntoView({
behavior: 'smooth',
block: 'start'
block: where
});
}
window.addEventListener('scroll', handleInView);
function handleInView()
{
var elements = document.querySelectorAll('#display img'); // Replace '.your-element-class' with the class of the elements you want to check
var elements = document.querySelectorAll('#display img');
var windowHeight = window.innerHeight;
elements.forEach((element)=>
for(let i = 0; i<elements.length; i++)
{
var bounding = element.getBoundingClientRect();
var bounding = elements[i].getBoundingClientRect();
if (bounding.top<windowHeight && (bounding.top + bounding.height) > windowHeight)
{
if(document.querySelector('.inView')) document.querySelector('.inView').classList.remove('inView');
document.getElementById(element.alt).classList.add('inView');
if(document.querySelector('.inView'))
{
if(document.querySelector('.inView').id != elements[i].id.split('_')[1])
{
fetch(window.location.origin+`/chapterRead/<%= data.scanlator %>/<%= encodeURIComponent(data.mangaLink)%>/<%= data.title %>/<%= data.chapterNum %>/${elements[i].classList[0].split('_')[1]}`, {method:'post', cache:"no-cache"})
.then(res=>console.log('Lastread Update'))
.catch(err=>console.log('Lastread Update Failed'));
}
document.querySelector('.inView').classList.remove('inView');
}
document.getElementById(elements[i].alt).classList.add('inView');
_scrollTo(elements[i].alt, 'end');
}
});
}
};
function toggle()
function _toggle()
{
let flexBox = document.getElementById('flexBox');
if( flexBox.style.display!='none') return closePreview();
@ -164,12 +170,5 @@
if(preview=='preview') return openPreview();
return closePreview();
}
try {
hasFinished();
checkPreview();
} catch (error) {
clearVariables();
hasFinished();
checkPreview();
}
</script>
Loading…
Cancel
Save