diff --git a/pos_chatter/__init__.py b/pos_chatter/__init__.py index 25acf6fe2..694a629d3 100644 --- a/pos_chatter/__init__.py +++ b/pos_chatter/__init__.py @@ -19,3 +19,4 @@ # If not, see . ################################################################################ from . import controllers +from . import models diff --git a/pos_chatter/controllers/systray.py b/pos_chatter/controllers/systray.py index fbda88b3b..b81da9f1c 100644 --- a/pos_chatter/controllers/systray.py +++ b/pos_chatter/controllers/systray.py @@ -18,6 +18,8 @@ # (AGPL v3) along with this program. # If not, see . ################################################################################ +from pyexpat.errors import messages + from odoo import http from odoo.http import request @@ -38,21 +40,31 @@ class SystrayController(http.Controller): @http.route('/pos_systray/message_data', auth='public', type='json') def get_data_pos_systray(self): - """ - Summary: - Getting data to the Message chatter list. - Return: - it contains details about chatter list. - """ - return [{'id': mail_channel_id.id, - 'type': mail_channel_id.channel_type, - 'name': mail_channel_id.name, - 'message_body': request.env['mail.message'].search( - [('model', '=', 'discuss.channel'), - ('res_id', '=', mail_channel_id.id)], limit=1).body - } for mail_channel_id in request.env['discuss.channel'].search([]) - for partner_id in mail_channel_id.channel_partner_ids - if partner_id.id == request.env.user.partner_id.id] + channels = request.env['discuss.channel'].search([]) + partner_id = request.env.user.partner_id.id + + data = [] + for mail_channel in channels: + if partner_id in mail_channel.channel_partner_ids.ids: + message = request.env['mail.message'].search( + [('model', '=', 'discuss.channel'), + ('res_id', '=', mail_channel.id)], + limit=1, order="id desc" + ) + messages = request.env['mail.message'].search_count( + [('model', '=', 'discuss.channel'), + ('res_id', '=', mail_channel.id), + ('is_read', '=', False)] + ) + data.append({ + 'message_id' : message.id, + 'id': mail_channel.id, + 'type': mail_channel.channel_type, + 'name': mail_channel.name, + 'message_body': message.body if message else "", + 'count': messages + }) + return data @http.route('/pos_systray/chat_message', auth='public', type='json') def get_data_chat_box(self, **kw): @@ -105,6 +117,7 @@ class SystrayController(http.Controller): 'model': 'discuss.channel', 'res_id': int(data['res_id']), 'message_type': 'comment', + 'is_read':True, 'author_id': request.env.user.partner_id.id }) return True diff --git a/pos_chatter/models/__init__.py b/pos_chatter/models/__init__.py new file mode 100644 index 000000000..a2bc21ba5 --- /dev/null +++ b/pos_chatter/models/__init__.py @@ -0,0 +1 @@ +from . import mail_message diff --git a/pos_chatter/models/mail_message.py b/pos_chatter/models/mail_message.py new file mode 100644 index 000000000..d4e51d2cc --- /dev/null +++ b/pos_chatter/models/mail_message.py @@ -0,0 +1,29 @@ +from odoo import models, fields , api + + +class MailMessage(models.Model): + _inherit = "mail.message" + + is_read = fields.Boolean(string="Read", default=False) + + @api.model + def compute_read_message(self, datas): + print(datas) + try: + messages = self.env['mail.message'].search([ + ('model', '=', 'discuss.channel'), + ('res_id', '=', datas), + ('is_read', '=', False) + ]) + for message in messages: + if message.is_read is False: + message.write({'is_read': True}) + print(f"Marked messages as read: {message.ids}") + else: + print("No unread messages found.") + except Exception as e: + print(f"An error occurred: {str(e)}") + + + + diff --git a/pos_chatter/static/description/assets/screenshots/ss2.png b/pos_chatter/static/description/assets/screenshots/ss2.png index c690610e7..2601c204a 100644 Binary files a/pos_chatter/static/description/assets/screenshots/ss2.png and b/pos_chatter/static/description/assets/screenshots/ss2.png differ diff --git a/pos_chatter/static/description/index.html b/pos_chatter/static/description/index.html index 924921479..42026554e 100644 --- a/pos_chatter/static/description/index.html +++ b/pos_chatter/static/description/index.html @@ -427,7 +427,7 @@

- On clicking an item in chat list, chat window open in pos screen. + When clicking on a chat item with an unread message count, the chat window opens in the POS screen.

