Skip to content

Commit

Permalink
Fixes #5010 - WhatsApp-Business Channel (third iteration).
Browse files Browse the repository at this point in the history
Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Tobias Schäfer <ts@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Benjamin Scharf <bs@zammad.com>
Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
Co-authored-by: Dominik Klein <dk@zammad.com>
  • Loading branch information
8 people committed Mar 11, 2024
1 parent bed981e commit e54fd4a
Show file tree
Hide file tree
Showing 131 changed files with 4,561 additions and 415 deletions.
14 changes: 14 additions & 0 deletions app/assets/javascripts/app/controllers/_channel/whatsapp.coffee
Expand Up @@ -32,6 +32,11 @@ class ChannelWhatsapp extends App.ControllerSubContent
channels: channels
)

new App.HttpLog(
el: @$('.js-log')
facility: 'WhatsApp::Business'
)

new: (e) =>
e.preventDefault()

Expand Down Expand Up @@ -166,6 +171,14 @@ class WhatsappAccountPhoneNumberModal extends App.ControllerModal

preselected_group_id = if @channel then @channel.group_id else 1

content.find('.js-reminderActive').replaceWith App.UiElement.switch.render(
name: 'reminder_active'
null: false
default: true
display: __('Automatic reminders')
value: if _.isUndefined(@channel?.options?.reminder_active) then true else @channel.options.reminder_active
)

content.find('.js-messagesGroup').replaceWith App.UiElement.tree_select.render(
name: 'group_id'
multiple: false
Expand All @@ -181,6 +194,7 @@ class WhatsappAccountPhoneNumberModal extends App.ControllerModal
multiple: false
value: @channel?.options?.phone_number_id || @params.available_phone_numbers?[0]?.value
options: @params.available_phone_numbers?.map (elem) -> { name: elem.label, value: elem.value }
rejectNonExistentValues: true
)

content
Expand Down
44 changes: 36 additions & 8 deletions app/assets/javascripts/app/controllers/ticket_zoom.coffee
Expand Up @@ -2,9 +2,10 @@ class App.TicketZoom extends App.Controller
@include App.TicketNavigable

elements:
'.main': 'main'
'.ticketZoom': 'ticketZoom'
'.scrollPageHeader': 'scrollPageHeader'
'.main': 'main'
'.ticketZoom': 'ticketZoom'
'.scrollPageHeader': 'scrollPageHeader'
'.scrollPageAlert': 'scrollPageAlert'

events:
'click .js-submit': 'submit'
Expand Down Expand Up @@ -304,6 +305,10 @@ class App.TicketZoom extends App.Controller
offset = element.offset()
if offset
position = offset.top

# Subtract possible top padding of the parent container.
position -= parseInt(element.parent().css('paddingTop') or 0, 10)

Math.abs(position)

hide: =>
Expand Down Expand Up @@ -394,14 +399,20 @@ class App.TicketZoom extends App.Controller

positionPageHeaderUpdate: =>
headerHeight = @scrollPageHeader.outerHeight()
mainScrollHeigth = @main.prop('scrollHeight')
mainHeigth = @main.height()
alertHeight = if @isPageAlertVisible() then @scrollPageAlert.outerHeight() else 0
mainScrollHeight = @main.prop('scrollHeight')
mainHeight = @main.height()

scroll = @main.scrollTop()

# if page header is not possible to use - mainScrollHeigth to low - hide page header
if not mainScrollHeigth > mainHeigth + headerHeight
# if page header is not possible to use - mainScrollHeight to low - hide page header
if not mainScrollHeight > mainHeight + headerHeight
@scrollPageHeader.css('transform', "translateY(#{-headerHeight}px)")

if alertHeight
@scrollPageAlert.css('transform', 'translateY(0)')
@main.css('paddingTop', "#{alertHeight}px")

return

if scroll > headerHeight
Expand All @@ -414,8 +425,15 @@ class App.TicketZoom extends App.Controller
# translateY: headerHeight .. 0
@scrollPageHeader.css('transform', "translateY(#{scroll - headerHeight}px)")

if alertHeight
@scrollPageAlert.css('transform', "translateY(#{scroll}px)")
@main.css('paddingTop', "#{scroll + alertHeight}px")

@scrollHeaderPos = scroll

