thread converted to new theme, page support

This commit is contained in:
Akif9748 2022-08-29 00:58:34 +03:00
parent c8474694da
commit 9ad7c03162
6 changed files with 274 additions and 167 deletions

View file

@ -30,12 +30,14 @@ Akf-forum has got an API for AJAX, other clients etc. And, you can learn about A
## Roadmap
### TO-DO:
- If thread deleted, not show its messages in API.
- Thread.ejs fix with new theme
- Profile photos will store in database
- regex for pfp for now and
- admin perm for undelete, thread+message
- admin perm for undelete, thread + message
- page support for threads
- message "<b>"
- author name of thread
- page for threads - users
- edit & delete button for thread
### Frontend
### User
| To do | Is done? | Priority |
@ -58,7 +60,7 @@ Akf-forum has got an API for AJAX, other clients etc. And, you can learn about A
| Ratelimit | 🟢 | HIGH |
| Send | 🟢 | HIGH |
| Delete | 🟢 | HIGH |
| Regex for scripts | 🔴 | HIGH |
| Regex for scripts | 🟢 | HIGH |
| Undelete | 🟢 | MEDIUM |
| React | 🟢 | MEDIUM |
| Edit | 🔴 | MEDIUM |

View file

@ -1,94 +1,98 @@
.title {
color: #414141;
font-weight: 700;
font-size: 36px;
font-weight: 700;
font-size: 36px;
}
.date{
.date {
color: gray;
}
.thread{
.message {
max-width: 800px;
box-shadow: 0 0 5px 0 #c3c3c3;
margin: 10px auto;
padding: 20px;
display:flex;
gap:10px;
position:relative;
display: flex;
gap: 10px;
position: relative;
}
.thread .left{
.message .left {
text-align: center;
border-right: 2px solid #d9d9d9;
border-right: 2px solid #d9d9d9;
}
.thread .left img{
.message .left img {
width: 100px;
height: 100px;
border-radius: 50%;
margin-right: 5px;
height: 100px;
border-radius: 50%;
margin-right: 5px;
}
.thread .left .username{
color: #555;
.message .left .username {
color: #555;
}
.content{
.content {
width: 70%;
}
.likes{
position:absolute;
right:20px;
bottom:20px;
display:flex;
align-items:center;
gap:10px;
.reactions {
position: absolute;
right: 20px;
bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.likes div{
display:flex;
align-items:center;
gap:5px;
padding:4px;
.reactions div {
display: flex;
align-items: center;
gap: 5px;
padding: 4px;
color: #747474;
cursor:pointer;
transition:color 0.3s ease;
cursor: pointer;
transition: color 0.3s ease;
}
.likes div:hover{
.reactions div:hover {
color: #151515;
}
.likes div i{
font-size:22px;
.reactions div i {
font-size: 22px;
}
.pagination{
.pagination {
box-shadow: 0 0 5px 0 #c3c3c3;
margin: 10px auto;
padding: 8px;
display:flex;
justify-content:space-between;
align-items:center;
max-width:400px;
gap:10px;
position:relative;
display: flex;
justify-content: space-between;
align-items: center;
max-width: 400px;
gap: 10px;
position: relative;
}
.pagination .back ,
.pagination .back,
.pagination .after {
color:#747474;
font-size:26px;
cursor:pointer;
color: #747474;
font-size: 26px;
cursor: pointer;
}
.pagination .numbers{
display:flex;
align-items:center;
gap:5px;
.pagination .numbers {
display: flex;
align-items: center;
gap: 5px;
}
.pagination .number {
color: #747474;
font-size: 22px;
@ -99,38 +103,38 @@ color: #555;
cursor: pointer;
margin: 8px;
}
.pagination .number.active{
color: #4d18e6;
font-weight: 700;
.pagination .number.active {
color: #4d18e6;
font-weight: 700;
}
.dots{
position: absolute;
right: 20px;
font-size: 22px;
top: 10px;
color: #747474;
cursor:pointer;
}
.dots-menu{
.dots {
position: absolute;
top: 50px;
right: 0;
background-color: #e6e6e6;
padding: ;
width: 100px;
text-align: center;
display:none;
right: 20px;
font-size: 22px;
top: 10px;
color: #747474;
cursor: pointer;
}
.dots-menu.active{
display:block;
.dots-menu {
position: absolute;
top: 50px;
right: 0;
background-color: #e6e6e6;
width: 100px;
text-align: center;
display: none;
}
.dots-menu a{
display:block;
margin:8px;
cursor:pointer;
.dots-menu.active {
display: block;
}
.dots-menu a {
display: block;
margin: 8px;
cursor: pointer;
}

View file

@ -45,7 +45,7 @@ window.scrollTo(0, document.body.scrollHeight);
/**
* Message Sender
*/
document.getElementById("send").addEventListener("submit", async e => {
document.getElementById("send")?.addEventListener("submit", async e => {
e.preventDefault();
const form = e.target;

View file

@ -17,18 +17,21 @@ app.get("/create*", (req, res) => res.reply("create_thread"));
app.get("/:id", async (req, res) => {
const { id } = req.params;
const thread = await ThreadModel.get(id);
const page = req.query.page || 0;
const thread = await ThreadModel.get(id).skip(page * 10).limit(page * 10 + 10);
thread.views++;
if (thread && (req.user?.admin || !thread.deleted)) {
const messages = await Promise.all(thread.messages.map(async id => {
const message = await MessageModel.get(id)
message.content = message.content.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").replaceAll("\n", "<br>")
message.content = message.content.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;").replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;").replaceAll("'", "&#39;")
.replaceAll("\n", "<br>");
return req.user?.admin || !message?.deleted ? message.toObject({ virtuals: true }) : null;
}));
res.reply("thread", { thread, messages, scroll: req.query.scroll || false });
res.reply("thread", { page,thread, messages, scroll: req.query.scroll || thread.messages[0]});
} else
res.error(404, "We have not got this thread.");
thread.save();

97
views/extra/ot.ejs Normal file
View file

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="en">
<%- include("extra/meta", {title: "Thread list!" }) %>
<body style="text-align: center;">
<%- include("extra/navbar") %>
<link rel="stylesheet" href="/css/thread.css" />
<% if (user){ %>
<script type="module" src="/js/thread.js"></script>
<% }%>
<h1 style="font-size: 35px;color: #4d18e6;" ><%= thread.title %></h1>
<h3 >View count: <%= thread.views %></h1>
<h2 style="display:inline;">By <a href="<%='/users/' + thread.author.id %>"> <%= thread.author.name %></a>
<img class="circle" src="<%=thread.author.avatar %>">
</h2>
<% if (user && !thread.deleted){ %>
<a onclick="delete_thread('<%= thread.id %>' )" value=style="display:inline;" >DELETE</a>
<a onclick="edit_thread('<%= thread.id %>')" style="display:inline;" >EDIT</a>
<% } else if (thread.deleted) { %>
<h3 style="display:inline;">This thread has been deleted</h3>
<a onclick="undelete_thread('<%= thread.id %>')" style="display:inline;" >UNDELETE</a>
<% }; %>
<hr>
<div id="messages" value="<%= thread.id %>">
<% messages.filter(Boolean).forEach(message=>{ %>
<div class="message" id="message-<%= message.id %>">
<h3 style="float:right;"><%= new Date(message.time).toLocaleString() %> </h3>
<h2>
<img class="circle" src="<%= message.author.avatar %>">
<a href="/users/<%=message.author.id %>"><%=message.author.name %></a>:
</h2>
<p><%= message.content%></p><br>
<div id="message-delete-<%=message.id %>">
<% if (!message.deleted){ %>
<a onclick="delete_message('<%=message.id %>');">DELETE</a>
<a onclick="edit_message('<%=message.id %>');">EDIT</a>
<% }else{ %>
<h3 style="display:inline;">This message has been deleted</h3>
<a onclick="undelete_message('<%=message.id %>');">UNDELETE</a>
<% } %>
</div>
<div style="float: right;">
<h3 id="count<%=message.id %>" style="display:inline;"><%=message.reactCount %></h3>
<a onclick="react('<%=message.id %>', 'like');">+🔼</a>
<a onclick="react('<%=message.id %>', 'dislike');">-🔽</a>
</div>
</div>
<% }); %>
</div>
<hr>
<form id="send">
<textarea rows="4" cols="133" name="content"></textarea>
<input name="threadID" type="hidden" value="<%= thread.id %>"></input>
<br>
<% if (user){ %>
<button type="submit">Send!</button>
<%} else {%>
<button disabled>Login for send</button>
<% }%>
</form>
<script>
document.getElementById("message-<%= scroll %>").scrollIntoView();
</script>
<!-- BURAYA Bİ İLERİ BİR GERİ SAYFA BUTONU GELMEZ Mİ BE-->
</body>
</html>

View file

@ -1,104 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<%- include("extra/meta", {title: "Thread list!" }) %>
<%- include("extra/meta", {title: thread.title }) %>
<body>
<%- include("extra/navbar") %>
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
<link rel="stylesheet" href="/css/thread.css" />
<% if (user){ %>
<script type="module" src="/js/thread.js"></script>
<% }%>
<% }; %>
<div style="text-align:center;padding:8px">
<div class="title">İlk Forum</div>
<div class="title"><%= thread.title %></div>
<div class="date">
20/04/2000
<%= new Date(thread.time).toLocaleString() %> • Views: <%= thread.views %>
</div>
</div>
<div class="thread">
<div id="messages" value="<%= thread.id %>">
<% messages.filter(Boolean).forEach(message=>{ %>
<div class="message" id="message-<%= message.id %>">
<div class="left">
<img src="https://renk.gen.tr/images/acik-mavi-renk.jpg"/>
<div class="username">Tokmak</div>
<img src="<%= message.author.avatar || '/images/guest.png' %>"/>
<div class="username"><a href="/users/<%=message.author.id %>"><%=message.author.name %></a></div>
<div class="date">
20/04/2000
<%= new Date(message.time).toLocaleDateString() %>
</div>
<div class="date">
<%= new Date(message.time).toLocaleTimeString() %>
</div>
</div>
<div class="content">
Merhaba ! Bu benim ilk yazım Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
Merhaba ! Bu benim ilk yazım
</div>
<div class="dots" onclick="dots()">
<i class='bx bx-dots-horizontal-rounded' ></i>
</div>
<div class="dots-menu">
<a>Delete</a>
<a>Edit</a>
<div class="content"><%- message.content %></div>
<% if(user){ %>
<% if(user.id === message.author.id || user.admin){ %>
<div class="dots" onclick="dots('<%=message.id %>')">
<i class='bx bx-dots-horizontal-rounded' ></i>
</div>
<% if (!message.deleted){ %>
<div class="likes">
<div class="dots-menu" id="dot-<%=message.id %>">
<a onclick="delete_message('<%=message.id %>');">Delete</a>
<a onclick="edit_message('<%=message.id %>');">Edit</a>
</div>
<% }else if (user.admin){ %>
<div class="dots-menu" id="dot-<%=message.id %>">
<a onclick="undelete_message('<%=message.id %>');">UNDELETE</a>
</div>
<% } %>
<% } %>
<div class="reactions">
<div>
<i class='bx bx-like'></i> 5
<i class='bx bx-like'></i> <%=message.react.like.length %>
</div>
<div>
<i class='bx bx-dislike'></i> 30
<i class='bx bx-dislike'></i> <%=message.react.dislike.length %>
</div>
</div>
<% }; %>
</div>
<div class="pagination">
<% }); %>
</div>
<div class="pagination">
<div class="back">
<i class='bx bxs-chevron-left'></i>
<% if (page > 0){ %>
<a href="<%= thread.getLink() %>?page=<%= page-1 %>" class='bx bxs-chevron-left'></a>
<% } %>
</div>
<div class="numbers">
<a class="number active" href="/deneme">1</a>
<a class="number" href="/deneme">2</a>
<a class="number" href="/deneme">3</a>
<a class="number" href="/deneme">4</a>
<a class="number" href="/deneme">5</a>
<a class="number" href="/deneme">6</a>
<% for(let i=0;i< Math.ceil(messages.length/10);i++){ %>
<a class="number <%= i==page?'active':'' %>" href="<%= thread.getLink() %>?page=<%= i %>"><%= i %></a>
<% } %>
</div>
<div class="after">
<i class='bx bxs-chevron-right'></i>
<% if (Math.ceil(messages.length/10)-1 > page){ %>
<a href="<%= thread.getLink() %>?page=<%= page +1 %>" class='bx bxs-chevron-right'></a>
<% } %>
</div>
</div>
<!-- ==== Dots Event ==== -->
<script>
function dots(){
var box= document.getElementsByClassName("dots-menu")[0];
box.classList.toggle("active")
}
</script>
</div>
<script>
document.getElementById("message-<%= scroll %>").scrollIntoView();
function dots(id) {
document.getElementById('dot-'+id).classList.toggle('active')
}
</script>
<!-- BURAYA Bİ İLERİ BİR GERİ SAYFA BUTONU GELMEZ Mİ BE-->
</body>
</html>