Modify Chat UI

This commit is contained in:
cuom1999 2023-08-29 18:36:01 -05:00
parent 9f0213865d
commit 00113848c8
10 changed files with 1663 additions and 1572 deletions

View file

@ -18,15 +18,22 @@ class Room(models.Model):
Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE
) )
@cache_wrapper(prefix="Rc")
def contain(self, profile): def contain(self, profile):
return self.user_one == profile or self.user_two == profile return self.user_one == profile or self.user_two == profile
@cache_wrapper(prefix="Rou")
def other_user(self, profile): def other_user(self, profile):
return self.user_one if profile == self.user_two else self.user_two return self.user_one if profile == self.user_two else self.user_two
@cache_wrapper(prefix="Rus")
def users(self): def users(self):
return [self.user_one, self.user_two] return [self.user_one, self.user_two]
@cache_wrapper(prefix="Rlmb")
def last_message_body(self):
return self.message_set.first().body
class Message(models.Model): class Message(models.Model):
author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE) author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE)

View file

@ -22,6 +22,7 @@ from django.db.models import (
Count, Count,
IntegerField, IntegerField,
F, F,
Max,
) )
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
@ -206,6 +207,7 @@ def post_message(request):
}, },
) )
else: else:
Room.last_message_body.dirty(room)
for user in room.users(): for user in room.users():
event.post( event.post(
encrypt_channel("chat_" + str(user.id)), encrypt_channel("chat_" + str(user.id)),
@ -342,15 +344,31 @@ def get_online_status(profile, other_profile_ids, rooms=None):
if rooms: if rooms:
unread_count = get_unread_count(rooms, profile) unread_count = get_unread_count(rooms, profile)
count = {} count = {}
last_msg = {}
room_of_user = {}
for i in unread_count: for i in unread_count:
count[i["other_user"]] = i["unread_count"] room = Room.objects.get(id=i["room"])
other_profile = room.other_user(profile)
count[other_profile.id] = i["unread_count"]
for room in rooms:
room = Room.objects.get(id=room)
other_profile = room.other_user(profile)
last_msg[other_profile.id] = room.last_message_body()
room_of_user[other_profile.id] = room.id
for other_profile in other_profiles: for other_profile in other_profiles:
is_online = False is_online = False
if other_profile.last_access >= last_5_minutes: if other_profile.last_access >= last_5_minutes:
is_online = True is_online = True
user_dict = {"user": other_profile, "is_online": is_online} user_dict = {"user": other_profile, "is_online": is_online}
if rooms and other_profile.id in count: if rooms:
user_dict["unread_count"] = count[other_profile.id] user_dict.update(
{
"unread_count": count.get(other_profile.id),
"last_msg": last_msg.get(other_profile.id),
"room": room_of_user.get(other_profile.id),
}
)
user_dict["url"] = encrypt_url(profile.id, other_profile.id) user_dict["url"] = encrypt_url(profile.id, other_profile.id)
ret.append(user_dict) ret.append(user_dict)
return ret return ret
@ -386,17 +404,9 @@ def get_status_context(profile, include_ignored=False):
recent_profile_ids = [str(i["other_user"]) for i in recent_profile] recent_profile_ids = [str(i["other_user"]) for i in recent_profile]
recent_rooms = [int(i["id"]) for i in recent_profile] recent_rooms = [int(i["id"]) for i in recent_profile]
friend_list = (
Friend.get_friend_profiles(profile)
.exclude(id__in=recent_profile_ids)
.exclude(id__in=ignored_users)
.order_by("-last_access")
.values_list("id", flat=True)
)
admin_list = ( admin_list = (
queryset.filter(display_rank="admin") queryset.filter(display_rank="admin")
.exclude(id__in=friend_list)
.exclude(id__in=recent_profile_ids) .exclude(id__in=recent_profile_ids)
.values_list("id", flat=True) .values_list("id", flat=True)
) )
@ -405,7 +415,6 @@ def get_status_context(profile, include_ignored=False):
queryset.filter(last_access__gte=last_5_minutes) queryset.filter(last_access__gte=last_5_minutes)
.annotate(is_online=Case(default=True, output_field=BooleanField())) .annotate(is_online=Case(default=True, output_field=BooleanField()))
.order_by("-rating") .order_by("-rating")
.exclude(id__in=friend_list)
.exclude(id__in=admin_list) .exclude(id__in=admin_list)
.exclude(id__in=recent_profile_ids) .exclude(id__in=recent_profile_ids)
.values_list("id", flat=True)[:30] .values_list("id", flat=True)[:30]
@ -416,10 +425,6 @@ def get_status_context(profile, include_ignored=False):
"title": "Recent", "title": "Recent",
"user_list": get_online_status(profile, recent_profile_ids, recent_rooms), "user_list": get_online_status(profile, recent_profile_ids, recent_rooms),
}, },
{
"title": "Following",
"user_list": get_online_status(profile, friend_list),
},
{ {
"title": "Admin", "title": "Admin",
"user_list": get_online_status(profile, admin_list), "user_list": get_online_status(profile, admin_list),
@ -488,18 +493,9 @@ def get_or_create_room(request):
def get_unread_count(rooms, user): def get_unread_count(rooms, user):
if rooms: if rooms:
res = ( return UserRoom.objects.filter(
UserRoom.objects.filter(user=user, room__in=rooms, unread_count__gt=0) user=user, room__in=rooms, unread_count__gt=0
.select_related("room__user_one", "room__user_two") ).values("unread_count", "room")
.values("unread_count", "room__user_one", "room__user_two")
)
for ur in res:
ur["other_user"] = (
ur["room__user_one"]
if ur["room__user_two"] == user.id
else ur["room__user_two"]
)
return res
else: # lobby else: # lobby
user_room = UserRoom.objects.filter(user=user, room__isnull=True).first() user_room = UserRoom.objects.filter(user=user, room__isnull=True).first()
if not user_room: if not user_room:

View file

@ -5,9 +5,10 @@ from django.db.models.query import QuerySet
import hashlib import hashlib
MAX_NUM_CHAR = 15 MAX_NUM_CHAR = 15
NONE_RESULT = "__None__"
def cache_wrapper(prefix, timeout=86400): def cache_wrapper(prefix, timeout=None):
def arg_to_str(arg): def arg_to_str(arg):
if hasattr(arg, "id"): if hasattr(arg, "id"):
return str(arg.id) return str(arg.id)
@ -31,8 +32,11 @@ def cache_wrapper(prefix, timeout=86400):
cache_key = get_key(func, *args, **kwargs) cache_key = get_key(func, *args, **kwargs)
result = cache.get(cache_key) result = cache.get(cache_key)
if result is not None: if result is not None:
if result == NONE_RESULT:
result = None
return result return result
if result is None:
result = NONE_RESULT
result = func(*args, **kwargs) result = func(*args, **kwargs)
cache.set(cache_key, result, timeout) cache.set(cache_key, result, timeout)
return result return result

View file

@ -47,11 +47,6 @@ else:
"pagedown-extra/Markdown.Extra.js", "pagedown-extra/Markdown.Extra.js",
"pagedown_init.js", "pagedown_init.js",
] ]
css = {
"all": [
"markdown.css",
]
}
class AdminPagedownWidget(PagedownWidget, admin_widgets.AdminTextareaWidget): class AdminPagedownWidget(PagedownWidget, admin_widgets.AdminTextareaWidget):
class Media: class Media:
@ -60,7 +55,6 @@ else:
"pagedown_widget.css", "pagedown_widget.css",
"content-description.css", "content-description.css",
"admin/css/pagedown.css", "admin/css/pagedown.css",
"markdown.css",
"pagedown.css", "pagedown.css",
] ]
} }
@ -125,7 +119,6 @@ else:
"pygment-github.css", "pygment-github.css",
"table.css", "table.css",
"ranks.css", "ranks.css",
"markdown.css",
"dmmd-preview.css", "dmmd-preview.css",
] ]
} }