isPageAlertVisible: =>
not @scrollPageAlert.hasClass('hide')

pendingTimeReminderReached: =>
App.TaskManager.touch(@taskKey)

Expand Down Expand Up @@ -502,6 +520,14 @@ class App.TicketZoom extends App.Controller
ticket_id: @ticket_id
)

# Check if the alert should be shown.
# Normally, this is a concern of the associated channel, so we only render it if it's known.
if @ticket.preferences?.channel_id
new App.TicketZoomAlert(
el: elLocal.find('.js-ticketAlertContainer')
object_id: @ticket_id
)

new App.TicketZoomSetting(
el: elLocal.find('.js-settingContainer')
ticket_id: @ticket_id
Expand Down Expand Up @@ -898,7 +924,9 @@ class App.TicketZoom extends App.Controller
@autosaveStart()
return

if articleParams && articleParams.body
# New article body required.
# But WhatsApp messages with some attachments go without adjacent text.
if articleParams && (articleParams.body || @articleNew?.checkBodyAllowEmpty())
article = new App.TicketArticle
article.load(articleParams)
errors = article.validate()
Expand Down
19 changes: 19 additions & 0 deletions app/assets/javascripts/app/controllers/ticket_zoom/alert.coffee
@@ -0,0 +1,19 @@
class App.TicketZoomAlert extends App.ControllerObserver
model: 'Ticket'
observe:
state_id: true
preferences: true
globalRerender: false

render: (ticket) =>
alert = new App.TicketZoomChannel(ticket).channelAlert()

if not alert
@html ''
@el.addClass('hide')
return

element = App.view('ticket_zoom/alert')(alert: alert)

@html element
@el.removeClass('hide')
Expand Up @@ -3,6 +3,7 @@ class WhatsappReply
return actions if !ticket.editable()
return actions if ticket.currentView() is 'customer'
return actions if article.type.name isnt 'whatsapp message'
return actions if !@canUseWhatsapp(ticket)

actions.push {
name: __('reply')
Expand Down Expand Up @@ -40,6 +41,8 @@ class WhatsappReply

return articleTypes if !ticket || !ticket.create_article_type_id

return articleTypes if !@canUseWhatsapp(ticket)

articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name

return articleTypes if articleTypeCreate isnt 'whatsapp message'
Expand All @@ -49,9 +52,28 @@ class WhatsappReply
icon: 'whatsapp'
attributes: []
internal: false,
features: ['body:limit', 'attachment']
features: ['body:limit', 'attachment', 'attachments:limit', 'attachments:size', 'body:ensureNoCaption', 'body:allowNoCaption']
maxTextLength: 4096
warningTextLength: -1
attachmentsLimit: 1
attachmentsSize: [
{ size: 16 * 1024 * 1024, label: __('Audio file'), content_types: ['audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/amr', 'audio/ogg'] },
{ size: 5 * 1024 * 1024, label: __('Image file'), content_types: ['image/jpeg', 'image/png'] },
{ size: 16 * 1024 * 1024, label: __('Video file'), content_types: ['video/mp4', 'video/3gp'] },
{ size: 500 * 1024, label: __('Sticker file'), content_types: ['image/webp'] },
{ size: 100 * 1024 * 1024, label: __('Document file'), content_types: ['text/plain', 'application/pdf', 'application/vnd.ms-powerpoint', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] },
],
bodyEnsureNoCaption: (attachmentsTypes) ->
base = __('%s is sent without text caption')

if _.intersection(attachmentsTypes, ['audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/amr', 'audio/ogg']).length
App.i18n.translateContent base, App.i18n.translateContent(__('Audio file'))
else if _.intersection(attachmentsTypes, ['image/webp']).length
App.i18n.translateContent base, App.i18n.translateContent(__('Sticker file'))
else
false
bodyAllowNoCaption: (attachments) ->
attachments.length > 0
}

articleTypes
Expand All @@ -64,4 +86,9 @@ class WhatsappReply

params

@canUseWhatsapp: (ticket) ->
alert = new App.TicketZoomChannel(ticket).channelAlert()

alert?.type and alert.type != 'danger'

App.Config.set('300-WhatsappReply', WhatsappReply, 'TicketZoomArticleAction')

0 comments on commit e54fd4a

Please sign in to comment.