diff --git a/pos_chatter/static/src/js/pos_msg_view.js b/pos_chatter/static/src/js/pos_msg_view.js index bf54d046b..e9b121e17 100644 --- a/pos_chatter/static/src/js/pos_msg_view.js +++ b/pos_chatter/static/src/js/pos_msg_view.js @@ -2,6 +2,8 @@ import { rpc } from "@web/core/network/rpc"; const { mount, xml, onMounted, useState, useRef } = owl; import { ChatMsgView } from "./pos_chat_view"; +import { useService } from "@web/core/utils/hooks"; + export class PosMsgView extends owl.Component { setup() { @@ -11,6 +13,11 @@ export class PosMsgView extends owl.Component { this.MsgWindow = new ChatMsgView(); this.state = useState({ data: [], + counts: { + all: 0, + chat: 0, + channels: 0 + } }); } @@ -20,15 +27,32 @@ export class PosMsgView extends owl.Component { const message_list = data.map((message) => { const parser = new DOMParser(); const parsedHtml = parser.parseFromString(message.message_body, "text/html"); - const plainText = parsedHtml.documentElement.textContent; + let plainText = parsedHtml.documentElement.textContent; + + // Format SMS failure messages + if (message.type === 'sms_failure') { + plainText = `SMS Failure: Contact\nAn error occurred when sending an SMS\nAug 18`; + } + return { id: message.id, type: message.type, name: message.name, message_body: plainText, + count: message.count || 0, }; }); + + // Calculate counts for each category + const counts = { + chat: message_list.filter(m => m.type === 'chat').reduce((sum, msg) => sum + msg.count, 0), + all: message_list.filter(m => m.type === 'channel').reduce((sum, msg) => sum + msg.count, 0), + channels : message_list.reduce((sum, msg) => sum + msg.count, 0), + + }; + this.state.data = message_list; + this.state.counts = counts; }); } @@ -56,14 +80,33 @@ export class PosMsgView extends owl.Component { } /** Open chat view */ - _onClickToMessage(ev) { + async _onClickToMessage(ev) { const channel_id = ev.currentTarget.getAttribute("value"); - this.__owl__.remove(); + if (!channel_id || isNaN(parseInt(channel_id))) { + console.error("Invalid channel_id:", channel_id); + alert("Cannot open chat: Invalid message ID"); + return; + } + console.log("Channel ID:", channel_id); + + try { + const result = await rpc("/web/dataset/call_kw/mail.message/compute_read_message", { + model: "mail.message", + method: "compute_read_message", + args: [channel_id], + kwargs:{} + }); - if (!document.querySelector("#pos_chat_view")) { - this.schedule_dropdown = mount(ChatMsgView, document.body, { props: { channel_id } }); - } else { + this.__owl__.remove(); this.schedule_dropdown = mount(ChatMsgView, document.body, { props: { channel_id } }); + } catch (error) { + console.error("RPC Error Details:", { + message: error.message, + data: error.data, + type: error.type, + stack: error.stack + }); + alert("Failed to load chat. Please try again."); } } } @@ -72,46 +115,75 @@ PosMsgView.template = xml`
-
-

All

-

Chat

-

Channels

+
+
+

All

+

Chat

+

Channels

+
+ + + + + + +

+
+ + +
- -
- +
+ + + + +
+
+ + -
`; +
`; \ No newline at end of file diff --git a/pos_chatter/static/src/js/pos_systray_icon.js b/pos_chatter/static/src/js/pos_systray_icon.js index 5dcd27f47..495ee7bf2 100644 --- a/pos_chatter/static/src/js/pos_systray_icon.js +++ b/pos_chatter/static/src/js/pos_systray_icon.js @@ -1,15 +1,60 @@ /** @odoo-module **/ -import { Component, useRef } from "@odoo/owl"; +import { Component, useRef, onWillUnmount } from "@odoo/owl"; import { mount } from "@odoo/owl"; import { Navbar } from "@point_of_sale/app/navbar/navbar"; import { patch } from "@web/core/utils/patch"; import { PosMsgView } from "./pos_msg_view"; +import { rpc } from "@web/core/network/rpc"; +import { useState } from "@odoo/owl"; patch(Navbar.prototype, { setup() { super.setup(); this.message = useRef('root'); + this.state = useState({ + messageCount: 0, + }); + this.schedule_dropdown = null; + this.refreshInterval = null; + + // Initial fetch + this.fetchMessageCount(); + + // Set up interval for refreshing every 5 seconds + this.setupRefreshInterval(); + + // Clean up interval on component destruction + onWillUnmount(() => { + if (this.refreshInterval) { + clearInterval(this.refreshInterval); + } + }); + }, + + setupRefreshInterval() { + // Refresh every 5 seconds (5000 milliseconds) + this.refreshInterval = setInterval(() => { + this.fetchMessageCount(); + }, 5000); }, + + async fetchMessageCount() { + try { + const data = await rpc("/pos_systray/message_data"); + const message_list = data.map((message) => ({ + id: message.id, + type: message.type, + name: message.name, + message_body: new DOMParser().parseFromString(message.message_body, "text/html").documentElement.textContent, + count: message.count || 0, + })); + const totalCount = message_list.reduce((sum, msg) => sum + msg.count, 0); + this.state.messageCount = totalCount; + } catch (error) { + console.error("Failed to fetch message count:", error); + } + }, + onClick(ev) { const systrayElements = document.querySelectorAll(".pos_systray_template"); if (systrayElements.length === 0) { @@ -20,4 +65,4 @@ patch(Navbar.prototype, { }); } }, -}); +}); \ No newline at end of file diff --git a/pos_chatter/static/src/xml/pos_systray_icon.xml b/pos_chatter/static/src/xml/pos_systray_icon.xml index 62becbe03..6d984f93c 100644 --- a/pos_chatter/static/src/xml/pos_systray_icon.xml +++ b/pos_chatter/static/src/xml/pos_systray_icon.xml @@ -6,9 +6,14 @@
+ t-on-click="onClick" style="cursor: pointer; position: relative;"> + + + +
- + \ No newline at end of file