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 ## Roadmap
### TO-DO: ### TO-DO:
- If thread deleted, not show its messages in API. - If thread deleted, not show its messages in API.
- Thread.ejs fix with new theme
- Profile photos will store in database - Profile photos will store in database
- regex for pfp for now and - regex for pfp for now and
- admin perm for undelete, thread + message - admin perm for undelete, thread + message
- page support for threads - page support for threads
- message "<b>"
- author name of thread
- page for threads - users
- edit & delete button for thread
### Frontend ### Frontend
### User ### User
| To do | Is done? | Priority | | 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 | | Ratelimit | 🟢 | HIGH |
| Send | 🟢 | HIGH | | Send | 🟢 | HIGH |
| Delete | 🟢 | HIGH | | Delete | 🟢 | HIGH |
| Regex for scripts | 🔴 | HIGH | | Regex for scripts | 🟢 | HIGH |
| Undelete | 🟢 | MEDIUM | | Undelete | 🟢 | MEDIUM |
| React | 🟢 | MEDIUM | | React | 🟢 | MEDIUM |
| Edit | 🔴 | MEDIUM | | Edit | 🔴 | MEDIUM |

View file

@ -3,11 +3,12 @@
font-weight: 700; font-weight: 700;
font-size: 36px; font-size: 36px;
} }
.date { .date {
color: gray; color: gray;
} }
.thread{ .message {
max-width: 800px; max-width: 800px;
box-shadow: 0 0 5px 0 #c3c3c3; box-shadow: 0 0 5px 0 #c3c3c3;
margin: 10px auto; margin: 10px auto;
@ -17,19 +18,19 @@ font-size: 36px;
position: relative; position: relative;
} }
.thread .left{ .message .left {
text-align: center; text-align: center;
border-right: 2px solid #d9d9d9; border-right: 2px solid #d9d9d9;
} }
.thread .left img{ .message .left img {
width: 100px; width: 100px;
height: 100px; height: 100px;
border-radius: 50%; border-radius: 50%;
margin-right: 5px; margin-right: 5px;
} }
.thread .left .username{ .message .left .username {
color: #555; color: #555;
} }
@ -37,7 +38,7 @@ color: #555;
width: 70%; width: 70%;
} }
.likes{ .reactions {
position: absolute; position: absolute;
right: 20px; right: 20px;
bottom: 20px; bottom: 20px;
@ -45,7 +46,8 @@ color: #555;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
.likes div{
.reactions div {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
@ -54,12 +56,13 @@ color: #555;
cursor: pointer; cursor: pointer;
transition: color 0.3s ease; transition: color 0.3s ease;
} }
.likes div:hover{
.reactions div:hover {
color: #151515; color: #151515;
} }
.likes div i{ .reactions div i {
font-size: 22px; font-size: 22px;
} }
@ -89,6 +92,7 @@ color: #555;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
} }
.pagination .number { .pagination .number {
color: #747474; color: #747474;
font-size: 22px; font-size: 22px;
@ -99,6 +103,7 @@ color: #555;
cursor: pointer; cursor: pointer;
margin: 8px; margin: 8px;
} }
.pagination .number.active { .pagination .number.active {
color: #4d18e6; color: #4d18e6;
font-weight: 700; font-weight: 700;
@ -119,7 +124,6 @@ color: #747474;
top: 50px; top: 50px;
right: 0; right: 0;
background-color: #e6e6e6; background-color: #e6e6e6;
padding: ;
width: 100px; width: 100px;
text-align: center; text-align: center;
display: none; display: none;

View file

@ -45,7 +45,7 @@ window.scrollTo(0, document.body.scrollHeight);
/** /**
* Message Sender * Message Sender
*/ */
document.getElementById("send").addEventListener("submit", async e => { document.getElementById("send")?.addEventListener("submit", async e => {
e.preventDefault(); e.preventDefault();
const form = e.target; 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) => { app.get("/:id", async (req, res) => {
const { id } = req.params; const { id } = req.params;
const page = req.query.page || 0;
const thread = await ThreadModel.get(id); const thread = await ThreadModel.get(id).skip(page * 10).limit(page * 10 + 10);
thread.views++; thread.views++;
if (thread && (req.user?.admin || !thread.deleted)) { if (thread && (req.user?.admin || !thread.deleted)) {
const messages = await Promise.all(thread.messages.map(async id => { const messages = await Promise.all(thread.messages.map(async id => {
const message = await MessageModel.get(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; 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 } else
res.error(404, "We have not got this thread."); res.error(404, "We have not got this thread.");
thread.save(); 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,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<%- include("extra/meta", {title: "Thread list!" }) %> <%- include("extra/meta", {title: thread.title }) %>
<body> <body>
@ -11,94 +11,95 @@
<link rel="stylesheet" href="/css/thread.css" /> <link rel="stylesheet" href="/css/thread.css" />
<% if (user){ %> <% if (user){ %>
<script type="module" src="/js/thread.js"></script> <script type="module" src="/js/thread.js"></script>
<% }%> <% }; %>
<div style="text-align:center;padding:8px"> <div style="text-align:center;padding:8px">
<div class="title">İlk Forum</div> <div class="title"><%= thread.title %></div>
<div class="date"> <div class="date">
20/04/2000 <%= new Date(thread.time).toLocaleString() %> • Views: <%= thread.views %>
</div> </div>
</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"> <div class="left">
<img src="https://renk.gen.tr/images/acik-mavi-renk.jpg"/> <img src="<%= message.author.avatar || '/images/guest.png' %>"/>
<div class="username">Tokmak</div> <div class="username"><a href="/users/<%=message.author.id %>"><%=message.author.name %></a></div>
<div class="date"> <div class="date">
20/04/2000 <%= new Date(message.time).toLocaleDateString() %>
</div>
<div class="date">
<%= new Date(message.time).toLocaleTimeString() %>
</div> </div>
</div> </div>
<div class="content"> <div class="content"><%- message.content %></div>
Merhaba ! Bu benim ilk yazım Merhaba ! Bu benim ilk yazım <% if(user){ %>
Merhaba ! Bu benim ilk yazım <% if(user.id === message.author.id || user.admin){ %>
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('<%=message.id %>')">
<div class="dots" onclick="dots()">
<i class='bx bx-dots-horizontal-rounded' ></i> <i class='bx bx-dots-horizontal-rounded' ></i>
</div> </div>
<div class="dots-menu">
<a>Delete</a>
<a>Edit</a>
<% if (!message.deleted){ %>
<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> </div>
<% }else if (user.admin){ %>
<div class="dots-menu" id="dot-<%=message.id %>">
<a onclick="undelete_message('<%=message.id %>');">UNDELETE</a>
</div>
<% } %>
<% } %>
<div class="likes"> <div class="reactions">
<div> <div>
<i class='bx bx-like'></i> 5 <i class='bx bx-like'></i> <%=message.react.like.length %>
</div> </div>
<div> <div>
<i class='bx bx-dislike'></i> 30 <i class='bx bx-dislike'></i> <%=message.react.dislike.length %>
</div> </div>
</div> </div>
<% }; %>
</div> </div>
<% }); %>
</div>
<div class="pagination"> <div class="pagination">
<div class="back"> <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>
<div class="numbers"> <div class="numbers">
<a class="number active" href="/deneme">1</a> <% for(let i=0;i< Math.ceil(messages.length/10);i++){ %>
<a class="number" href="/deneme">2</a> <a class="number <%= i==page?'active':'' %>" href="<%= thread.getLink() %>?page=<%= i %>"><%= i %></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>
</div> </div>
<div class="after"> <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>
</div> </div>
<!-- ==== Dots Event ==== -->
<script>
function dots(){
var box= document.getElementsByClassName("dots-menu")[0];
box.classList.toggle("active")
}
</script>
<script> <script>
document.getElementById("message-<%= scroll %>").scrollIntoView(); document.getElementById("message-<%= scroll %>").scrollIntoView();
function dots(id) {
document.getElementById('dot-'+id).classList.toggle('active')
}
</script> </script>
<!-- BURAYA Bİ İLERİ BİR GERİ SAYFA BUTONU GELMEZ Mİ BE-->
</body> </body>
</html> </html>