mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-26 05:10:41 +03:00
thread converted to new theme, page support
This commit is contained in:
parent
c8474694da
commit
9ad7c03162
6 changed files with 274 additions and 167 deletions
10
README.md
10
README.md
|
@ -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 |
|
||||||
|
|
|
@ -1,136 +1,140 @@
|
||||||
.title {
|
.title {
|
||||||
color: #414141;
|
color: #414141;
|
||||||
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;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display:flex;
|
display: flex;
|
||||||
gap:10px;
|
gap: 10px;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content{
|
.content {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.likes{
|
.reactions {
|
||||||
position:absolute;
|
position: absolute;
|
||||||
right:20px;
|
right: 20px;
|
||||||
bottom:20px;
|
bottom: 20px;
|
||||||
display:flex;
|
display: flex;
|
||||||
align-items:center;
|
align-items: center;
|
||||||
gap:10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.likes div{
|
|
||||||
display:flex;
|
.reactions div {
|
||||||
align-items:center;
|
display: flex;
|
||||||
gap:5px;
|
align-items: center;
|
||||||
padding:4px;
|
gap: 5px;
|
||||||
|
padding: 4px;
|
||||||
color: #747474;
|
color: #747474;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
transition:color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
.likes div:hover{
|
|
||||||
color: #151515;
|
.reactions div:hover {
|
||||||
|
color: #151515;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.likes div i{
|
.reactions div i {
|
||||||
font-size:22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.pagination{
|
.pagination {
|
||||||
box-shadow: 0 0 5px 0 #c3c3c3;
|
box-shadow: 0 0 5px 0 #c3c3c3;
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
display:flex;
|
display: flex;
|
||||||
justify-content:space-between;
|
justify-content: space-between;
|
||||||
align-items:center;
|
align-items: center;
|
||||||
max-width:400px;
|
max-width: 400px;
|
||||||
gap:10px;
|
gap: 10px;
|
||||||
position:relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination .back ,
|
.pagination .back,
|
||||||
.pagination .after {
|
.pagination .after {
|
||||||
color:#747474;
|
color: #747474;
|
||||||
font-size:26px;
|
font-size: 26px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.pagination .numbers{
|
.pagination .numbers {
|
||||||
display:flex;
|
display: flex;
|
||||||
align-items:center;
|
align-items: center;
|
||||||
gap:5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination .number {
|
.pagination .number {
|
||||||
color: #747474;
|
color: #747474;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
border: 0 0 5px #747474;
|
border: 0 0 5px #747474;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
.pagination .number.active{
|
|
||||||
color: #4d18e6;
|
.pagination .number.active {
|
||||||
font-weight: 700;
|
color: #4d18e6;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.dots{
|
.dots {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
color: #747474;
|
color: #747474;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dots-menu{
|
.dots-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dots-menu.active{
|
.dots-menu.active {
|
||||||
display:block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dots-menu a{
|
.dots-menu a {
|
||||||
display:block;
|
display: block;
|
||||||
margin:8px;
|
margin: 8px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\n", "<br>")
|
message.content = message.content.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<").replaceAll(">", ">")
|
||||||
|
.replaceAll("\"", """).replaceAll("'", "'")
|
||||||
|
.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
97
views/extra/ot.ejs
Normal 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>
|
149
views/thread.ejs
149
views/thread.ejs
|
@ -1,104 +1,105 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<%- include("extra/meta", {title: "Thread list!" }) %>
|
<%- include("extra/meta", {title: thread.title }) %>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<%- include("extra/navbar") %>
|
<%- 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" />
|
<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 class="title"><%= thread.title %></div>
|
||||||
|
<div class="date">
|
||||||
|
<%= new Date(thread.time).toLocaleString() %> • Views: <%= thread.views %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="messages" value="<%= thread.id %>">
|
||||||
|
|
||||||
<div style="text-align:center;padding:8px">
|
<% messages.filter(Boolean).forEach(message=>{ %>
|
||||||
<div class="title">İlk Forum</div>
|
|
||||||
<div class="date">
|
<div class="message" id="message-<%= message.id %>">
|
||||||
20/04/2000
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="thread">
|
|
||||||
<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
|
<div class="dots" onclick="dots('<%=message.id %>')">
|
||||||
Merhaba ! Bu benim ilk yazım
|
<i class='bx bx-dots-horizontal-rounded' ></i>
|
||||||
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>
|
</div>
|
||||||
|
|
||||||
|
<% if (!message.deleted){ %>
|
||||||
<div class="likes">
|
|
||||||
<div>
|
<div class="dots-menu" id="dot-<%=message.id %>">
|
||||||
<i class='bx bx-like'></i> 5
|
<a onclick="delete_message('<%=message.id %>');">Delete</a>
|
||||||
</div>
|
<a onclick="edit_message('<%=message.id %>');">Edit</a>
|
||||||
<div>
|
|
||||||
<i class='bx bx-dislike'></i> 30
|
|
||||||
</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="reactions">
|
||||||
|
<div>
|
||||||
|
<i class='bx bx-like'></i> <%=message.react.like.length %>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<i class='bx bx-dislike'></i> <%=message.react.dislike.length %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% }; %>
|
||||||
|
|
||||||
<div class="pagination">
|
|
||||||
<div class="back">
|
|
||||||
<i class='bx bxs-chevron-left'></i>
|
|
||||||
</div>
|
</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>
|
|
||||||
|
|
||||||
|
<% }); %>
|
||||||
</div>
|
</div>
|
||||||
<div class="after">
|
<div class="pagination">
|
||||||
<i class='bx bxs-chevron-right'></i>
|
<div class="back">
|
||||||
</div>
|
<% if (page > 0){ %>
|
||||||
|
<a href="<%= thread.getLink() %>?page=<%= page-1 %>" class='bx bxs-chevron-left'></a>
|
||||||
</div>
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="numbers">
|
||||||
|
<% 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>
|
||||||
|
|
||||||
<!-- ==== Dots Event ==== -->
|
<div class="after">
|
||||||
|
<% if (Math.ceil(messages.length/10)-1 > page){ %>
|
||||||
|
<a href="<%= thread.getLink() %>?page=<%= page +1 %>" class='bx bxs-chevron-right'></a>
|
||||||
<script>
|
<% } %>
|
||||||
function dots(){
|
</div>
|
||||||
var box= document.getElementsByClassName("dots-menu")[0];
|
|
||||||
box.classList.toggle("active")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
</div>
|
||||||
document.getElementById("message-<%= scroll %>").scrollIntoView();
|
|
||||||
</script>
|
<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>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue