chat overhaul
parent
111f664440
commit
f2fdefa8cd
|
@ -17,6 +17,7 @@ COPY requirements.txt /etc/requirements.txt
|
|||
RUN pip3 install -r /etc/requirements.txt
|
||||
|
||||
RUN mkdir /images
|
||||
RUN mkdir /chat_images
|
||||
RUN mkdir /songs
|
||||
RUN mkdir /videos
|
||||
RUN mkdir /audio
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
#chat-window {
|
||||
max-height: calc(100vh - 220px);
|
||||
overflow-y: auto;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
#online {
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
background-color: var(--background) !important;
|
||||
}
|
||||
|
||||
#chat-window .chat-profile {
|
||||
min-width: 42px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
#chat-window::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-window {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.chat-mention {
|
||||
background-color: #{{v.themecolor}}55;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5 {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
blockquote + :not(blockquote) {
|
||||
display: inline-block;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.chat-group {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#shrink * {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
.chat-group {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
#chat-window {
|
||||
max-height: 62vh;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chat-line .btn {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.chat-line-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cdiv {
|
||||
overflow: hidden;
|
||||
margin-left: 27px;
|
||||
}
|
||||
|
||||
.quote, .del {
|
||||
padding: 0 0.5rem !important;
|
||||
border: none !important;
|
||||
float: right;
|
||||
color: var(--black);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
lite-youtube {
|
||||
min-width: min(80vw,500px);
|
||||
}
|
||||
|
||||
.chat-group:nth-child(even) {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
#cancel {
|
||||
font-size: 10px;
|
||||
padding: 0 0 0.2rem 0.5rem;
|
||||
}
|
||||
|
||||
.btn:focus, .btn.focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 10px;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.chat-line:target {
|
||||
background: #ffffff44 !important;
|
||||
}
|
||||
|
||||
.QuotedMessage * {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.quotes {
|
||||
margin-top: 0.5rem;
|
||||
}
|
|
@ -1,329 +0,0 @@
|
|||
/* src/App.css */
|
||||
html,
|
||||
body {
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
html {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
body {
|
||||
min-height: 100vh;
|
||||
min-height: calc(var(--vh, 1vh) * 100);
|
||||
overflow: hidden;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
.App {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.App-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin: 0 2rem;
|
||||
}
|
||||
.App-heading {
|
||||
flex-basis: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.App-heading small {
|
||||
opacity: 0.2;
|
||||
font-size: 10px;
|
||||
}
|
||||
.App-side {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
background: var(--gray-500);
|
||||
position: relative;
|
||||
}
|
||||
.App-content {
|
||||
position: relative;
|
||||
flex: 3;
|
||||
height: 62vh;
|
||||
height: calc(var(--vh, 1vh) * 68);
|
||||
max-height: 1000px;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.App-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.App-drawer {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
background: rgb(var(--background));
|
||||
height: 100%;
|
||||
}
|
||||
.App-center {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.App-bottom-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.App-bottom {
|
||||
flex: 3;
|
||||
}
|
||||
.App-bottom-dummy {
|
||||
flex: 1;
|
||||
}
|
||||
.App-bottom-extra {
|
||||
padding: .25rem;
|
||||
}
|
||||
@media screen and (max-width: 1100px) {
|
||||
.App-wrapper {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.App-heading {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.App-side {
|
||||
display: none;
|
||||
}
|
||||
.App-bottom-dummy {
|
||||
display: none;
|
||||
}
|
||||
.App-bottom-wrapper {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
lite-youtube {
|
||||
min-width: min(80vw, 500px);
|
||||
}
|
||||
.btn-secondary {
|
||||
border: none !important;
|
||||
}
|
||||
.btn-secondary:focus {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* src/features/chat/UserList.css */
|
||||
.UserList {
|
||||
padding: 1rem;
|
||||
flex: 1;
|
||||
}
|
||||
.UserList-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.UserList-heading h5 {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
.UserList ul {
|
||||
max-height: calc(var(--vh, 1vh) * 50);
|
||||
overflow: auto;
|
||||
}
|
||||
.UserList ul::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* src/features/chat/ChatHeading.css */
|
||||
.ChatHeading {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ChatHeading i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* src/features/chat/Username.css */
|
||||
.Username {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.Username > a {
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* src/features/chat/QuotedMessageLink.css */
|
||||
.QuotedMessageLink {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* src/features/chat/ChatMessage.css */
|
||||
.ChatMessage {
|
||||
position: relative;
|
||||
padding-right: 1.5rem;
|
||||
max-height: 300px;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.ChatMessage__isDm {
|
||||
background: var(--gray-800);
|
||||
border-top: 1px dashed var(--primary);
|
||||
border-bottom: 1px dashed var(--primary);
|
||||
}
|
||||
.ChatMessage__isOptimistic {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.ChatMessage p {
|
||||
margin: 0;
|
||||
}
|
||||
.ChatMessage .btn {
|
||||
border: none !important;
|
||||
}
|
||||
.ChatMessage-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.ChatMessage-timestamp {
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
}
|
||||
.ChatMessage-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ChatMessage-content {
|
||||
margin-right: 0.5rem;
|
||||
word-wrap: break-word;
|
||||
display: inline-block;
|
||||
}
|
||||
.ChatMessage-button {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.ChatMessage-button i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.ChatMessage-button__confirmed {
|
||||
color: red !important;
|
||||
}
|
||||
.ChatMessage-quoted-link {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.ChatMessage-actions-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
z-index: 5;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.ChatMessage-actions-button button {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.ChatMessage-actions-button button i {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.ChatMessage-actions {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(20, 20, 20, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 1rem;
|
||||
padding-right: 3rem;
|
||||
animation: fading-in 0.3s ease-in-out forwards;
|
||||
}
|
||||
.ChatMessage-actions button {
|
||||
font-size: 10px;
|
||||
background: none !important;
|
||||
}
|
||||
.ChatMessageList {
|
||||
flex: 1;
|
||||
}
|
||||
.ChatMessageList-group {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.3rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.ChatMessageList-group:nth-child(even) {
|
||||
background: rgba(255, 255, 255, 0.025);
|
||||
}
|
||||
|
||||
/* src/drawers/BaseDrawer.css */
|
||||
.BaseDrawer {
|
||||
flex: 1;
|
||||
padding-right: 2rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* src/features/emoji/EmojiGenres.css */
|
||||
|
||||
/* src/features/emoji/EmojiDrawer.css */
|
||||
.EmojiDrawer-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* src/features/chat/UserInput.css */
|
||||
.UserInput {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.UserInput-input {
|
||||
flex: 1;
|
||||
margin-right: 2rem;
|
||||
min-height: 50px;
|
||||
height: 50px;
|
||||
max-height: 50px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.UserInput-input {
|
||||
min-height: 70px !important;
|
||||
height: 70px !important;
|
||||
max-height: 70px !important;
|
||||
}
|
||||
}
|
||||
.UserInput-emoji {
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
transform: rotateY(180deg);
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* src/features/chat/UsersTyping.css */
|
||||
.UsersTyping {
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* src/features/chat/QuotedMessage.css */
|
||||
.QuotedMessage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.QuotedMessage-content {
|
||||
margin-left: 1rem;
|
||||
flex: 1;
|
||||
max-width: 420px;
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 1rem;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
|
@ -1 +1,299 @@
|
|||
window.global = window
|
||||
function timeSince(timeStamp) {
|
||||
var now = new Date(),
|
||||
secondsPast = (now.getTime() - timeStamp) / 1000;
|
||||
if (secondsPast < 60) {
|
||||
return parseInt(secondsPast) + 's';
|
||||
}
|
||||
if (secondsPast < 3600) {
|
||||
return parseInt(secondsPast / 60) + 'm';
|
||||
}
|
||||
if (secondsPast <= 86400) {
|
||||
return parseInt(secondsPast / 3600) + 'h';
|
||||
}
|
||||
if (secondsPast > 86400) {
|
||||
day = timeStamp.getDate();
|
||||
month = timeStamp.toDateString().match(/ [a-zA-Z]*/)[0].replace(" ", "");
|
||||
year = timeStamp.getFullYear() == now.getFullYear() ? "" : " " + timeStamp.getFullYear();
|
||||
return day + " " + month + year;
|
||||
}
|
||||
}
|
||||
|
||||
const ua=window.navigator.userAgent
|
||||
let socket
|
||||
|
||||
socket=io()
|
||||
|
||||
const chatline = document.getElementsByClassName('chat-line')[0]
|
||||
const box = document.getElementById('chat-window')
|
||||
const textbox = document.getElementById('input-text')
|
||||
const icon = document.getElementById('icon')
|
||||
const vid = document.getElementById('vid').value
|
||||
const vusername = document.getElementById('vusername').value
|
||||
const site_name = document.getElementById('site_name').value
|
||||
const slurreplacer = document.getElementById('slurreplacer').value
|
||||
|
||||
let notifs = 0;
|
||||
let focused = true;
|
||||
let is_typing = false;
|
||||
let alert=true;
|
||||
|
||||
function flash(){
|
||||
let title = document.getElementsByTagName('title')[0]
|
||||
if (notifs >= 1 && !focused){
|
||||
title.innerHTML = `[+${notifs}] Chat`;
|
||||
if (alert) {
|
||||
icon.href = escapeHTML(`/assets/images/${site_name}/alert.webp?v=3`)
|
||||
alert=false;
|
||||
}
|
||||
else {
|
||||
icon.href = escapeHTML(`/assets/images/${site_name}/icon.webp?v=3009`)
|
||||
alert=true;
|
||||
}
|
||||
setTimeout(flash, 500)
|
||||
}
|
||||
else {
|
||||
icon.href = escapeHTML(`/assets/images/${site_name}/icon.webp?v=3009`)
|
||||
notifs = 0
|
||||
title.innerHTML = 'Chat';
|
||||
}
|
||||
|
||||
if (is_typing) {
|
||||
is_typing = false
|
||||
socket.emit('typing', false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handle_message(json) {
|
||||
let text = json['text']
|
||||
let text_html
|
||||
|
||||
if (slurreplacer == 'True') text_html = json['text_censored']
|
||||
else text_html = json['text_html']
|
||||
|
||||
if (text_html.includes(`<a href="/id/${vid}">`)){
|
||||
chatline.classList.add('chat-mention');
|
||||
}
|
||||
else {
|
||||
chatline.classList.remove('chat-mention');
|
||||
};
|
||||
|
||||
notifs = notifs + 1;
|
||||
if (notifs == 1) {
|
||||
setTimeout(flash, 500);
|
||||
}
|
||||
|
||||
const users = document.getElementsByClassName('userlink');
|
||||
const last_user = users[users.length-1].innerHTML;
|
||||
const scrolled_down = (box.scrollHeight - box.scrollTop <= window.innerHeight)
|
||||
|
||||
if (last_user == json['username']) {
|
||||
document.getElementsByClassName('userlink')[0].classList.add('d-none')
|
||||
document.getElementsByClassName('avatar')[0].classList.add('d-none')
|
||||
document.getElementsByClassName('time')[0].classList.add('d-none')
|
||||
}
|
||||
else {
|
||||
document.getElementsByClassName('userlink')[0].classList.remove('d-none')
|
||||
document.getElementsByClassName('avatar')[0].classList.remove('d-none')
|
||||
document.getElementsByClassName('avatar-pic')[0].src = '/pp/' + json["user_id"]
|
||||
if (json['hat'])
|
||||
document.getElementsByClassName('avatar-hat')[0].src = json['hat'] + "?h=7"
|
||||
else
|
||||
document.getElementsByClassName('avatar-hat')[0].removeAttribute("src")
|
||||
|
||||
document.getElementsByClassName('userlink')[0].href = '/@' + json['username']
|
||||
document.getElementsByClassName('userlink')[0].style.color = '#' + json['namecolor']
|
||||
document.getElementsByClassName('time')[0].classList.remove('d-none')
|
||||
document.getElementsByClassName('time')[0].innerHTML = timeSince(json['time']*1000) + ' ago'
|
||||
}
|
||||
|
||||
document.getElementsByClassName('chat-line')[0].id = json['id']
|
||||
document.getElementsByClassName('userlink')[0].innerHTML = json['username']
|
||||
document.getElementsByClassName('text')[0].innerHTML = escapeHTML(text)
|
||||
document.getElementsByClassName('chat-message')[0].innerHTML = text_html.replace(/data-src/g, 'src').replace(/data-cfsrc/g, 'src').replace(/style="display:none;visibility:hidden;"/g, '')
|
||||
|
||||
if (json['quotes']) {
|
||||
document.getElementsByClassName('quotes')[0].classList.remove("d-none")
|
||||
const quoted = document.getElementById(json['quotes'])
|
||||
document.getElementsByClassName('QuotedMessageLink')[0].href = '#' + json['quotes']
|
||||
document.getElementsByClassName('QuotedUser')[0].innerHTML = quoted.querySelector('.userlink').innerHTML
|
||||
document.getElementsByClassName('QuotedMessage')[0].innerHTML = quoted.querySelector('.chat-message').innerHTML
|
||||
}
|
||||
else {
|
||||
document.getElementsByClassName('quotes')[0].classList.add("d-none")
|
||||
}
|
||||
|
||||
let line = document.getElementsByClassName('chat-line')[0].cloneNode(true)
|
||||
register_new_elements(line);
|
||||
bs_trigger(line)
|
||||
if (last_user == json['username']) {
|
||||
box.querySelector('.chat-group:last-child').append(line)
|
||||
}
|
||||
else {
|
||||
const chatgroup = document.createElement("div");
|
||||
chatgroup.className = "chat-group";
|
||||
chatgroup.append(line)
|
||||
box.append(chatgroup)
|
||||
}
|
||||
|
||||
if (scrolled_down || json['username'] == vusername)
|
||||
box.scrollTo(0, box.scrollHeight)
|
||||
}
|
||||
|
||||
socket.on('speak', function(json) {
|
||||
handle_message(json)
|
||||
})
|
||||
|
||||
socket.on('catchup', function(json) {
|
||||
for (const message of json) {
|
||||
handle_message(message)
|
||||
}
|
||||
})
|
||||
|
||||
function send() {
|
||||
const text = textbox.value.trim()
|
||||
const files = document.getElementById('file').files
|
||||
if (text || files)
|
||||
{
|
||||
let sending;
|
||||
if (files[0]) sending = files[0]
|
||||
else sending = ''
|
||||
socket.emit('speak', {
|
||||
"message": text,
|
||||
"quotes": document.getElementById('quotes_id').value,
|
||||
"file": sending,
|
||||
});
|
||||
textbox.value = ''
|
||||
is_typing = false
|
||||
socket.emit('typing', false);
|
||||
autoExpand(textbox);
|
||||
document.getElementById("quotes").classList.add("d-none")
|
||||
document.getElementById("filename").innerHTML = '<i class="fas fa-image" style="font-size:1.3rem!important"></i>'
|
||||
scroll_chat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function quote(t) {
|
||||
document.getElementById("quotes").classList.remove("d-none")
|
||||
|
||||
const text = t.parentElement.getElementsByClassName("text")[0].innerHTML.replace(/\*/g,"\\*").split('\n').pop()
|
||||
document.getElementById('QuotedMessage').innerHTML = text
|
||||
|
||||
const username = t.parentElement.parentElement.getElementsByClassName('userlink')[0].innerHTML
|
||||
document.getElementById('QuotedUser').innerHTML = username
|
||||
|
||||
const id = t.parentElement.parentElement.parentElement.parentElement.id
|
||||
document.getElementById('quotes_id').value = id
|
||||
document.getElementById('QuotedMessageLink').href = `#${id}`
|
||||
}
|
||||
|
||||
textbox.addEventListener("keyup", function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
send();
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('online', function(data){
|
||||
document.getElementsByClassName('board-chat-count')[0].innerHTML = data.length
|
||||
let online = ''
|
||||
let online2 = '<b>Users Online</b>'
|
||||
for (const u of data)
|
||||
{
|
||||
online += `<li><a href="/@${u}">@${u}</a></li>`
|
||||
online2 += `<br>@${u}`
|
||||
}
|
||||
document.getElementById('online').innerHTML = online
|
||||
document.getElementById('online2').setAttribute("data-bs-original-title", online2);
|
||||
document.getElementById('online3').innerHTML = online
|
||||
})
|
||||
|
||||
window.addEventListener('blur', function(){
|
||||
focused=false
|
||||
})
|
||||
window.addEventListener('focus', function(){
|
||||
focused=true
|
||||
})
|
||||
|
||||
|
||||
textbox.addEventListener("input", function() {
|
||||
text = textbox.value
|
||||
if (!text && is_typing==true){
|
||||
is_typing=false;
|
||||
socket.emit('typing', false);
|
||||
}
|
||||
else if (text && is_typing==false) {
|
||||
is_typing=true;
|
||||
socket.emit('typing', true);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
socket.on('typing', function (users){
|
||||
if (users.length==0){
|
||||
document.getElementById('typing-indicator').innerHTML = '';
|
||||
document.getElementById('loading-indicator').classList.add('d-none');
|
||||
}
|
||||
else if (users.length==1){
|
||||
document.getElementById('typing-indicator').innerHTML = '<b>'+users[0]+"</b> is typing...";
|
||||
document.getElementById('loading-indicator').classList.remove('d-none');
|
||||
}
|
||||
else if (users.length==2){
|
||||
document.getElementById('typing-indicator').innerHTML = '<b>'+users[0]+"</b> and <b>"+users[1]+"</b> are typing...";
|
||||
document.getElementById('loading-indicator').classList.remove('d-none');
|
||||
}
|
||||
else {
|
||||
document.getElementById('typing-indicator').innerHTML = '<b>'+users[0]+"</b>, <b>"+users[1]+"</b>, and <b>"+users[2]+"</b> are typing...";
|
||||
document.getElementById('loading-indicator').classList.remove('d-none');
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function del(t) {
|
||||
const chatline = t.parentElement.parentElement.parentElement.parentElement
|
||||
socket.emit('delete', chatline.id);
|
||||
chatline.remove()
|
||||
}
|
||||
|
||||
socket.on('delete', function(text) {
|
||||
const text_spans = document.getElementsByClassName('text')
|
||||
for(const span of text_spans) {
|
||||
if (span.innerHTML == text)
|
||||
{
|
||||
span.parentElement.parentElement.parentElement.parentElement.parentElement.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function scroll_chat() {
|
||||
setTimeout(function () {
|
||||
box.scrollTo(0, box.scrollHeight)
|
||||
}, 200);
|
||||
setTimeout(function () {
|
||||
box.scrollTo(0, box.scrollHeight)
|
||||
}, 500);
|
||||
setTimeout(function () {
|
||||
box.scrollTo(0, box.scrollHeight)
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
scroll_chat();
|
||||
box.scrollTo(0, box.scrollHeight);
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('fa-trash-alt')) {
|
||||
e.target.nextElementSibling.classList.remove('d-none');
|
||||
e.target.classList.add('d-none');
|
||||
}
|
||||
else {
|
||||
for (const btn of document.querySelectorAll('.delmsg:not(.d-none)')) {
|
||||
btn.classList.add('d-none');
|
||||
btn.previousElementSibling.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
if (e.target.id == "cancel") {
|
||||
document.getElementById("quotes").classList.add("d-none")
|
||||
}
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -8,25 +8,17 @@ from files.helpers.actions import *
|
|||
from files.helpers.alerts import *
|
||||
from files.helpers.config.const import *
|
||||
from files.helpers.regex import *
|
||||
from files.helpers.media import process_image
|
||||
from files.helpers.sanitize import sanitize
|
||||
from files.helpers.alerts import push_notif
|
||||
from files.routes.wrappers import *
|
||||
|
||||
from files.__main__ import app, cache, limiter
|
||||
|
||||
if IS_LOCALHOST:
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
async_mode='gevent',
|
||||
logger=True,
|
||||
engineio_logger=True,
|
||||
debug=True
|
||||
)
|
||||
else:
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
async_mode='gevent',
|
||||
)
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
async_mode='gevent',
|
||||
)
|
||||
|
||||
typing = {
|
||||
f'{SITE_FULL}/chat': [],
|
||||
|
@ -45,8 +37,6 @@ messages = cache.get(f'messages') or {
|
|||
f'{SITE_FULL}/chat': [],
|
||||
f'{SITE_FULL}/admin/chat': []
|
||||
}
|
||||
socket_ids_to_user_ids = {}
|
||||
user_ids_to_socket_ids = {}
|
||||
|
||||
@app.get("/chat")
|
||||
@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID)
|
||||
|
@ -66,6 +56,13 @@ def admin_chat(v):
|
|||
@limiter.limit("3/second;10/minute", key_func=get_ID)
|
||||
@admin_level_required(PERMS['CHAT'])
|
||||
def speak(data, v):
|
||||
image = None
|
||||
if data['file']:
|
||||
name = f'/chat_images/{time.time()}'.replace('.','') + '.webp'
|
||||
with open(name, 'wb') as f:
|
||||
f.write(data['file'])
|
||||
image = process_image(name, v)
|
||||
|
||||
limiter.check()
|
||||
if v.is_banned: return '', 403
|
||||
if TRUESCORE_CHAT_MINIMUM and v.truescore < TRUESCORE_CHAT_MINIMUM:
|
||||
|
@ -79,32 +76,26 @@ def speak(data, v):
|
|||
global messages
|
||||
|
||||
text = sanitize_raw_body(data['message'], False)[:CHAT_LENGTH_LIMIT]
|
||||
if image: text += f'\n\n![]({image})'
|
||||
if not text: return '', 400
|
||||
|
||||
text_html = sanitize(text, count_marseys=True)
|
||||
quotes = data['quotes']
|
||||
recipient = data['recipient']
|
||||
data = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"quotes": quotes,
|
||||
"hat": v.hat_active(v)[0],
|
||||
"user_id": v.id,
|
||||
"dm": bool(recipient and recipient != ""),
|
||||
"username": v.username,
|
||||
"namecolor": v.name_color,
|
||||
"text": text,
|
||||
"text_html": text_html,
|
||||
"base_text_censored": censor_slurs(text, 'chat'),
|
||||
"text_censored": censor_slurs(text_html, 'chat'),
|
||||
"time": int(time.time()),
|
||||
}
|
||||
|
||||
if v.shadowbanned or not execute_blackjack(v, None, text, "chat"):
|
||||
emit('speak', data)
|
||||
elif recipient:
|
||||
if user_ids_to_socket_ids.get(recipient):
|
||||
recipient_sid = user_ids_to_socket_ids[recipient]
|
||||
emit('speak', data, broadcast=False, to=recipient_sid)
|
||||
else:
|
||||
emit('speak', data, room=request.referrer, broadcast=True)
|
||||
messages[request.referrer].append(data)
|
||||
|
@ -148,10 +139,6 @@ def connect(v):
|
|||
online[request.referrer].append(v.username)
|
||||
refresh_online()
|
||||
|
||||
if not socket_ids_to_user_ids.get(request.sid):
|
||||
socket_ids_to_user_ids[request.sid] = v.id
|
||||
user_ids_to_socket_ids[v.id] = request.sid
|
||||
|
||||
emit('catchup', messages[request.referrer], room=request.referrer)
|
||||
emit('typing', typing[request.referrer], room=request.referrer)
|
||||
return '', 204
|
||||
|
@ -167,10 +154,6 @@ def disconnect(v):
|
|||
if v.username in typing[request.referrer]:
|
||||
typing[request.referrer].remove(v.username)
|
||||
|
||||
if socket_ids_to_user_ids.get(request.sid):
|
||||
del socket_ids_to_user_ids[request.sid]
|
||||
del user_ids_to_socket_ids[v.id]
|
||||
|
||||
emit('typing', typing[request.referrer], room=request.referrer, broadcast=True)
|
||||
|
||||
leave_room(request.referrer)
|
||||
|
@ -193,13 +176,13 @@ def typing_indicator(data, v):
|
|||
@socketio.on('delete')
|
||||
@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID)
|
||||
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
|
||||
def delete(text, v):
|
||||
|
||||
for message in messages:
|
||||
if message['text'] == text:
|
||||
def delete(id, v):
|
||||
for message in messages[request.referrer]:
|
||||
if message['id'] == id:
|
||||
messages[request.referrer].remove(message)
|
||||
break
|
||||
|
||||
emit('delete', text, room=request.referrer, broadcast=True)
|
||||
emit('delete', id, room=request.referrer, broadcast=True)
|
||||
|
||||
return '', 204
|
||||
|
||||
|
|
|
@ -11,19 +11,90 @@
|
|||
{% include "header.html" %}
|
||||
{% include "modals/expanded_image.html" %}
|
||||
{% include "modals/emoji.html" %}
|
||||
<div
|
||||
id="root"
|
||||
data-id="{{v.id}}"
|
||||
data-username="{{v.username}}"
|
||||
data-admin="{{v.admin_level >= PERMS['POST_COMMENT_MODERATION_TOOLS_VISIBLE']}}"
|
||||
data-censored="{{v.slurreplacer}}"
|
||||
data-sitename="{{SITE_NAME}}"
|
||||
data-themecolor="{{v.themecolor}}"
|
||||
data-namecolor="{{v.namecolor}}"
|
||||
data-hat="{{v.hat_active(v)[0]}}">
|
||||
<div class="container pb-4">
|
||||
<div class="row justify-content-around" id="main-content-row">
|
||||
<div class="col h-100 {% block customPadding %}{% if request.path.startswith('/@') %}user-gutters{% else %}custom-gutters{% endif %}{% endblock %}" id="main-content-col">
|
||||
|
||||
<div class="border-right pb-1 pt-2 px-3">
|
||||
<span id="online2" data-bs-html="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Users Online" class="text-muted">
|
||||
<i class="far fa-user fa-sm mr-1"></i>
|
||||
<span class="board-chat-count">0</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="chat-line-template" class="d-none">
|
||||
<div class="chat-line">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="pl-md-3 text-muted chat-line-content">
|
||||
<div class="avatar profile-pic-20-wrapper">
|
||||
<img class="avatar-pic pp20 mr-1">
|
||||
<img class="avatar-hat profile-pic-20-hat hat" loading="lazy">
|
||||
</div>
|
||||
<a href="" class="font-weight-bold text-black userlink" target="_blank"></a>
|
||||
<span class="text-black time ml-1 mb-3 d-none text-center">just now</span>
|
||||
<div class="cdiv">
|
||||
<div class="d-none quotes" style="font-size:10px">
|
||||
<a class="QuotedMessageLink">
|
||||
<i class="fas fa-reply"></i> <span class="text-primary">@<span class="QuotedUser"></span></span>: <em class="QuotedMessage"></em>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<span class="chat-message text-black text-break"></span>
|
||||
<span class="text d-none"></span>
|
||||
{% if v.admin_level > 1 %}
|
||||
<i class="btn del fas fa-trash-alt"></i>
|
||||
<i class="btn d-none del delmsg fas fa-trash-alt text-danger" data-nonce="{{g.nonce}}" data-onclick="del(this)"></i>
|
||||
{% endif %}
|
||||
<i class="quote btn fas fa-reply" data-nonce="{{g.nonce}}" data-onclick="quote(this)"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="shrink">
|
||||
<div id="chat-window" class="container pl-0 py-0">
|
||||
</div>
|
||||
|
||||
<div class="mt-3"></div>
|
||||
<div id="quotes" class="mt-3 ml-3 mb-2 d-none" style="font-size:10px">
|
||||
<a id="QuotedMessageLink">
|
||||
<i class="fas fa-reply"></i> <span class="text-primary">@<span id="QuotedUser"></span></span>: <em id="QuotedMessage"></em>
|
||||
<button type="button" id="cancel" class="btn btn-secondary">Cancel</button>
|
||||
<input hidden id="quotes_id">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id='message' class="d-none position-relative form-group d-flex">
|
||||
<div class="position-absolute text-muted text-small ml-1" style="bottom: -1.5rem; line-height: 1;">
|
||||
<span id="typing-indicator"></span>
|
||||
<span id="loading-indicator" class="d-none"></span>
|
||||
</div>
|
||||
<i class="btn btn-secondary fas fa-smile-beam my-auto" style="font-size:1.3rem!important" data-nonce="{{g.nonce}}" data-onclick="loadEmojis('input-text')"></i>
|
||||
|
||||
<label class="btn btn-secondary format my-auto ml-2">
|
||||
<div id="filename" class="mr-4" style="font-size:10px"><i class="fas fa-image" style="font-size:1.3rem!important"></i></div>
|
||||
<input autocomplete="off" id="file" accept="image/*" type="file" multiple="multiple" name="file" {% if g.is_tor %}disabled{% endif %} data-nonce="{{g.nonce}}" data-onchange="changename('filename','file')" hidden>
|
||||
</label>
|
||||
|
||||
<textarea data-nonce="{{g.nonce}}" data-onclick="scroll_chat()" id="input-text" minlength="1" maxlength="{% if SITE == 'rdrama.net' %}200{% else %}1000{% endif %}" style="font-size:16px!important" class="form-control" placeholder="Message" autocomplete="off" autofocus rows="1"></textarea>
|
||||
|
||||
<i id="chatsend" data-nonce="{{g.nonce}}" data-onclick="send()" class="btn btn-secondary fas fa-reply ml-3 my-auto" type="submit" data-nonce="{{g.nonce}}" data-onclick="disable(this)" style="transform:rotateY(180deg);font-size:1.3rem!important"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col text-left d-none d-lg-block pt-3" style="max-width:300px">
|
||||
<h5>Users Online</h5>
|
||||
<div id="online" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input id="vid" type="hidden" value="{{v.id}}">
|
||||
<input id="vusername" type="hidden" value="{{v.username}}">
|
||||
<input id="site_name" type="hidden" value="{{SITE_NAME}}">
|
||||
<input id="slurreplacer" type="hidden" value="{{v.slurreplacer}}">
|
||||
<script defer src="{{'js/vendor/socketio.js' | asset}}"></script>
|
||||
<script defer src="{{'js/chat.js' | asset}}"></script>
|
||||
<script defer src="{{'js/chat_done.js' | asset}}"></script>
|
||||
<script defer src="{{'js/vendor/lozad.js' | asset}}"></script>
|
||||
<script defer src="{{'js/vendor/lite-youtube.js' | asset}}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -322,9 +322,11 @@
|
|||
</li>
|
||||
{% endif %}
|
||||
<li class="mt-3">
|
||||
{% if has_sidebar %}
|
||||
{% set sidebar = "sidebar_" ~ SITE_NAME ~ ".html" %}
|
||||
{% include sidebar %}
|
||||
{% if request.path.endswith("/chat") %}
|
||||
<h5 class="ml-3">Users Online</h5>
|
||||
<div id="online3" class="col text-left d-lg-none bg-white mb-6" style="max-width:300px"></div>
|
||||
{% elif has_sidebar %}
|
||||
{% include "sidebar_" ~ SITE_NAME ~ ".html" %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -95,7 +95,6 @@
|
|||
|
||||
{% macro stylesheets(include_user_css) %}
|
||||
<link rel="stylesheet" href="{{'css/main.css' | asset}}">
|
||||
<link id="favicon" rel="icon" type="image/webp" href="/icon.webp?v=1">
|
||||
{% if v %}
|
||||
{% if v.agendaposter %}
|
||||
<link rel="stylesheet" href="{{('css/agendaposter.css') | asset}}">
|
||||
|
@ -135,7 +134,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if request.path.endswith('/chat') %}
|
||||
<link rel="stylesheet" href="{{'css/chat_done.css' | asset}}">
|
||||
<link rel="stylesheet" href="{{'css/chat.css' | asset}}">
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ server {
|
|||
alias /images/;
|
||||
include includes/serve-static;
|
||||
}
|
||||
location /chat_images/ {
|
||||
alias /chat_images/;
|
||||
include includes/serve-static;
|
||||
}
|
||||
location /videos/ {
|
||||
alias /videos/;
|
||||
include includes/serve-static;
|
||||
|
|
|
@ -37,6 +37,7 @@ psql -U postgres -f seed-db.sql postgres
|
|||
pip3 install -r requirements.txt
|
||||
|
||||
mkdir /images
|
||||
mkdir /chat_images
|
||||
mkdir /songs
|
||||
mkdir /videos
|
||||
mkdir /audio
|
||||
|
|
Loading…
Reference in New Issue