View file

@ -39,15 +39,15 @@
#chat-online { #chat-online {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
padding-bottom: 0 !important; padding-bottom: 0 !important;
min-width: 25%;
border-bottom: 0; border-bottom: 0;
font-size: 1.2em;
} }
#chat-online-content { #chat-online-content {
margin-bottom: 0; margin-bottom: 0;
overflow: hidden; overflow: hidden;
overflow-wrap: break-word; overflow-wrap: break-word;
overflow-y: auto; overflow-y: auto;
max-height: calc(100% - 44px); max-height: 100%;
} }
#chat-box { #chat-box {
/*border: 1px solid #ccc;*/ /*border: 1px solid #ccc;*/
@ -75,6 +75,14 @@
padding: 0; padding: 0;
width: 100%; width: 100%;
} }
.selected-status-row {
background-color: lightgray;
}
.status_last_message {
color: darkgray;
font-size: 0.8em;
}
@media (min-width: 800px) { @media (min-width: 800px) {
#chat-container { #chat-container {
display: flex; display: flex;
@ -86,6 +94,10 @@
} }
#chat-online { #chat-online {
margin: 0; margin: 0;
width: 35%;
}
#chat-area {
flex-grow: 1;
} }
.chat-left-panel, .chat-right-panel { .chat-left-panel, .chat-right-panel {
display: block !important; display: block !important;
@ -138,29 +150,29 @@
transition: 1.5s ease-in-out; transition: 1.5s ease-in-out;
} }
.status-pic { .status-pic {
height: 1.3em; height: 32px;
width: 1.3em; width: 32px;
border-radius: 0.3em; border-radius: 50%;
} }
.status-container { .status-container {
position: relative; position: relative;
display: inline-flex; display: inline-flex;
flex: 0 0 auto;
} }
.status-circle { .status-circle {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 0; right: 0;
cx: 18px; cx: 27px;
cy: 18px; cy: 27px;
r: 4.5px; r: 4.5px;
stroke: white; stroke: white;
stroke-width: 1; stroke-width: 1;
} }
.status-row { .status-row {
display: flex; display: flex;
font-size: 15px; padding: 15px;
padding: 0.2em 0.2em 0.2em 1em; gap: 0.5em;
border-radius: 4px;
} }
.status-row:hover { .status-row:hover {
background: lightgray; background: lightgray;
@ -168,6 +180,7 @@
} }
.status-list { .status-list {
padding: 0; padding: 0;
margin: 0;
} }
.status-section-title { .status-section-title {
cursor: pointer; cursor: pointer;
@ -200,6 +213,7 @@
background-color: darkcyan; background-color: darkcyan;
border-radius: 2px; border-radius: 2px;
padding: 0 0.5em; padding: 0 0.5em;
align-self: flex-end;
} }
#setting-content { #setting-content {
display: none; display: none;
@ -225,7 +239,7 @@
@media (max-width: 799px) { @media (max-width: 799px) {
#chat-area { #chat-area {
height: 500px; height: calc(100vh - 50px);
} }
#emoji-button { #emoji-button {
display: none; display: none;

