This commit is contained in:
nnduc
2026-10-24 14:03:52 +07:00
commit 4fe046de58
4790 changed files with 311146 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
{% if muc %}
<nav class="spin" id="{{ jid|cleanupId }}-nav"></nav>
<div id="chat_conference_call" class="spin"></div>
{% endif %}
<header id="{{ jid|cleanupId }}-header">
{{ header|safe }}
</header>
<div id="{{ jid|cleanupId }}-discussion" class="contained {% if muc %}muc{% endif %}">
<section id="{{ jid|cleanupId }}-messages">
<div class="placeholder first_messages">
<i class="material-symbols fill">waving_hand</i>
<h1>{{ __('chat.first_messages_title') }}</h1>
{% if not muc %}
<h4 style="margin-bottom: 1rem;">{{ __('chat.first_messages_text') }}</h4>
<h4>
<button class="button color" onclick="Notifications_ajaxAddAsk('{{ jid|echapJS }}')">
<i class="material-symbols">add</i> {{ __('chat.first_messages_add') }}
</button>
<button class="button flat" onclick="ChatActions_ajaxBlock('{{ jid|echapJS }}'); Notifications_ajaxRefuse('{{ jid|echapJS }}');">
{{ __('chat.first_messages_block') }}
</button>
</h4>
{% endif %}
</div>
<ul class="list spin conversation" id="{{ jid|cleanupId }}-conversation"></ul>
<ul class="list state" id="{{ jid|cleanupId }}-state"></ul>
<div class="placeholder empty">
<i class="material-symbols fill">chat</i>
<h1>{{ __('chat.new_title') }}</h1>
<h4>{{ __('chat.new_text') }}</h4>
<h4>{{ __('message.edit_help') }}</h4>
<h4>{{ __('message.emoji_help') }}</h4>
</div>
</section>
</div>
<div class="chat_box {% if visitor %}disabled{% endif %}">
<button id="scroll_now" class="button action color small" onclick="Chat_ajaxClearAndGetMessages('{{ jid|echapJS }}', {% if muc %}true{% endif %})">
<i class="material-symbols">clock_arrow_down</i>
</button>
<button id="scroll_down" class="button action color transparent small" onclick="Chat.scrollTotally()">
<i class="material-symbols">expand_more</i>
</button>
<ul class="list fill">
<div id="reply"></div>
<ul class="list middle" id="embed"></ul>
<div id="dictaphone"></div>
<li class="emojis"></li>
<li class="main">
<span class="primary icon gray active"
title="{{ __('emojisconfig.title') }}"
onclick="Stickers_ajaxReaction(null)">
<a href="#" onclick="listIconClick(event);">
<i class="material-symbols">emoji_emotions</i>
</a>
</span>
{% if upload %}
<span class="emojis control icon gray active"
id="dictaphone_toggle"
title="{{ __('dictaphone.name') }}"
onclick="Dictaphone.toggle()">
<a href="#" onclick="listIconClick(event);">
<i class="material-symbols">mic</i>
</a>
</span>
<span class="attach control icon" onclick="Chat.toggleAttach()">
<a href="#" onclick="listIconClick(event);">
<i class="material-symbols">add_circle</i>
</a>
</span>
<ul class="list active actions">
<li onclick="Chat.toggleAttach(); Snap.init()">
<span class="control icon gray">
<i class="material-symbols">camera_alt</i>
</span>
<div>
<p class="line">Snap</p>
</div>
</li>
<li onclick="Chat.toggleAttach(); Draw_ajaxHttpGet()">
<span class="control icon gray">
<i class="material-symbols">gesture</i>
</span>
<div>
<p class="line">{{ __('draw.title') }}</p>
</div>
</li>
<li onclick="Chat.toggleAttach(); Upload_ajaxGetPanel()">
<span class="control icon gray">
<i class="material-symbols">attach_file</i>
</span>
<div>
<p class="line">{{ __('upload.title') }}</p>
</div>
</li>
</ul>
{% endif %}
<span title="{{ __('button.submit') }}"
class="send control icon gray"
onclick="Chat.sendMessage()">
<a href="#" onclick="listIconClick(event);">
<i class="material-symbols">send</i>
</a>
</span>
<form>
<div>
<textarea
dir="auto"
rows="1"
id="chat_textarea"
data-jid="{{ jid }}"
data-muc="{% if muc %}true{% endif %}"
data-muc-group="{% if group %}true{% endif %}"
{% if visitor %}
placeholder="{{ __('message.visitor_help') }}"
{% elif rand == 1 %}
placeholder="{{ __('message.emoji_help') }}"
{% else %}
placeholder="{{ __('chat.placeholder') }}"
{% endif %}
{% if visitor %}disabled{% endif %}
></textarea>
<span class="control icon encrypted" title="{{ __('omemo.encrypted') }}"
onclick="ChatOmemo.disableContactState('{{ jid|echapJS }}', {% if muc %}true{% else %}false{% endif %})">
<i class="material-symbols fill">lock</i>
</span>
<span class="control icon encrypted_disabled" title="{{ __('omemo.encrypted_disabled') }}"
onclick="ChatOmemo.enableContactState('{{ jid|echapJS }}', {% if muc %}true{% else %}false{% endif %})">
<i class="material-symbols">no_encryption</i>
</span>
<span class="control icon encrypted_loading" title="{{ __('omemo.encrypted_loading') }}"
onclick="ChatOmemo.disableContactState('{{ jid|echapJS }}', {% if muc %}true{% else %}false{% endif %})">
<i class="material-symbols">lock_clock</i>
</span>
</div>
</form>
</li>
</ul>
</div>
+28
View File
@@ -0,0 +1,28 @@
<li {% if mine %}class="oppose"{% endif %}>
{% if muc %}
<span class="primary icon bubble" onclick="Chat.quoteMUC(this.dataset.resource, true);"></span>
<span class="control icon bubble"></span>
{% else %}
<span class="primary icon bubble">
{% if not mine %}
<a href="#" onclick="ChatActions_ajaxGetContact('{{ contact.id|echapJS }}')">
{% endif %}
<img src="{{ contact.picture }}" data-name="{{ contact.truename }}">
{% if not mine %}</a>{% endif %}
</span>
{% endif %}
<div class="bubble">
<div class="message">
<p></p>
<ul class="reactions"></ul>
<span class="info"></span>
<span class="reaction" title="{{ __('message.react') }}">
<i class="material-symbols">add_reaction</i>
</span>
<span class="reply">
<i class="material-symbols">reply</i>
</span>
</div>
</div>
</li>
+16
View File
@@ -0,0 +1,16 @@
<section>
<h3>{{ __('chat.clear_history') }}</h3>
<br />
<h4 class="gray">{{ __('chat.clear_history_text', count) }}</h4>
</section>
<footer>
<button onclick="Dialog_ajaxClear()" class="button flat">
{{ __('button.cancel') }}
</button>
<button
name="submit"
class="button color red"
onclick="Chat_ajaxClearHistoryConfirm('{{ jid|echapJS }}'); Dialog_ajaxClear()">
{{ __('button.delete') }}
</button>
</footer>
+15
View File
@@ -0,0 +1,15 @@
<li>
<span class="primary icon bubble color transparent">
{% if not list %}
<img src="{{ contact.picture }}">
{% else %}
<i class="material-symbols tiny">edit</i>
{% endif %}
</span>
<div class="bubble">
<div class="message" title="{{ __('message.composing') }}">
<span class="dots"></span>
<p>{% if list %}{{ list }}{% endif %}</p>
</div>
</div>
</li>
+5
View File
@@ -0,0 +1,5 @@
<li class="date">
<div>
<p>Date</p>
</div>
</li>
+54
View File
@@ -0,0 +1,54 @@
<li class="block">
{% if image %}
<span class="primary icon thumb active color {{ color }}"
onclick="event.stopPropagation(); Preview_ajaxHttpShow('{{ url.image|echapJS }}')"
style="background-image: url({{ image }})">
<i class="material-symbols">image</i>
</span>
{% else %}
<span class="primary icon bubble gray">
{% if icon %}
<img src="{{ icon }}" onerror="this.remove()"/>
{% else %}
<i class="material-symbols">link</i>
{% endif %}
</span>
{% endif %}
<span class="control icon gray active" onclick="event.stopPropagation(); MovimUtils.openInNew('{{ url.url|echapJS }}')">
<i class="material-symbols">open_in_new</i>
</span>
<div>
{% if url.type == 'video' or url.type == 'audio' %}
<p class="line">
{% if url.type == 'audio' %}
<i class="material-symbols">equalizer</i> {{ __('chats.audio') }}
{% else %}
<i class="material-symbols">local_movies</i> {{ __('chats.video') }}
{% endif %}
{% if size %}
<span class="second">•</span>
<span class="second">{{ size }}</span>
{% endif %}
</p>
{% elif url.title %}
<p class="line two" title="{{ url.title }}">
{{ url.title }}
</p>
{% endif %}
<p class="line two" title="{% if url.description %}{{ url.description }}{% endif %}">
{% if url.provider_name %}
{{ url.provider_name }}
{% endif %}
{% if url.author_name %}
<span class="second">•</span>
<span class="second">{{ url.author_name }}</span>
{% endif %}
{% if url.description %}
<span class="second">•</span>
<span class="second">{{ url.description }}</span>
{% endif %}
</p>
</div>
</li>
+40
View File
@@ -0,0 +1,40 @@
<div class="placeholder" aria-hidden="true">
<i class="material-symbols fill">forum</i>
</div>
<div id="chat_frequent" aria-label="{{ __('chat.frequent') }}">
<ul class="list flex fifth card shadow compact middle active">
{% if top %}
<li class="subheader">
<div>
<p>{{ __('chat.frequent') }}</p>
</div>
</li>
{% for value in top %}
<li class="block"
onclick="Search.chat('{{ value.jid|echapJS }}', false)">
<img class="main" src="{{ value.picture }}">
<span class="primary icon bubble {% if value.presence %}status {{ value.presence }}{% endif %}">
<a href="#" onclick="listIconClick(event)">
<img src="{{ value.picture }}">
</a>
</span>
<div>
<p class="line" title="{{ value.truename }}">
{{ value.truename }}
</p>
{% if value.status %}
<p class="line">{{ value.status }}</p>
{% else %}
<p class="line">{{ value.jid }}</p>
{% endif %}
</div>
</li>
{% endfor %}
{% endif %}
</ul>
</div>
<br aria-hidden="true"/>
<br aria-hidden="true"/>
+11
View File
@@ -0,0 +1,11 @@
<li class="block">
<span class="primary icon bubble gray">
<i class="material-symbols fill">{{ file.type|mimeToIcon }}</i>
</span>
<div>
<p class="line"><a href="{{ file.url }}" target="_blank" rel="noopener noreferrer">{{ file.name }}</a></p>
{% if file.cleansize %}
<p>{{ file.cleansize }} · {{ file.type|mimeToLabel }}</p>
{% endif %}
</div>
</li>
+305
View File
@@ -0,0 +1,305 @@
{% if muc %}
<ul class="list middle">
<li>
<span class="primary icon active on_mobile_after"
id="chatheadercounter" onclick="Chat.get()"
{% if counter > 0 %}data-counter="{{ counter }}"{% endif %}>
<i class="material-symbols">arrow_back</i>
</span>
<span class="primary icon bubble active {% if not conference.connected %}disabled{% endif %}"
onclick="RoomsUtils_ajaxGetDrawer('{{ jid|echapJS }}')">
<img src="{{ conference.picture }}">
{% if conference.connected %}
<span class="counter alt" {% if conference.group or conference.count < 10 %}data-mucreceipts="true"{% endif %}>
{% if conference.count > 99 %}99+{% else %}{{ conference.count }}{% endif %}
</span>
{% endif %}
</span>
<span class="control icon active" onclick="ChatActions_ajaxShowSearchDialog('{{ jid|echapJS }}', true)">
<i class="material-symbols">manage_search</i>
</span>
{% if conference.group and conference.connected and conference.muji.count == 0 %}
<span class="control icon active divided {% if in_call %}disabled{% endif %}" onclick="Visio_ajaxGetMujiLobby('{{ jid|echapJS }}', true, false);">
<i class="material-symbols">call</i>
</span>
<span class="control icon active {% if in_call %}disabled{% endif %}" onclick="Visio_ajaxGetMujiLobby('{{ jid|echapJS }}', true, true);">
<i class="material-symbols">videocam</i>
</span>
{% endif %}
<span
class="control icon show_context_menu active {% if not conference.connected %}disabled{% endif %}"
onclick="MovimTpl.showContextMenu()">
<i class="material-symbols">more_vert</i>
</span>
<div>
{% if conference.muji.count > 0 %}
{% if conference.muji.joined %}
<button class="button oppose color red"
onclick="Visio_ajaxLeaveMuji('{{ jid|echapJS }}'); this.classList.add('disabled')">
<i class="material-symbols blink">call_end</i>
</button>
{% else %}
<button class="button oppose color blue {% if in_call %}disabled{% endif %}"
onclick="Visio_ajaxJoinMuji('{{ jid|echapJS }}', {% if conference.muji.video %}true{% else %}false{% endif %});">
<i class="material-symbols">call</i> {{ conference.muji.count }}
</button>
{% endif %}
{% endif %}
<p title="{{ jid }}" class="line active" onclick="RoomsUtils_ajaxGetDrawer('{{ jid|echapJS }}')">
{{ conference.title }}
{% if conference.notify == 0 %}
<span class="second" title="{{ __('room.notify_never') }}">
<i class="material-symbols">notifications_off</i>
</span>
{% elif conference.notify == 2 %}
<span class="second" title="{{ __('room.notify_always') }}">
<i class="material-symbols">notifications_active</i>
</span>
{% endif %}
<span class="second">
{% if conference.group %}
<i class="material-symbols">people_alt</i>
{% else %}
<i class="material-symbols">wifi_tethering</i>
{% endif %}
</span>
{% if conference.group and conference.subject %}
<span class="second" title="{{ conference.subject }}">
{{ conference.subject }}
</span>
{% endif %}
</p>
<p class="line active">
{% if conference.muji.count > 0 %}
{% if conference.muji.joined %}
<i class="material-symbols icon green blink">call</i>
{{ __('visio.joined_call') }}
{% else %}
<i class="material-symbols icon blue blink">call</i>
{{ __('visio.in_call') }}
{% endif %}
<span class="second">
<i class="material-symbols">schedule</i>
{{ conference.muji.since }}
-
{{ conference.muji.count }}
<i class="material-symbols">people</i>
</span>
•
{% endif %}
{% if not conference.connected %}
{{ __('button.connecting') }}…
{% elif conference.group %}
{% for value in conference.others %}
<span onclick="Chat.quoteMUC('{{ value.nick|echapJS }}', true);" class="icon bubble tiny">
<img src="{{ value.picture }}">
</span><span onclick="Chat.quoteMUC('{{ value.nick|echapJS }}', true);">{{ value.nick }}</span>
{% endfor %}
{% elif conference.subject %}
<span onclick="RoomsUtils_ajaxGetDrawer('{{ jid|echapJS }}')">{{ conference.subject }}</span>
{% endif %}
</p>
</div>
</li>
</ul>
<ul class="list context_menu thin active">
{% if conference.role == 'moderator' or conference.affiliation == 'owner' %}
<li class="subheader">
<div>
<p class="line">{{ __('page.administration') }}</p>
</div>
</li>
{% endif %}
{% if conference.affiliation == 'owner' %}
<li onclick="RoomsUtils_ajaxGetConfig('{{ jid|echapJS }}')">
<span class="primary icon gray">
<a href="#" onclick="listIconClick(event)">
<i class="material-symbols">settings</i>
</a>
</span>
<div>
<p>{{ __('chatrooms.config') }}</p>
</div>
</li>
{% endif %}
{% if conference.role == 'moderator' %}
<li onclick="RoomsUtils_ajaxGetSubject('{{ jid|echapJS }}')">
<span class="primary icon gray">
<a href="#" onclick="listIconClick(event)">
<i class="material-symbols">short_text</i>
</a>
</span>
<div>
<p>{{ __('chatrooms.subject') }}</p>
</div>
</li>
{% endif %}
{% if conference.role == 'moderator' or conference.affiliation == 'owner' %}
<hr />
{% endif %}
<li onclick="RoomsUtils_ajaxAdd('{{ jid|echapJS }}');">
<span class="primary icon gray">
<i class="material-symbols">edit</i>
</span>
<div>
<p>{{ __('button.edit') }}</p>
</div>
</li>
<li onclick="RoomsUtils_ajaxAskInvite('{{ jid|echapJS }}');">
<span class="primary icon gray">
<i class="material-symbols">person_add</i>
</span>
<div>
<p>{{ __('room.invite') }}</p>
</div>
</li>
<li onclick="Chat_ajaxClearHistory('{{ jid|echapJS }}')">
<span class="primary icon gray">
<i class="material-symbols">delete_sweep</i>
</span>
<div>
<p class="line">{{ __('chat.clear_history') }}</p>
</div>
</li>
<li onclick="Rooms_ajaxExit('{{ jid|echapJS }}');">
<span class="primary icon gray">
<i class="material-symbols">logout</i>
</span>
<div>
<p>{{ __('status.disconnect') }}</p>
</div>
</li>
</ul>
{% else %}
<ul class="list middle">
<li id="chat_header">
<span class="primary icon active on_mobile_after" id="chatheadercounter" onclick="Chat.get()"
{% if counter > 0 %}data-counter="{{ counter }}"{% endif %}>
<i class="material-symbols">arrow_back</i>
</span>
<span class="primary icon bubble active {% if contact.presence %}status {{ contact.presence }}{% endif %}"
onclick="ChatActions_ajaxGetContact('{{ contact.id|echapJS }}')">
<img src="{{ contact.picture }}">
</span>
<span class="control icon active" onclick="ChatActions_ajaxShowSearchDialog('{{ jid|echapJS }}')">
<i class="material-symbols">manage_search</i>
</span>
{% if not in_call %}
{% if call_audio %}
<span title="{{ __('button.audio_call') }}" class="control icon active divided"
onclick="Visio_ajaxGetLobby('{{ call_audio|echapJS }}', true);">
<i class="material-symbols">phone</i>
</span>
{% endif %}
{% if call_video %}
<span title="{{ __('button.video_call') }}" class="control icon active"
onclick="Visio_ajaxGetLobby('{{ call_video|echapJS }}', true, true);">
<i class="material-symbols">videocam</i>
</span>
{% endif %}
{% endif %}
<span class="control icon show_context_menu active"
onclick="MovimTpl.showContextMenu()">
<i class="material-symbols">more_vert</i>
</span>
<div>
{% if in_call %}
<button class="button oppose color red" onclick="Visio_ajaxRemoteGoodbye()">
<i class="material-symbols icon">call_end</i>
</button>
{% endif %}
<p class="line active" onclick="ChatActions_ajaxGetContact('{{ contact.id|echapJS }}')">
{{ contact.truename }}
{% if contact.blocked %}
<span class="tag color red">{{ __('blocked.title') }}</span>
{% endif %}
{% if omemo %}
<span class="second"><i class="material-symbols">lock</i></span>
{% endif %}
</p>
<p class="line active" onclick="ChatActions_ajaxGetContact('{{ contact.id|echapJS }}')">
{% if in_call %}
<i class="material-symbols icon green blink">phone_in_talk</i>
{{ __('visio.in_call') }} •
{% endif %}
{% if contact.seen %}
{{ __('last.title') }} {{ contact.seen }}
{% elif contact.status %}
{{ contact.status }}
{% elif contact.presence %}
{{ presencetext }}
{% else %}
{{ contact.id }}
{% endif %}
</p>
</div>
</li>
</ul>
<ul class="list context_menu active">
<li onclick="ChatActions_ajaxGetContact('{{ contact.id|echapJS }}')">
<span class="primary icon gray">
<i class="material-symbols">person</i>
</span>
<div>
<p class="line">{{ __('chat.profile') }}</p>
</div>
</li>
<li onclick="Chat_ajaxClearHistory('{{ contact.id|echapJS }}')">
<span class="primary icon gray">
<i class="material-symbols">delete_sweep</i>
</span>
<div>
<p class="line">{{ __('chat.clear_history') }}</p>
</div>
</li>
<hr />
{% if contact.blocked %}
<li onclick="ChatActions_ajaxUnblock('{{ contact.id|echapJS }}')">
<span class="primary icon gray">
<i class="material-symbols">check_circle</i>
</span>
<div>
<p class="line">{{ __('blocked.unblock_account') }}</p>
</div>
</li>
{% else %}
<li onclick="ChatActions_ajaxBlock('{{ contact.id|echapJS }}'); Notifications_ajaxRefuse('{{ contact.id|echapJS }}');">
<span class="primary icon gray">
<i class="material-symbols">block</i>
</span>
<div>
<p class="line">{{ __('blocked.block_account') }}</p>
</div>
</li>
{% endif %}
<li onclick="Chats_ajaxClose('{{ jid|echapJS }}', true);">
<span class="primary icon gray">
<i class="material-symbols">close</i>
</span>
<div>
<p class="line">{{ __('button.close') }}</p>
</div>
</li>
</ul>
{% endif %}
+1
View File
@@ -0,0 +1 @@
<i class="material-symbols">outgoing_mail</i> {{ __('message.invitation') }} <a href="#" onclick="Chat.getRoom('{{ room|echapJS }}')">{{ room }}</a>{% if reason %} — {{ reason }}{% endif %}
+7
View File
@@ -0,0 +1,7 @@
{% if type == 'jingle_incoming' %}<i class="material-symbols icon green">call_received</i> {{ __('chat.jingle_incoming') }}
{% elif type == 'jingle_outgoing' %}<i class="material-symbols icon blue">call_made</i> {{ __('chat.jingle_outgoing') }}
{% elif type == 'jingle_reject' %}<i class="material-symbols icon orange">phone_missed</i> {{ __('chat.jingle_reject') }}
{% elif type == 'jingle_retract' %}<i class="material-symbols icon gray">phone_missed</i> {{ __('chat.jingle_retract') }}
{% elif type == 'jingle_finish' %}<i class="material-symbols icon red">phone_disabled</i> {{ __('chat.jingle_end') }}
{% else %}<i class="material-symbols icon red">call_end</i> {{ __('chat.jingle_end') }}{% if duration %} • {{ duration }}{% endif %}
{% endif %}
+14
View File
@@ -0,0 +1,14 @@
{% for r in reactions %}
<li {% if muc %}title="{{ r.from|implodeCsv }}"{% endif %}
{% if r.mine %}class="reacted"{% endif %}
onclick="Chat_ajaxHttpDaemonSendReaction('{{ mid }}', '{{ r.emoji|echapJS }}')">
{{ r.emoji|addEmojis(true) }}
{{ r.from|length }}
</li>
{% endfor %}
{% if reactions %}
<li onclick="Stickers_ajaxReaction('{{ mid }}')" title="{{ __('message.react') }}">
<i class="material-symbols">add_reaction</i>
</li>
{% endif %}
+38
View File
@@ -0,0 +1,38 @@
<div data-mid="{{ message.mid }}">
<ul class="list">
<li>
<span class="primary icon">
<i class="material-symbols">reply</i>
</span>
<span class="control icon active toggleable" onclick="Chat_ajaxClearReply()">
<i class="material-symbols">close</i>
</span>
<div class="parent">
{% if message.type == 'groupchat' %}
<p class="line from">
<span class="resource {{ color }}">{{ message.resource }}</span>
</p>
{% else %}
<p class="line from">{{ from_name }}</p>
{% endif %}
{% if file %}
<p class="line">
{% if file.picture %}
<i class="material-symbols">image</i> {{ __('chats.picture') }}
{% elif file.audio %}
<i class="material-symbols">equalizer</i> {{ __('chats.audio') }}
{% elif file.video %}
<i class="material-symbols">local_movies</i> {{ __('chats.video') }}
{% else %}
<i class="material-symbols">insert_drive_file</i> {{ __('avatar.file') }}
{% endif %}
</p>
{% elif message.encrypted %}
<p class="line"><i class="material-symbols">lock</i> {{ __('message.encrypted') }}</p>
{% else %}
<p class="line">{{ message.body }}</p>
{% endif %}
</div>
</li>
</ul>
</div>
+12
View File
@@ -0,0 +1,12 @@
<ul class="list thin active" id="room_nav_members">
{{ contactsHtml|safe }}
</ul>
<ul class="list thin" id="room_nav_members_search">
<li class="search">
<form name="search" onsubmit="return false;">
<div>
<input name="keyword" autocomplete="off" oninput="Chat.searchMembers(this.value)" type="text">
</div>
</form>
</li>
</ul>
+1
View File
@@ -0,0 +1 @@
<li class="separator"></li>
+1
View File
@@ -0,0 +1 @@
<div id="chat_widget" class="spin"></div>