Add direct message

This commit is contained in:
cuom1999 2021-11-20 22:23:03 -06:00
parent 259cb95b43
commit 2f8ef1b524
20 changed files with 1066 additions and 195 deletions

View file

@ -8,24 +8,53 @@
<script type="text/javascript" src="{{ static('event.js') }}"></script>
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script type="module" src="https://unpkg.com/emoji-picker-element@1"></script>
<script type="text/javascript">
let message_template = `
{% with message=message_template %}
{% include "chat/message.html" %}
{% endwith %}
`;
</script>
<script type="text/javascript">
window.currentPage = 1;
window.limit_time = 0;
window.limit_time = 24;
window.messages_per_page = 50;
window.room_id = "{{room if room else ''}}";
window.unread_message = 0;
window.other_user_id = "{{other_user.id if other_user else ''}}";
window.num_pages = {{paginator.num_pages}};
window.lock = false;
function load_page(page) {
$.get('?page=' + page)
function load_page(page, refresh_html=false) {
var param = {
'page': page,
}
$.get("{{ url('chat', '') }}" + window.room_id, param)
.fail(function() {
console.log("Fail to load page " + page);
})
.done(function(data) {
if (refresh_html) {
$('#chat-log').html('');
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
window.lock = true;
}
window.num_pages = parseInt($('<div>' + data + '</div>').find('#num_pages').html());
var time = refresh_html ? 0 : 500;
setTimeout(function() {
let container = $('#chat-box');
let lastMsgPos = scrollTopOfBottom(container)
let $chat_box = $('#chat-box');
let lastMsgPos = scrollTopOfBottom($chat_box)
$('#loader').hide();
$('#chat-log').prepend(data);
if (refresh_html) {
$('#chat-log').append(data);
}
else {
$('#chat-log').prepend(data);
}
$('.body-block').slice(0, window.messages_per_page).each(function() {
resize_emoji($(this));
@ -34,8 +63,14 @@
register_time($('.time-with-rel'));
merge_authors();
container.scrollTop(scrollTopOfBottom(container) - lastMsgPos);
}, 500);
if (!refresh_html) {
$chat_box.scrollTop(scrollTopOfBottom($chat_box) - lastMsgPos);
}
else {
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
}
window.lock = false;
}, time);
})
}
@ -46,7 +81,7 @@
function scrollContainer(container, loader) {
container.scroll(function() {
if (container.scrollTop() == 0) {
if (currentPage < {{paginator.num_pages}}) {
if (currentPage < window.num_pages && !window.lock) {
currentPage++;
loader.show();
load_page(currentPage);
@ -57,41 +92,121 @@
window.load_dynamic_update = function (last_msg) {
return new EventReceiver(
"{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
['chat'], last_msg, function (message) {
switch (message.type) {
case 'new_message':
add_new_message(message.message);
break;
['chat_lobby', 'chat_{{request.profile.id}}'], last_msg, function (message) {
var room = (message.type == 'lobby') ? '' : message.room;
if (message.author_id == {{request.profile.id}}) {
check_new_message(message.message, message.tmp_id, room);
}
else {
add_new_message(message.message, room);
}
}
);
}
function add_new_message(message) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
var $data = $(data);
resize_emoji($data.find('.body-block'));
$('#chat-log').append($data);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
register_time($('.time-with-rel'));
merge_authors();
},
error: function (data) {
if (data.status === 403)
console.log('No right to see: ' + message);
else {
console.log('Could not load chat message:');
console.log(data.responseText);
function refresh_status() {
$.get("{{url('online_status_ajax')}}")
.fail(function() {
console.log("Fail to get online status");
})
.done(function(data) {
if (data.status == 403) {
console.log("Fail to retrieve data");
}
else {
$('#chat-online-list').html(data).find('.toggle').each(function () {
register_toggle($(this));
});
register_click_space();
}
})
var data = {
'user': window.other_user_id,
};
$.get("{{url('user_online_status_ajax')}}", data)
.fail(function() {
console.log("Fail to get user online status");
})
.done(function(data) {
$('#chat-info').html(data);
register_time($('.time-with-rel'));
register_setting();
})
}
function add_message(data) {
var $data = $(data);
resize_emoji($data.find('.body-block'));
$('#chat-log').append($data);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
register_time($('.time-with-rel'));
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
merge_authors();
}
function add_new_message(message, room) {
function callback(update) {
if (!document['hidden']) {
if (update) update_last_seen();
refresh_status();
}
});
else {
window.unread_message++;
document.title = "(" + window.unread_message + ") " + "{{ _('New message(s)') }}";
}
}
if (room == window.room_id) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
add_message(data);
callback(true);
},
error: function (data) {
console.log('Could not add new message');
}
});
}
else {
callback(false);
}
}
function check_new_message(message, tmp_id, room) {
if (room == "{{room}}") {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
var $body_block = $(data).find('.body-block');
if ($('#message-'+tmp_id).length) {
$('#message-'+tmp_id).replaceWith(data);
}
else {
$('#body-block-'+tmp_id).replaceWith($body_block);
}
resize_emoji($body_block);
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
register_time($('.time-with-rel'));
remove_unread_current_user();
},
error: function (data) {
console.log('Fail to check message');
var $body = $('#body-block-'+tmp_id + ' p');
$body.css('text-decoration', 'line-through');
$body.css('text-decoration-color', 'red');
}
});
}
}
function merge_authors() {
@ -119,25 +234,40 @@
});
}
function add_message_from_template(body, tmp_id) {
var html = message_template;
html = html.replaceAll('$body', body).replaceAll('$id', tmp_id);
var $html = $(html);
$html.find('.time-with-rel').attr('data-iso', (new Date()).toISOString());
add_message($html[0].outerHTML, false);
}
function submit_chat() {
{% if last_msg and not request.profile.mute %}
if ($("#chat-input").val().trim()) {
var body = $('#chat-input').val().trim();
body = body.split('\n').join('\n\n');
var message = {
body: body,
room: window.room_id,
tmp_id: Date.now(),
};
$('#chat-input').val('');
add_message_from_template(body, message.tmp_id);
$.post("{{ url('post_chat_message') }}", message)
.fail(function(res) {
console.log('Fail to send message');
})
.done(function(res, status) {
$('#empty_msg').hide();
$('#chat-input').focus();
})
}
{% endif %}
}
function resize_emoji(element) {
@ -165,6 +295,77 @@
}
}
function register_click_space() {
function callback() {
history.replaceState(null, '', "{{url('chat', '')}}" + window.room_id);
load_page(window.currentPage, true, refresh_status);
update_last_seen();
refresh_status();
$('#chat-input').focus();
}
$('.click_space').on('click', function(e) {
if ($(this).attr('id') == 'click_space_' + window.other_user_id) {
return;
}
var other_user = $(this).attr('value');
$.get("{{url('get_or_create_room')}}" + `?other=${other_user}`)
.done(function(data) {
window.currentPage = 1;
window.room_id = data.room;
window.other_user_id = data.other_user_id;
callback();
})
.fail(function() {
console.log('Fail to get_or_create_room');
})
});
$('#lobby_row').on('click', function(e) {
if (window.room_id) {
window.currentPage = 1;
window.room_id = '';
window.other_user_id = '';
callback();
}
});
}
function update_last_seen() {
var data = {
room: window.room_id
};
$.post("{{ url('update_last_seen') }}", data)
.fail(function(data) {
console.log('Fail to update last seen');
})
.done(function(data) {
})
}
function remove_unread_current_user() {
if (window.other_user_id) {
$("#unread-count-" + window.other_user_id).hide();
}
else {
$('#unread-count-lobby').hide();
}
}
function register_setting() {
$('#setting-button').on('click', function() {
$('#setting-content').toggle();
});
$('#setting-content li').on('click', function() {
$(this).children('a')[0].click();
})
$('#setting-content a').on('click', function() {
var href = $(this).attr('href');
href += '?next=' + window.location.pathname;
$(this).attr('href', href);
})
}
$(function() {
$('#loader').hide();
merge_authors();
@ -191,7 +392,7 @@
}
},
fail: function(data) {
alert('Fail to delete');
console.log('Fail to delete');
},
});
});
@ -201,6 +402,7 @@
resize_emoji($(this));
});
$("#chat-log").show();
$("#chat-log").change(function() {
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
});
@ -237,26 +439,9 @@
$('.chat-right-panel').show();
});
$('#refresh-button').on('click', function() {
$.get("{{url('online_status_ajax')}}")
.fail(function() {
console.log("Fail to get online status");
})
.done(function(data) {
if (data.status == 403) {
console.log("Fail to retrieve data");
}
else {
$('#chat-online-content').html(data).find('.toggle').each(function () {
register_toggle($(this));
});;
}
})
})
$('#refresh-button').on('click', refresh_status)
setInterval(function() {
$('#refresh-button').click();
}, 5 * 60 * 1000);
setInterval(refresh_status, 2 * 60 * 1000);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
load_dynamic_update({{last_msg}});
@ -279,11 +464,67 @@
$chat.focus();
})
register_click_space();
document.addEventListener('keydown', function(e) {
if (e.keyCode === 27 && $('.tooltip').hasClass('shown')) {
toggleEmoji();
}
})
$('#search-handle').replaceWith($('<select>').attr({
id: 'search-handle',
name: 'other',
onchange: 'form.submit()'
}));
var in_user_redirect = false;
$('#search-handle').select2({
placeholder: '{{ _('Search by handle...') }}',
ajax: {
url: '{{ url('chat_user_search_select2_ajax') }}'
},
minimumInputLength: 1,
escapeMarkup: function (markup) {
return markup;
},
templateResult: function (data, container) {
return $('<span>')
.append($('<img>', {
'class': 'user-search-image', src: data.gravatar_url,
width: 24, height: 24
}))
.append($('<span>', {'class': data.display_rank + ' user-search-name'}).text(data.text))
.append($('<a>', {href: '/user/' + data.text, 'class': 'user-redirect'})
.append($('<i>', {'class': 'fa fa-mail-forward'}))
.mouseover(function () {
in_user_redirect = true;
}).mouseout(function () {
in_user_redirect = false;
}));
}
}).on('select2:selecting', function () {
return !in_user_redirect;
});
// https://stackoverflow.com/questions/42121565/detecting-class-change-without-setinterval
if (typeof(MutationObserver) !== undefined) {
var observer = new MutationObserver(function (event) {
if (!document['hidden'] && window.unread_message > 0) {
update_last_seen();
refresh_status();
window.unread_message = 0;
document.title = "{{_('Chat Box')}}";
}
})
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
childList: false,
characterData: false
})
}
register_setting();
});
</script>
@ -314,23 +555,37 @@
</button>
</h3>
<div id="chat-online-content">
{% include "chat/online_status.html" %}
<div id="search-container">
<center>
<form id="search-form" name="form" action="{{ url('get_or_create_room') }}" method="get">
<input id="search-handle" type="text" name="search"
placeholder="{{ _('Search by handle...') }}">
</form>
</center>
</div>
<div id="chat-online-list">
{% include "chat/online_status.html" %}
</div>
</div>
</div>
<div id="chat-area" class="chat-left-panel" style="width:100%">
<div id="chat-box">
<img src="http://opengraphicdesign.com/wp-content/uploads/2009/01/loader64.gif" id="loader">
<ul id="chat-log">
{% include 'chat/message_list.html' %}
</ul>
</div>
<div style="height: 15%">
<a id="emoji-button" href="#" title="{{_('Emoji')}}"><i class="icofont-slightly-smile"></i></a>
<textarea id="chat-input" placeholder="{{_('Enter your message')}}"></textarea>
</div>
<div class="tooltip" role="tooltip">
<emoji-picker></emoji-picker>
</div>
<div id="chat-info" style="height: 8%">
{% include 'chat/user_online_status.html' %}
</div>
<div id="chat-box">
<img src="{{static('loading.gif')}}" id="loader">
<ul id="chat-log" style="display: none">
{% include 'chat/message_list.html' %}
</ul>
</div>
<div style="height: 15%">
<a id="emoji-button" href="#" title="{{_('Emoji')}}"><i class="icofont-slightly-smile"></i></a>
<textarea id="chat-input" placeholder="{{_('Enter your message')}}"></textarea>
</div>
<div class="tooltip" role="tooltip">
<emoji-picker></emoji-picker>
</div>
</div>
</div>
{% endblock body %}