File diff suppressed because one or more lines are too long

View file

@ -20,7 +20,6 @@
window.lock = false; window.lock = false;
window.lock_click_space = false; window.lock_click_space = false;
window.pushed_messages = new Set(); window.pushed_messages = new Set();
let isMobile = window.matchMedia("only screen and (max-width: 799px)").matches;
window.load_dynamic_update = function (last_msg) { window.load_dynamic_update = function (last_msg) {
var receiver = new EventReceiver( var receiver = new EventReceiver(
@ -71,25 +70,20 @@
<div id="chat-container"> <div id="chat-container">
<div id="chat-online" class="chat-right-panel sidebox"> <div id="chat-online" class="chat-right-panel sidebox">
<h3>
<i class="fa fa-users"></i>{{_('Online Users')}}
</h3>
<div id="chat-online-content"> <div id="chat-online-content">
<div id="search-container"> <div id="search-container">
<center> <form id="chat-search-form" name="form" action="{{ url('get_or_create_room') }}" method="post">
<form id="search-form" name="form" action="{{ url('get_or_create_room') }}" method="post">
{% csrf_token %} {% csrf_token %}
<input id="search-handle" type="text" name="search" <input id="search-handle" type="text" name="search"
placeholder="{{ _('Search by handle...') }}"> placeholder="{{ _('Search by handle...') }}">
</form> </form>
</center>
</div> </div>
<div id="chat-online-list"> <div id="chat-online-list">
{% include "chat/online_status.html" %} {% include "chat/online_status.html" %}
</div> </div>
</div> </div>
</div> </div>
<div id="chat-area" class="chat-left-panel" style="width:100%"> <div id="chat-area" class="chat-left-panel">
<div id="chat-info" style="height: 10%"> <div id="chat-info" style="height: 10%">
{% include 'chat/user_online_status.html' %} {% include 'chat/user_online_status.html' %}
</div> </div>

View file

@ -102,12 +102,22 @@
float: inherit; float: inherit;
} }
#search-container { #search-container {
margin-bottom: 0.4em; padding: 1em;
} }
#setting { #setting {
position: relative; position: relative;
} }
.status-user {
display: flex;
flex-direction: column;
min-width: 0;
flex-grow: 1;
}
.wrapline {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
@media (min-width: 800px) { @media (min-width: 800px) {
#page-container { #page-container {

View file

@ -5,6 +5,8 @@
"{{_('Admin')}}", "{{_('Admin')}}",
"{{_('Other')}}", "{{_('Other')}}",
]; ];
let isMobile = window.matchMedia("only screen and (max-width: 799px)").matches;
function load_next_page(last_id, refresh_html=false) { function load_next_page(last_id, refresh_html=false) {
var param = { var param = {
'last_id': last_id, 'last_id': last_id,
@ -82,6 +84,7 @@
register_toggle($(this)); register_toggle($(this));
}); });
register_click_space(); register_click_space();
color_selected_room();
} }
}) })
@ -207,6 +210,9 @@
} }
function add_message_from_template(body, tmp_id) { function add_message_from_template(body, tmp_id) {
if (window.room_id) {
$("#last_msg-" + window.room_id).html(body);
}
var html = message_template; var html = message_template;
html = html.replaceAll('$body', body).replaceAll('$id', tmp_id); html = html.replaceAll('$body', body).replaceAll('$id', tmp_id);
var $html = $(html); var $html = $(html);
@ -267,6 +273,11 @@
} }
} }
function color_selected_room() {
$(".status-row").removeClass("selected-status-row");
$("#click_space_" + window.other_user_id).addClass("selected-status-row");
}
function load_room(encrypted_user) { function load_room(encrypted_user) {
if (window.lock_click_space) return; if (window.lock_click_space) return;
@ -283,6 +294,7 @@
.done(function(data) { .done(function(data) {
window.room_id = data.room; window.room_id = data.room;
window.other_user_id = data.other_user_id; window.other_user_id = data.other_user_id;
color_selected_room();
callback(); callback();
}) })
.fail(function() { .fail(function() {
@ -292,6 +304,7 @@
else { else {
window.room_id = ''; window.room_id = '';
window.other_user_id = ''; window.other_user_id = '';
color_selected_room();
callback(); callback();
} }
window.lock_click_space = false; window.lock_click_space = false;
@ -494,7 +507,7 @@
var in_user_redirect = false; var in_user_redirect = false;
$('#search-handle').select2({ $('#search-handle').select2({
placeholder: '{{ _('Search by handle...') }}', placeholder: '<i class="fa fa-search"></i> {{ _('Search by handle...') }}',
ajax: { ajax: {
url: '{{ url('chat_user_search_select2_ajax') }}' url: '{{ url('chat_user_search_select2_ajax') }}'
}, },
@ -544,5 +557,6 @@
}) })
} }
register_setting(); register_setting();
color_selected_room();
}); });
</script> </script>

View file

@ -27,11 +27,18 @@
fill="{{'green' if user.is_online else 'red'}}"/> fill="{{'green' if user.is_online else 'red'}}"/>
</svg> </svg>
</div> </div>
<span style="padding-left:0.3em" class="username {{ user.user.css_class }}"> <div class="status-user">
<span class="username {{ user.user.css_class }} wrapline">
{{ user.user.username }} {{ user.user.username }}
</span> </span>
<span class="spacer"> {% if user.last_msg %}
<span class="unread-count" id="unread-count-{{user.user.id}}">{{user.unread_count if user.unread_count}}</span> <span class="status_last_message wrapline" {% if user.room %}id="last_msg-{{user.room}}"{% endif %}>
{{ user.last_msg }}
</span>
{% endif %}
</div>
<span class="unread-count" id="unread-count-{{user.user.id}}">
{{user.unread_count if user.unread_count}}
</span> </span>
</li> </li>
{% endfor %} {% endfor %}