From e7685c38c871b0f701423f9c272bc8f0c8ccda41 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Sat, 13 Jun 2026 22:10:32 +0700 Subject: [PATCH] feat: add promotional image support to notifications and expose thumbnails via API --- controllers/main.py | 26 +++++++++++++++++++------- models/app_notification.py | 25 +++++++++++++++++++++---- wizard/push_wizard.py | 8 ++++++++ wizard/push_wizard_views.xml | 2 ++ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/controllers/main.py b/controllers/main.py index 4073256..1557f00 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -26,29 +26,41 @@ class AppNotificationController(http.Controller): def fetch_notifications(self, **kw): """ Endpoint for the Flutter app Background Task and In-App notification center. + Returns image_128 (base64 thumbnail) for list display. + The Flutter detail screen loads the full image via /web/image/ with session cookie. """ + import base64 as b64mod user = request.env.user partner = user.partner_id - + last_id = kw.get('last_id', 0) - + # Give them global notifications OR notifications tagged explicitly to them domain = [ ('id', '>', last_id), - '|', + '|', ('partner_ids', 'in', [partner.id]), ('is_global', '=', True) ] - + notifications = request.env['mapan.app.notification'].sudo().search_read( domain, - ['id', 'title', 'body', 'create_date'], + ['id', 'title', 'body', 'create_date', 'image_128'], order='create_date desc', limit=50 ) - + + # Normalize image_128 bytes → base64 string for JSON transport + for notif in notifications: + img = notif.get('image_128') + if img and isinstance(img, bytes): + notif['image_128'] = b64mod.b64encode(img).decode('utf-8') + elif not img: + notif['image_128'] = None + notif['has_image'] = bool(notif['image_128']) + return { - 'status': 'success', + 'status': 'success', 'data': notifications } diff --git a/models/app_notification.py b/models/app_notification.py index 7a903dd..978b625 100644 --- a/models/app_notification.py +++ b/models/app_notification.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api class AppNotification(models.Model): _name = 'mapan.app.notification' @@ -7,20 +7,37 @@ class AppNotification(models.Model): title = fields.Char(string='Notification Title', required=True) body = fields.Text(string='Notification Body', required=True) - + + # Optional promotional image — Odoo auto-generates image_128 thumbnail + image = fields.Image( + string='Notification Image', + max_width=1920, + max_height=1920, + help='Optional image shown in the notification detail screen.' + ) + image_128 = fields.Image( + string='Thumbnail', + related='image', + max_width=128, + max_height=128, + store=True, + readonly=True, + ) + # If no partner_ids are selected, it is broadcast to everyone logically partner_ids = fields.Many2many( - 'res.partner', + 'res.partner', string='Selected Customers', help='Leave empty to broadcast to all app users.' ) - + is_global = fields.Boolean( string='Broadcast to All?', compute='_compute_is_global', store=True ) + @api.depends('partner_ids') def _compute_is_global(self): for rec in self: rec.is_global = not bool(rec.partner_ids) diff --git a/wizard/push_wizard.py b/wizard/push_wizard.py index 3f98107..c9199f8 100644 --- a/wizard/push_wizard.py +++ b/wizard/push_wizard.py @@ -7,6 +7,12 @@ class PushNotificationWizard(models.TransientModel): title = fields.Char(string='Notification Title', required=True) body = fields.Text(string='Notification Body', required=True) + image = fields.Image( + string='Notification Image (optional)', + max_width=1920, + max_height=1920, + help='Upload a promotional image to display in the notification detail.' + ) recipient_type = fields.Selection([ ('all', 'All Activated Members'), ('specific', 'Specific Members') @@ -26,6 +32,7 @@ class PushNotificationWizard(models.TransientModel): self.env['mapan.app.notification'].create({ 'title': self.title, 'body': self.body, + 'image': self.image, }) target_msg = 'all activated app members' else: @@ -33,6 +40,7 @@ class PushNotificationWizard(models.TransientModel): self.env['mapan.app.notification'].create({ 'title': self.title, 'body': self.body, + 'image': self.image, 'partner_ids': [(6, 0, self.partner_ids.ids)], }) target_msg = f'{len(self.partner_ids)} selected member(s)' diff --git a/wizard/push_wizard_views.xml b/wizard/push_wizard_views.xml index 8e09ec9..c53e060 100644 --- a/wizard/push_wizard_views.xml +++ b/wizard/push_wizard_views.xml @@ -8,6 +8,8 @@ +