View file

@ -2,53 +2,7 @@
#content {
margin: -1em 1em 0 0;
}
#refresh-button {
padding: 0;
margin-left: auto;
margin-right: 0.3em;
background: transparent;
border: none;
height: 1.5em;
width: 1.5em;
}
#refresh-button:hover {
background: lightgreen;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
transition: 1.5s ease-in-out;
}
.status-pic {
height: 1.3em;
width: 1.3em;
border-radius: 0.3em;
}
.status-container {
position: relative;
display: inline-flex;
}
.status-circle {
position: absolute;
bottom: 0;
right: 0;
cx: 18px;
cy: 18px;
r: 4.5px;
stroke: white;
stroke-width: 1;
}
.status-row {
display: flex;
margin-bottom: 0.5em;
font-size: 15px;
}
.status-list {
padding: 0;
padding-left: 1em;
}
.status-section-title {
cursor: pointer;
margin-top: 0.5em;
}
::-webkit-scrollbar {
width: 20px;
}
@ -143,14 +97,55 @@
.body-block:hover {
background: #eee;
}
#chat-input, #chat-log .content-message {
font-family: "Apple Color Emoji", "Segoe UI", "Lucida Grande", Arial, sans-serif;
.active-span {
margin-top: 1em;
margin-right: 1em;
color: #636363;
}
.unread-count {
float: right;
color: white;
background-color: darkcyan;
border-radius: 2px;
padding: 0 0.5em;
}
#search-form {
float: inherit;
}
#search-container {
margin-bottom: 0.4em;
}
#setting {
position: relative;
}
#setting-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0;
}
#setting-content li {
padding: 12px 16px;
text-decoration: none;
display: block;
color: black;
font-weight: bold;
}
#setting-content li:hover {
background-color: #ddd;
cursor: pointer;
}
#page-container {
position:fixed;
overflow:hidden;
}
@media (min-width: 800px) {
#page-container {
position:fixed;
overflow:hidden;
}
}
@media (max-width: 799px) {
html, body {
@ -160,5 +155,11 @@
#mobile ul {
width: 100%;
}
.info-pic {
margin-left: 0.5em;
}
.active-span {
display: none;
}
}
</style>

