diff --git a/pos-module-chat/modules/chat/public/assets/js/pos-chat.js b/pos-module-chat/modules/chat/public/assets/js/pos-chat.js
index e985fccc..254d3964 100644
--- a/pos-module-chat/modules/chat/public/assets/js/pos-chat.js
+++ b/pos-module-chat/modules/chat/public/assets/js/pos-chat.js
@@ -16,7 +16,7 @@ import consumer from 'pos-chat-consumer.js';
// purpose: handles sending and receiving messages as well as the inbox page
// ************************************************************************
-const chat = function(){
+window.pos.modules.chat = function(userSettings = {}){
// cache 'this' value not to be overwritten later
const module = this;
@@ -24,10 +24,11 @@ const chat = function(){
// purpose: settings that are being used across the module
// ------------------------------------------------------------------------
module.settings = {};
- // do you want to enable debug mode that logs to console (bool)
- module.settings.debug = false;
+ // unique id for the modules (string)
+ module.settings.id = 'pos-module-chat';
+
// the main container with the chat inbox (dom node)
- module.settings.inbox = document.querySelector('#pos-chat-inbox');
+ module.settings.inbox = userSettings.inbox || document.querySelector('#pos-chat-inbox');
// the input for typing new message (dom node)
module.settings.messageInput = document.querySelector('#chat-messageInput');
// the send button for new message (dom node)
@@ -73,7 +74,19 @@ const chat = function(){
// current page of messages (int)
module.settings.currentPage = 1;
// are there more pages (bool)
- module.settings.morePages = true
+ module.settings.morePages = true;
+
+ // stores all the conversation list related stuff (object)
+ module.settings.conversations = {};
+ // container for the conversations list (dom node)
+ module.settings.conversations.container = document.querySelector('#pos-chat-conversations');
+ // selctor for the button to load more conversations (string)
+ module.settings.conversations.loadMoreButtonSelector = '.pos-chat-conversations-more';
+ // current page of conversations (int)
+ module.settings.conversations.currentPage = 1;
+ // are there more pages of conversations (bool)
+ module.settings.conversations.morePages = document.querySelector(module.settings.conversations.loadMoreButtonSelector) ? true : false;
+
// the message that will appear when the connection is lost
module.settings.lostConnection = pos.translations.connectionError;
@@ -84,6 +97,98 @@ const chat = function(){
// instance of the toast notification shown when something fails
module.errorNotification = null;
+ // to enable debug mode (bool)
+ module.settings.debug = (userSettings?.debug) ? userSettings.debug : false;
+
+
+
+ // purpose: initializes the module
+ // ------------------------------------------------------------------------
+ module.init = () => {
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Initializing chat', module.settings.inbox);
+
+ // create subscription for the channel
+ module.createSubscription();
+
+ // scroll to bottom after loading the messages
+ scrollBottom();
+
+ // parse dates from BE to be in the same format as browser locale
+ module.parseDates();
+
+ let is_desktop = true;
+
+ if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
+ is_desktop = false;
+ }
+
+ // handling what will happen on pressing enter in the input
+ module.settings.messageInput.addEventListener('keypress', (event) => {
+ if(event.which == 13 && is_desktop && !event.shiftKey && module.settings.messageInput.value.trim()){
+ event.preventDefault();
+
+ module.sendMessage(module.settings.messageInput.value.trim());
+ setTimeout(() => {
+ module.settings.messageInput.value = '';
+ }, 100);
+ }
+ });
+
+ module.settings.messageInput.addEventListener("paste", (event) => {
+ event.preventDefault();
+ const text = event.clipboardData.getData("text/plain");
+ document.execCommand("insertHTML", false, text);
+ });
+
+ // handling send button click
+ module.settings.sendButton.addEventListener('click', () => {
+ if(module.settings.messageInput.value.trim()) {
+ module.sendMessage(module.settings.messageInput.value.trim());
+ setTimeout(() => {
+ module.settings.messageInput.value = '';
+ }, 100);
+ }
+ });
+
+ // load previous messages when user scrolls to top
+ let messagesListTimeout = '';
+ module.settings.messagesListContainer.addEventListener('scroll', () => {
+ if(module.settings.morePages === true){
+ clearTimeout(messagesListTimeout);
+ messagesListTimeout = setTimeout(() => {
+ if(module.settings.messagesListContainer.scrollTop === 0){
+ module.settings.currentPage = module.settings.currentPage + 1;
+ module.loadPage(module.settings.currentPage);
+ }
+ }, 300);
+ }
+ });
+
+ // load nex page of conversations when user scrolls to bottom
+ let conversationsListTimeout = '';
+ module.settings.conversations.container.addEventListener('scroll', () => {
+ if(module.settings.conversations.morePages === true){
+ clearTimeout(conversationsListTimeout);
+ conversationsListTimeout = setTimeout(() => {
+ if(module.settings.conversations.container.scrollTop === module.settings.conversations.container.scrollHeight - module.settings.conversations.container.clientHeight){
+ module.settings.conversations.currentPage = module.settings.conversations.currentPage + 1;
+ module.conversations.load(module.settings.conversations.currentPage);
+ }
+ }, 300);
+ }
+ });
+
+ if(module.settings.conversations.morePages){
+ pos.modules.debug(module.settings.debug, module.settings.id, 'More conversations available');
+ } else {
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Showing all conversations, no more pages available');
+ }
+
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Chat initialized', module.settings.inbox);
+
+ };
+
+
// purpose: escapes the html to a browser-safe string
// arguments: a html string to be escaped (string/html)
@@ -131,6 +236,8 @@ const chat = function(){
// appears on the channel (send or received), passess the message details
// ------------------------------------------------------------------------
module.createSubscription = () => {
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Creating subscription');
+
module.channel = consumer.subscriptions.create(
{
channel: 'conversate',
@@ -146,55 +253,43 @@ const chat = function(){
status: (module.settings.currentUserId == data.autor_id) ? 'sent' : 'received'
})
);
- //document.dispatchEvent(new CustomEvent('message', {detail: Object.assign(data, { status: (module.settings.currentUserId == data.autor_id) ? 'sent' : 'received'})}));
if(module.settings.debug){
if(data.status === 'received'){
- console.log('[pos-module-chat] Message received', data);
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Message received', data);
}
}
},
initialized: function(){
if(module.settings.debug){
- console.log('[pos-module-chat] Initialized');
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Subscription initialized');
}
},
connected: function(){
- if(module.settings.debug){
- console.log('[pos-module-chat] Connected')
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, `Connected to channel and joined the room ${module.conversationId}`);
module.settings.messageInput.disabled = false;
module.settings.messageInput.focus();
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Unlocked message input');
// remove the error notification when connected
if(module.errorNotification){
module.errorNotification.hide();
}
-
- if(module.settings.debug){
- console.log(`[pos-module-chat] Connected to channel and joined room ${module.conversationId}`);
- }
},
rejected: function(){
- console.log('rejected');
module.blocked();
- if(module.settings.debug){
- console.log('[pos-module-chat] The connection was rejected by the server');
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, `The connection was rejected by the server`);
},
disconnected: function(){
- console.log('disconnected')
module.blocked();
- if(module.settings.debug){
- console.log(`[pos-module-chat] You've been disconnected from the server`);
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, `Disconnected from the server`);
}
}
);
@@ -214,9 +309,7 @@ const chat = function(){
module.channel.send(Object.assign(messageData, { create: true }));
- if(module.settings.debug){
- console.log('[pos-module-chat] Message sent', messageData);
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Message sent', messageData);
};
@@ -253,15 +346,9 @@ const chat = function(){
module.settings.messagesList.append(messageHtml);
}
- // showMessage only ever runs for a freshly-arrived realtime message (pagination
- // renders its own markup), so always scroll to the bottom to reveal it - even when
- // an out-of-order burst inserted this node above a later one, the newest message
- // still sits at the bottom.
scrollBottom('smooth');
- if(module.settings.debug){
- console.log('[pos-module-chat] Message shown in chat');
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Message shown on page', messageData);
};
@@ -270,9 +357,7 @@ const chat = function(){
// items per page to get (int, default: 30)
// ------------------------------------------------------------------------
module.loadPage = (page = 1, perPage = 30) => {
- if(module.settings.debug){
- console.log('[pos-module-chat] Trying to load previous messages');
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Trying to load previous messages');
let secondOldestMessage = module.settings.messagesList.querySelector('li:nth-of-type(2)');
@@ -315,9 +400,7 @@ const chat = function(){
module.settings.morePages = false;
}
- if(module.settings.debug){
- console.log('[pos-module-chat] Previous messages loaded');
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Previous messages loaded');
})
.catch((error) => {
console.log(error);
@@ -331,9 +414,7 @@ const chat = function(){
module.settings.messagesListContainer.scrollTop = secondOldestMessage.offsetTop - module.settings.messagesListContainer.clientHeight;
}
- if(module.settings.debug){
- console.log('[pos-module-chat] Finished trying to load previous messages');
- }
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Finished loading previous messages');
});
};
@@ -346,6 +427,8 @@ const chat = function(){
'error',
window.pos.translations.chat.connectionError
);
+
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Blocked the chat due to error');
};
@@ -359,124 +442,60 @@ const chat = function(){
};
- // purpose: initializes the module
+ // conversations
// ------------------------------------------------------------------------
- module.init = () => {
- // create subscription for the channel
- module.createSubscription();
-
- // scroll to bottom after loading the messages
- scrollBottom();
-
- // parse dates from BE to be in the same format as browser locale
- module.parseDates();
+ module.conversations = {};
- let is_desktop = true;
- if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
- is_desktop = false;
- }
-
- // handling what will happen on pressing enter in the input
- module.settings.messageInput.addEventListener('keypress', (event) => {
- if(event.which == 13 && is_desktop && !event.shiftKey && module.settings.messageInput.value.trim()){
- event.preventDefault();
+ // purpose: loads next page of conversations
+ // ------------------------------------------------------------------------
+ module.conversations.load = (page = 1) => {
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Trying to load next page of conversations');
- module.sendMessage(module.settings.messageInput.value.trim());
- setTimeout(() => {
- module.settings.messageInput.value = '';
- }, 100);
+ // get the data
+ fetch(`/conversations.frame?page=${page}`)
+ .then(response => {
+ // parse it to JSON if valid
+ if(response.ok){
+ return response.text();
+ } else {
+ return Promise.reject(response);
}
- });
-
- module.settings.messageInput.addEventListener("paste", (event) => {
- event.preventDefault();
- const text = event.clipboardData.getData("text/plain");
- document.execCommand("insertHTML", false, text);
- });
+ })
+ .then((data) => {
+ // remove the 'load more' button for previous page
+ document.querySelector(module.settings.conversations.loadMoreButtonSelector)?.remove();
- // handling send button click
- module.settings.sendButton.addEventListener('click', () => {
- if(module.settings.messageInput.value.trim()) {
- module.sendMessage(module.settings.messageInput.value.trim());
- setTimeout(() => {
- module.settings.messageInput.value = '';
- }, 100);
- }
- });
+ module.settings.conversations.container.insertAdjacentHTML('beforeend', data);
- // Incoming messages are rendered directly from the channel's `received` callback
- // (see createSubscription -> showMessage). Recipients are notified of messages in
- // other conversations server-side, so there is no client-side notification send here.
+ pos.modules.debug(module.settings.debug, module.settings.id, `Conversations page ${page} loaded`, { data });
- // load previous messages when user scrolls to top
- let messagesListTimeout = '';
- module.settings.messagesListContainer.addEventListener('scroll', () => {
- if(module.settings.morePages === true){
- clearTimeout(messagesListTimeout);
- messagesListTimeout = setTimeout(() => {
- if(module.settings.messagesListContainer.scrollTop === 0){
- module.settings.currentPage = module.settings.currentPage + 1;
- module.loadPage(module.settings.currentPage);
- }
- }, 300);
+ // disable loading next pages if there is nothing left
+ if(!document.querySelector(module.settings.conversations.loadMoreButtonSelector)){
+ module.settings.conversations.morePages = false;
+ pos.modules.debug(module.settings.debug, module.settings.id, 'There are no more conversations to load, disabling infinite scroll');
}
+ })
+ .catch((error) => {
+ console.log(error);
+ error.json().then(data => console.log(data));
+ })
+ .finally(() => {
+ pos.modules.debug(module.settings.debug, module.settings.id, 'Finished loading previous conversations');
});
-
};
- module.init();
-
-};
-
-document.addEventListener('DOMContentLoaded', () => {
- if(document.querySelector('#chat-messagesList-container')){
- window.pos.modules.chat = new chat();
- }
-});
-
-
-// purpose: handles the behavior of 'send message' button
-// argumenst: configurable settings (object)
-// ************************************************************************
-const sendMessageButton = function(userSettings){
-
- // cache 'this' value not to be overwritten later
- const module = this;
-
-
- // purpose: settings that are being used across the module
- // ------------------------------------------------------------------------
- module.settings = {};
- // the 'send message' button (dom node)
- module.sendMessageButton = userSettings.sendMessageButton ? userSettings.sendMessageButton : document.querySelector('.chat-sendMessage');
- // purpose: blocks the button after first click to prevent
- // cloning the conversations to a single user
- // ------------------------------------------------------------------------
- module.preventDoubleClick = () => {
- module.sendMessageButton.addEventListener('click', () => {
- module.sendMessageButton.setAttribute('disabled', 'disabled');
- });
- };
-
-
- // purpose: initializes the module
- // ------------------------------------------------------------------------
- module.init = () => {
- module.preventDoubleClick();
- };
-
module.init();
};
document.addEventListener('DOMContentLoaded', () => {
- document.querySelectorAll('.chat-sendMessage').forEach((item) => {
- new sendMessageButton({
- sendMessageButton: item
+ if(document.querySelector('#pos-chat-inbox')){
+ window.pos.modules.active.chat = new window.pos.modules.chat({
+ inbox: document.querySelector('#pos-chat-inbox'),
});
- });
-});
+ }
+});
\ No newline at end of file
diff --git a/pos-module-chat/modules/chat/public/assets/style/pos-chat-inbox.css b/pos-module-chat/modules/chat/public/assets/style/pos-chat-inbox.css
index 7ba68826..2b2be644 100644
--- a/pos-module-chat/modules/chat/public/assets/style/pos-chat-inbox.css
+++ b/pos-module-chat/modules/chat/public/assets/style/pos-chat-inbox.css
@@ -117,7 +117,7 @@
transition: all .2s ease-in-out;
}
- .pos-chat-conversations:not(:has(.pos-chat-conversationCard:hover)) .pos-chat-conversationCard.active {
+ .pos-chat-conversations:not(:has(.pos-chat-conversationCard:hover)) .pos-chat-conversationCard-active {
anchor-name: --pos-chat-conversation-active;
}
@@ -165,6 +165,14 @@
white-space: nowrap;
}
+/* load more button */
+.pos-chat-conversations-more {
+ padding-block-start: var(--pos-padding-card);
+ display: none;
+
+ text-align: center;
+}
+
/* conversation
============================================================================ */
diff --git a/pos-module-chat/modules/chat/public/graphql/conversations/search.graphql b/pos-module-chat/modules/chat/public/graphql/conversations/search.graphql
index 2469f55a..c962a81d 100644
--- a/pos-module-chat/modules/chat/public/graphql/conversations/search.graphql
+++ b/pos-module-chat/modules/chat/public/graphql/conversations/search.graphql
@@ -21,6 +21,8 @@ query search(
sort: { updated_at: { order: DESC } }
) {
total_entries
+ current_page
+ has_next_page
results {
id
created_at
diff --git a/pos-module-chat/modules/chat/public/views/pages/api/conversations/show.json.liquid b/pos-module-chat/modules/chat/public/views/pages/api/conversations/show.json.liquid
new file mode 100644
index 00000000..673694b7
--- /dev/null
+++ b/pos-module-chat/modules/chat/public/views/pages/api/conversations/show.json.liquid
@@ -0,0 +1,26 @@
+---
+slug: api/chat/conversations
+method: get
+---
+
+{% doc %}
+ Get conversations for given participant
+
+ @param {string} page - page number for pagination
+ @param {number} [per_page] - items per page (default: 20)
+{% enddoc %}
+
+{% liquid
+ function current_profile = 'modules/user/helpers/current_profile'
+
+ # platformos-check-disable ConvertIncludeToRender, UnreachableCode, DeprecatedTag, MetadataParamsCheck
+ include 'modules/user/helpers/can_do_or_unauthorized', requester: current_profile, do: 'chat.inbox', entity: null, access_callback: null, redirect_anonymous_to_login: null, forbidden_partial: null
+ # platformos-check-enable ConvertIncludeToRender, UnreachableCode, DeprecatedTag, MetadataParamsCheck
+
+ assign page = context.params.page | to_positive_integer: 0
+ assign per_page = context.params.per_page | to_positive_integer: 4
+
+ function conversations = 'modules/chat/queries/conversations/search_by_participant', page: page, limit: per_page, participant_id: current_profile.id
+
+ render 'modules/chat/json/conversations/show', conversations: conversations
+%}
diff --git a/pos-module-chat/modules/chat/public/views/pages/api/messages/show.json.liquid b/pos-module-chat/modules/chat/public/views/pages/api/messages/show.json.liquid
index 2a30a516..b8379b51 100644
--- a/pos-module-chat/modules/chat/public/views/pages/api/messages/show.json.liquid
+++ b/pos-module-chat/modules/chat/public/views/pages/api/messages/show.json.liquid
@@ -3,14 +3,13 @@ slug: api/chat/messages
method: get
---
-{% comment %}
+{% doc %}
Get messages for given conversation
- Params:
- - conversation id (string, required)
- - page number for pagination (int, default: 1)
- - items per page (int, default: 30)
-{% endcomment %}
+ @param {string} conversation_id - the ID of the conversation
+ @param {string} page - page number for pagination
+ @param {number} [per_page] - items per page (default: 30)
+{% enddoc %}
{% liquid
function current_profile = 'modules/user/helpers/current_profile'
@@ -20,10 +19,10 @@ method: get
# platformos-check-enable ConvertIncludeToRender, UnreachableCode, DeprecatedTag, MetadataParamsCheck
assign conversation_id = context.params.conversation_id
- assign page = context.params.page | plus: 0 | default: 1
- assign limit = context.params.per_page | plus: 0 | default: 30
+ assign page = context.params.page | to_positive_integer: 1
+ assign per_page = context.params.per_page | to_positive_integer: 30
- function messages = 'modules/chat/queries/messages/search_by_participant', conversation_id: conversation_id, page: page, limit: limit, participant_id: current_profile.id
+ function messages = 'modules/chat/queries/messages/search_by_participant', conversation_id: conversation_id, page: page, limit: per_page, participant_id: current_profile.id
- render 'modules/chat/theme/json/messages/show', messages: messages
+ render 'modules/chat/json/messages/show', messages: messages
%}
diff --git a/pos-module-chat/modules/chat/public/views/pages/conversations.frame.liquid b/pos-module-chat/modules/chat/public/views/pages/conversations.frame.liquid
new file mode 100644
index 00000000..2bd3be51
--- /dev/null
+++ b/pos-module-chat/modules/chat/public/views/pages/conversations.frame.liquid
@@ -0,0 +1,26 @@
+---
+layout: ''
+---
+
+{% doc %}
+ Get conversations for given participant and return them in HTML
+
+ @param {string} page - page number for pagination
+ @param {number} [per_page] - items per page (default: 20)
+{% enddoc %}
+
+
+{% liquid
+ function current_profile = 'modules/user/helpers/current_profile'
+
+ # platformos-check-disable ConvertIncludeToRender, UnreachableCode, DeprecatedTag, MetadataParamsCheck
+ include 'modules/user/helpers/can_do_or_unauthorized', requester: current_profile, do: 'chat.inbox', entity: null, access_callback: null, redirect_anonymous_to_login: null, forbidden_partial: null
+ # platformos-check-enable ConvertIncludeToRender, UnreachableCode, DeprecatedTag, MetadataParamsCheck
+
+ assign page = context.params.page | to_positive_integer: 0
+ assign per_page = context.params.per_page | to_positive_integer: 30
+
+ function conversations = 'modules/chat/queries/conversations/search_by_participant', page: page, limit: per_page, participant_id: current_profile.id
+
+ render 'modules/chat/conversations', conversations: conversations, current: null, current_profile: current_profile
+%}
diff --git a/pos-module-chat/modules/chat/public/views/pages/inbox.html.liquid b/pos-module-chat/modules/chat/public/views/pages/inbox.liquid
similarity index 99%
rename from pos-module-chat/modules/chat/public/views/pages/inbox.html.liquid
rename to pos-module-chat/modules/chat/public/views/pages/inbox.liquid
index 8f7e575c..b13e1b6a 100644
--- a/pos-module-chat/modules/chat/public/views/pages/inbox.html.liquid
+++ b/pos-module-chat/modules/chat/public/views/pages/inbox.liquid
@@ -28,7 +28,7 @@ slug: inbox
endfor
endif
endif
- function conversations = 'modules/chat/queries/conversations/search_by_participant', participant_id: current_profile.id, limit: 20, page: 1
+ function conversations = 'modules/chat/queries/conversations/search_by_participant', participant_id: current_profile.id, limit: 30, page: 1
if conversations.total_entries > 0
if current_conversation and current_conversation.participants == blank
diff --git a/pos-module-chat/modules/chat/public/views/partials/conversation.liquid b/pos-module-chat/modules/chat/public/views/partials/conversation.liquid
index 37d0860b..1b47e94a 100644
--- a/pos-module-chat/modules/chat/public/views/partials/conversation.liquid
+++ b/pos-module-chat/modules/chat/public/views/partials/conversation.liquid
@@ -1,15 +1,17 @@
{% doc %}
- @param {string} id - Conversation ID
- @param {boolean} current - Whether this is the active conversation
- @param {string} name - Display name of the participant
- @param {string} image_src - Avatar image URL
- @param {string} last_message - Last message text
- @param {string} [timezone] - Timezone for date formatting
+ @param {number} id - conversation ID
+ @param {boolean} current - whether this is the active conversation
+ @param {string} name - display name of the participant
+ @param {string} image_src - avatar image URL
+ @param {string} last_message - last message text
+ @param {string} [timezone] - timezone for date formatting
{% enddoc %}
+
+
{% if current == false %}
-
+
{% endif %}
-