View file

@ -6,7 +6,7 @@
<div class="user-time">
<span class="username {{ message.author.css_class }}">
<a href="{{ url('user_page', message.author.user.username) }}">
{{message.author}}
{{ message.author }}
</a>
</span>
<span class="time">
@ -14,7 +14,7 @@
</span>
</div>
<span class="content-message">
<div class="body-block" title="{{ message.time|date('g:i a') }}">
<div class="body-block" id="body-block-{{ message.id }}" title="{{ message.time|date('g:i a') }}">
{% if request.user.is_staff %}
<a class="chatbtn_remove_mess" value="{{message.id}}" style="color:red; cursor: pointer;">
{{_('Delete')}}

View file

@ -1,8 +1,12 @@
{% if object_list %}
<div style="display: none" id="num_pages">{{num_pages}}</div>
{% for message in object_list | reverse%}
{% include "chat/message.html" %}
{% endfor %}
{% else %}
<center id="empty_msg">{{_('You are connect now. Say something to start the conversation.')}}</center>
{% endif %}
{% if REQUIRE_JAX %}
{% include "mathjax-load.html" %}
{% endif %}
{% include "comments/math.html" %}
{% include "comments/math.html" %}

View file

@ -1,3 +1,14 @@
<li class="status-row" id="lobby_row">
<div class="status-container">
<img src="{{ static('icons/logo.png') }}" style="height:1.3em">
</div>
<span style="padding-left:0.5em">
<b>{{_('Lobby')}}</b>
</span>
<span class="spacer">
<span class="unread-count" id="unread-count-lobby">{{unread_count_lobby if unread_count_lobby}}</span>
</span>
</li>
{% for section in status_sections %}
{% if section.user_list %}
<div class="status-section-title toggle open">
@ -8,7 +19,7 @@
</div>
<ul class="status-list toggled">
{% for user in section.user_list %}
<li style="padding-left: 0.1em" class="status-row">
<li class="status-row">
<div class="status-container">
<img src="{{ gravatar(user.user, 135) }}" class="status-pic">
<svg style="position:absolute;" height="32" width="32">
@ -19,6 +30,9 @@
<span style="padding-left:0.3em" class="username {{ user.user.css_class }}">
{{ link_user(user.user) }}
</span>
<span class="click_space spacer" value="{{user.url}}" id="click_space_{{user.user.id}}">
<span class="unread-count" id="unread-count-{{user.user.id}}">{{user.unread_count if user.unread_count}}</span>
</span>
</li>
{% endfor %}
</ul>

View file

@ -0,0 +1,35 @@
{% if other_user %}
<div class="status-container" style="height: 100%">
<img src="{{ gravatar(other_user.user, 135) }}" class="info-pic">
<svg style="position:absolute; height:100%; width: 110%">
<circle class="info-circle"
fill="{{'green' if other_online else 'red'}}"/>
</svg>
</div>
{% endif %}
<span class="info-name username">
{% if other_user %}
<a href="{{url('user_page', other_user)}}">{{other_user.user.username}}</a>
{% else%}
<a href="#" style="margin-left: 3em">{{ _('Lobby') }}</a>
{% endif %}
</span>
<span class="spacer"></span>
{% if other_user and not other_online %}
<span class="active-span">{{ relative_time(other_user.last_access, abs=_('Last online on {time}'), rel=_('Online {time}'), format=_('g:i a d/m/Y')) }}</span>
{% endif %}
{% if other_user %}
<span style="margin-right: 0.3em" id="setting">
<button class="control-button" style="height:100%;" id="setting-button">
<i class="fa fa-ellipsis-h"></i>
</button>
<div id="setting-content">
<li>
<a href="{{url('toggle_ignore', other_user.id)}}" style="color: {{'green' if is_ignored else 'red'}}">{{_('Unignore') if is_ignored else _('Ignore')}}</a>
</li>
</div>
</span>
{% else %}
<span class="active-span">{{online_count}} {{_('users are online')}}</span>
{% endif %}