diff --git a/.editorconfig b/.editorconfig
index 7203adb09..2a5fe28cf 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -7,8 +7,8 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-indent_style = spaces
+indent_style = space
tab_width = 2
-[{*.{rb,erb,js,coffee,json,yml,css,scss,sh,markdown,md,html}]
+[*.{rb,erb,js,coffee,json,yml,css,scss,sh,markdown,md,html}]
indent_size = 2
diff --git a/.github/workflows/publish_foss_docker.yml b/.github/workflows/publish_foss_docker.yml
index aa1b15df2..2ddaba7e5 100644
--- a/.github/workflows/publish_foss_docker.yml
+++ b/.github/workflows/publish_foss_docker.yml
@@ -58,6 +58,6 @@ jobs:
with:
context: .
file: docker/Dockerfile
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64
push: true
tags: ${{ env.DOCKER_TAG }}
diff --git a/.gitignore b/.gitignore
index e4b14d2f5..11a8c50e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,9 +39,6 @@ public/packs*
*.un~
.jest-cache
-#VS Code files
-.vscode
-
# ignore jetbrains IDE files
.idea
@@ -62,4 +59,4 @@ package-lock.json
test/cypress/videos/*
/config/master.key
-/config/*.enc
\ No newline at end of file
+/config/*.enc
diff --git a/.rubocop.yml b/.rubocop.yml
index dafd9a620..3665ad2e3 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -16,6 +16,7 @@ Metrics/ClassLength:
- 'app/models/message.rb'
- 'app/builders/messages/facebook/message_builder.rb'
- 'app/controllers/api/v1/accounts/contacts_controller.rb'
+ - 'app/controllers/api/v1/accounts/conversations_controller.rb'
- 'app/listeners/action_cable_listener.rb'
- 'app/models/conversation.rb'
RSpec/ExampleLength:
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..254e696a4
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,32 @@
+{
+ "recommendations": [
+ // Spell check
+ "streetsidesoftware.code-spell-checker",
+ // Better Comments
+ "aaron-bond.better-comments",
+ // Rails Test Runner
+ "davidpallinder.rails-test-runner",
+ // Eslint
+ "dbaeumer.vscode-eslint",
+ // Auto Close Tag
+ "formulahendry.auto-close-tag",
+ // Auto Rename Tag
+ "formulahendry.auto-rename-tag",
+ // Hight light colors
+ "naumovs.color-highlight",
+ // GitLens
+ "eamodio.gitlens",
+ // Ruby
+ "rebornix.ruby",
+ // Vue
+ "octref.vetur",
+ // Prettier
+ "esbenp.prettier-vscode",
+ // Dot Env
+ "mikestead.dotenv",
+ // HTML CSS Support
+ "ecmel.vscode-html-css",
+ // Tailwind CSS Intellisense
+ "bradlc.vscode-tailwindcss",
+ ]
+}
diff --git a/app/builders/contact_inbox_builder.rb b/app/builders/contact_inbox_builder.rb
index 1b8782c51..8fcd2b158 100644
--- a/app/builders/contact_inbox_builder.rb
+++ b/app/builders/contact_inbox_builder.rb
@@ -1,13 +1,12 @@
+# This Builder will create a contact inbox with specified attributes. If the contact inbox already exists, it will be returned.
+# For Specific Channels like whatsapp, email etc . it smartly generated appropriate the source id when none is provided.
+
class ContactInboxBuilder
- pattr_initialize [:contact_id!, :inbox_id!, :source_id]
+ pattr_initialize [:contact, :inbox, :source_id, { hmac_verified: false }]
def perform
- @contact = Contact.find(contact_id)
- @inbox = @contact.account.inboxes.find(inbox_id)
- return unless ['Channel::TwilioSms', 'Channel::Sms', 'Channel::Email', 'Channel::Api', 'Channel::Whatsapp'].include? @inbox.channel_type
-
- source_id = @source_id || generate_source_id
- create_contact_inbox(source_id) if source_id.present?
+ @source_id ||= generate_source_id
+ create_contact_inbox if source_id.present?
end
private
@@ -19,23 +18,37 @@ class ContactInboxBuilder
when 'Channel::Whatsapp'
wa_source_id
when 'Channel::Email'
- @contact.email
+ email_source_id
when 'Channel::Sms'
- @contact.phone_number
- when 'Channel::Api'
+ phone_source_id
+ when 'Channel::Api', 'Channel::WebWidget'
SecureRandom.uuid
+ else
+ raise "Unsupported operation for this channel: #{@inbox.channel_type}"
end
end
+ def email_source_id
+ raise ActionController::ParameterMissing, 'contact email' unless @contact.email
+
+ @contact.email
+ end
+
+ def phone_source_id
+ raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
+
+ @contact.phone_number
+ end
+
def wa_source_id
- return unless @contact.phone_number
+ raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
# whatsapp doesn't want the + in e164 format
@contact.phone_number.delete('+').to_s
end
def twilio_source_id
- return unless @contact.phone_number
+ raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
case @inbox.channel.medium
when 'sms'
@@ -45,11 +58,11 @@ class ContactInboxBuilder
end
end
- def create_contact_inbox(source_id)
- ::ContactInbox.find_or_create_by!(
+ def create_contact_inbox
+ ::ContactInbox.create_with(hmac_verified: hmac_verified || false).find_or_create_by!(
contact_id: @contact.id,
inbox_id: @inbox.id,
- source_id: source_id
+ source_id: @source_id
)
end
end
diff --git a/app/builders/contact_builder.rb b/app/builders/contact_inbox_with_contact_builder.rb
similarity index 51%
rename from app/builders/contact_builder.rb
rename to app/builders/contact_inbox_with_contact_builder.rb
index 938072643..d97f64cfe 100644
--- a/app/builders/contact_builder.rb
+++ b/app/builders/contact_inbox_with_contact_builder.rb
@@ -1,25 +1,47 @@
-class ContactBuilder
- pattr_initialize [:source_id!, :inbox!, :contact_attributes!, :hmac_verified]
+# This Builder will create a contact and contact inbox with specified attributes.
+# If an existing identified contact exisits, it will be returned.
+# for contact inbox logic it uses the contact inbox builder
+
+class ContactInboxWithContactBuilder
+ pattr_initialize [:inbox!, :contact_attributes!, :source_id, :hmac_verified]
def perform
- contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id)
- return contact_inbox if contact_inbox
+ find_or_create_contact_and_contact_inbox
+ # in case of race conditions where contact is created by another thread
+ # we will try to find the contact and create a contact inbox
+ rescue ActiveRecord::RecordNotUnique
+ find_or_create_contact_and_contact_inbox
+ end
- build_contact_inbox
+ def find_or_create_contact_and_contact_inbox
+ @contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id) if source_id.present?
+ return @contact_inbox if @contact_inbox
+
+ ActiveRecord::Base.transaction(requires_new: true) do
+ build_contact_with_contact_inbox
+ update_contact_avatar(@contact) unless @contact.avatar.attached?
+ @contact_inbox
+ end
end
private
+ def build_contact_with_contact_inbox
+ @contact = find_contact || create_contact
+ @contact_inbox = create_contact_inbox
+ end
+
def account
@account ||= inbox.account
end
- def create_contact_inbox(contact)
- ::ContactInbox.create_with(hmac_verified: hmac_verified || false).find_or_create_by!(
- contact_id: contact.id,
- inbox_id: inbox.id,
- source_id: source_id
- )
+ def create_contact_inbox
+ ContactInboxBuilder.new(
+ contact: @contact,
+ inbox: @inbox,
+ source_id: @source_id,
+ hmac_verified: hmac_verified
+ ).perform
end
def update_contact_avatar(contact)
@@ -61,16 +83,4 @@ class ContactBuilder
account.contacts.find_by(phone_number: phone_number)
end
-
- def build_contact_inbox
- ActiveRecord::Base.transaction do
- contact = find_contact || create_contact
- contact_inbox = create_contact_inbox(contact)
- update_contact_avatar(contact)
- contact_inbox
- rescue StandardError => e
- Rails.logger.error e
- raise e
- end
- end
end
diff --git a/app/builders/messages/facebook/message_builder.rb b/app/builders/messages/facebook/message_builder.rb
index 9f670602a..42d567e54 100644
--- a/app/builders/messages/facebook/message_builder.rb
+++ b/app/builders/messages/facebook/message_builder.rb
@@ -22,10 +22,9 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
return if @inbox.channel.reauthorization_required?
ActiveRecord::Base.transaction do
- build_contact
+ build_contact_inbox
build_message
end
- ensure_contact_avatar
rescue Koala::Facebook::AuthenticationError
@inbox.channel.authorization_error!
rescue StandardError => e
@@ -35,15 +34,12 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
private
- def contact
- @contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact
- end
-
- def build_contact
- return if contact.present?
-
- @contact = Contact.create!(contact_params.except(:remote_avatar_url))
- @contact_inbox = ContactInbox.find_or_create_by!(contact: contact, inbox: @inbox, source_id: @sender_id)
+ def build_contact_inbox
+ @contact_inbox = ::ContactInboxWithContactBuilder.new(
+ source_id: @sender_id,
+ inbox: @inbox,
+ contact_attributes: contact_params
+ ).perform
end
def build_message
@@ -54,19 +50,11 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
end
end
- def ensure_contact_avatar
- return if contact_params[:remote_avatar_url].blank?
- return if @contact.avatar.attached?
-
- Avatar::AvatarFromUrlJob.perform_later(@contact, contact_params[:remote_avatar_url])
- end
-
def conversation
@conversation ||= Conversation.find_by(conversation_params) || build_conversation
end
def build_conversation
- @contact_inbox ||= contact.contact_inboxes.find_by!(source_id: @sender_id)
Conversation.create!(conversation_params.merge(
contact_inbox_id: @contact_inbox.id
))
@@ -94,7 +82,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
{
account_id: @inbox.account_id,
inbox_id: @inbox.id,
- contact_id: contact.id
+ contact_id: @contact_inbox.contact_id
}
end
@@ -105,7 +93,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
message_type: @message_type,
content: response.content,
source_id: response.identifier,
- sender: @outgoing_echo ? nil : contact
+ sender: @outgoing_echo ? nil : @contact_inbox.contact
}
end
@@ -113,7 +101,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
{
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
account_id: @inbox.account_id,
- remote_avatar_url: result['profile_pic'] || ''
+ avatar_url: result['profile_pic']
}
end
diff --git a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb
index fdcdcaf9e..b4287ae08 100644
--- a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb
+++ b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb
@@ -2,8 +2,11 @@ class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts:
before_action :ensure_inbox, only: [:create]
def create
- source_id = params[:source_id] || SecureRandom.uuid
- @contact_inbox = ContactInbox.create!(contact: @contact, inbox: @inbox, source_id: source_id)
+ @contact_inbox = ContactInboxBuilder.new(
+ contact: @contact,
+ inbox: @inbox,
+ source_id: params[:source_id]
+ ).perform
end
private
diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb
index 1c56e9c04..b86b973df 100644
--- a/app/controllers/api/v1/accounts/contacts_controller.rb
+++ b/app/controllers/api/v1/accounts/contacts_controller.rb
@@ -134,8 +134,11 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
return if params[:inbox_id].blank?
inbox = Current.account.inboxes.find(params[:inbox_id])
- source_id = params[:source_id] || SecureRandom.uuid
- ContactInbox.create!(contact: @contact, inbox: inbox, source_id: source_id)
+ ContactInboxBuilder.new(
+ contact: @contact,
+ inbox: inbox,
+ source_id: params[:source_id]
+ ).perform
end
def permitted_params
diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb
index 0515eabca..8734a3dd4 100644
--- a/app/controllers/api/v1/accounts/conversations_controller.rb
+++ b/app/controllers/api/v1/accounts/conversations_controller.rb
@@ -3,7 +3,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
include DateRangeHelper
before_action :conversation, except: [:index, :meta, :search, :create, :filter]
- before_action :contact_inbox, only: [:create]
+ before_action :inbox, :contact, :contact_inbox, only: [:create]
def index
result = conversation_finder.perform
@@ -109,22 +109,35 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
authorize @conversation.inbox, :show?
end
+ def inbox
+ return if params[:inbox_id].blank?
+
+ @inbox = Current.account.inboxes.find(params[:inbox_id])
+ authorize @inbox, :show?
+ end
+
+ def contact
+ return if params[:contact_id].blank?
+
+ @contact = Current.account.contacts.find(params[:contact_id])
+ end
+
def contact_inbox
@contact_inbox = build_contact_inbox
+ # fallback for the old case where we do look up only using source id
+ # In future we need to change this and make sure we do look up on combination of inbox_id and source_id
+ # and deprecate the support of passing only source_id as the param
@contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id])
authorize @contact_inbox.inbox, :show?
end
def build_contact_inbox
- return if params[:contact_id].blank? || params[:inbox_id].blank?
-
- inbox = Current.account.inboxes.find(params[:inbox_id])
- authorize inbox, :show?
+ return if @inbox.blank? || @contact.blank?
ContactInboxBuilder.new(
- contact_id: params[:contact_id],
- inbox_id: inbox.id,
+ contact: @contact,
+ inbox: @inbox,
source_id: params[:source_id]
).perform
end
diff --git a/app/controllers/concerns/request_exception_handler.rb b/app/controllers/concerns/request_exception_handler.rb
index 6e9ed04cc..2f53fdc2b 100644
--- a/app/controllers/concerns/request_exception_handler.rb
+++ b/app/controllers/concerns/request_exception_handler.rb
@@ -13,6 +13,8 @@ module RequestExceptionHandler
render_not_found_error('Resource could not be found')
rescue Pundit::NotAuthorizedError
render_unauthorized('You are not authorized to do this action')
+ rescue ActionController::ParameterMissing => e
+ render_could_not_create_error(e.message)
ensure
# to address the thread variable leak issues in Puma/Thin webserver
Current.reset
diff --git a/app/controllers/public/api/v1/inboxes/contacts_controller.rb b/app/controllers/public/api/v1/inboxes/contacts_controller.rb
index eb794f2a0..1fde3051e 100644
--- a/app/controllers/public/api/v1/inboxes/contacts_controller.rb
+++ b/app/controllers/public/api/v1/inboxes/contacts_controller.rb
@@ -4,7 +4,7 @@ class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesCon
def create
source_id = params[:source_id] || SecureRandom.uuid
- @contact_inbox = ::ContactBuilder.new(
+ @contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: source_id,
inbox: @inbox_channel.inbox,
contact_attributes: permitted_params.except(:identifier, :identifier_hash)
diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss
index 9d3f84b25..478045000 100644
--- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss
+++ b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss
@@ -113,9 +113,22 @@ $default-button-height: 4.0rem;
}
&.clear {
+ color: var(--w-700);
+
+ &.secondary {
+ color: var(--s-700)
+ }
+
+ &.success {
+ color: var(--g-700)
+ }
+
+ &.alert {
+ color: var(--r-700)
+ }
&.warning {
- color: var(--y-600);
+ color: var(--y-700)
}
&:hover {
@@ -146,6 +159,8 @@ $default-button-height: 4.0rem;
&.small {
height: var(--space-large);
+ padding-bottom: var(--space-smaller);
+ padding-top: var(--space-smaller);
}
&.large {
diff --git a/app/javascript/dashboard/assets/scss/widgets/_modal.scss b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
index fc497f069..543a60797 100644
--- a/app/javascript/dashboard/assets/scss/widgets/_modal.scss
+++ b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
@@ -14,15 +14,9 @@
}
.modal--close {
- border-radius: 50%;
- color: $color-heading;
- cursor: pointer;
- font-size: $font-size-big;
- line-height: $space-normal;
- padding: $space-normal;
position: absolute;
- right: $space-micro;
- top: $space-micro;
+ right: $space-small;
+ top: $space-small;
&:hover {
background: $color-background;
diff --git a/app/javascript/dashboard/components/Modal.vue b/app/javascript/dashboard/components/Modal.vue
index f4cad844a..d5dd55ffa 100644
--- a/app/javascript/dashboard/components/Modal.vue
+++ b/app/javascript/dashboard/components/Modal.vue
@@ -7,9 +7,13 @@
@click="onBackDropClick"
>
-
+
diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js
index cc4287503..fa4633ec1 100644
--- a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js
+++ b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js
@@ -39,7 +39,7 @@ const primaryMenuItems = accountId => [
label: 'HELP_CENTER.TITLE',
featureFlag: 'help_center',
toState: frontendURL(`accounts/${accountId}/portals`),
- toStateName: 'list_all_portals',
+ toStateName: 'default_portal_articles',
roles: ['administrator'],
},
{
diff --git a/app/javascript/dashboard/components/widgets/Avatar.vue b/app/javascript/dashboard/components/widgets/Avatar.vue
index daf044288..df654ee7e 100644
--- a/app/javascript/dashboard/components/widgets/Avatar.vue
+++ b/app/javascript/dashboard/components/widgets/Avatar.vue
@@ -78,7 +78,7 @@ export default {
if (initials.length > 2 && initials.search(/[A-Z]/) !== -1) {
initials = initials.replace(/[a-z]+/g, '');
}
- initials = initials.substr(0, 2).toUpperCase();
+ initials = initials.substring(0, 2).toUpperCase();
return initials;
},
},
diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue
index 3481125ca..7fb24301b 100644
--- a/app/javascript/dashboard/components/widgets/conversation/Message.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue
@@ -420,6 +420,8 @@ export default {
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue
index 33559e123..4e33bc7fa 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue
@@ -10,10 +10,11 @@
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue
index 9dda5c02c..c9294aa2a 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue
@@ -239,7 +239,7 @@ export default {
const hasNextWord = value.includes(' ');
const isShortCodeActive = this.hasSlashCommand && !hasNextWord;
if (isShortCodeActive) {
- this.cannedResponseSearchKey = value.substr(1, value.length);
+ this.cannedResponseSearchKey = value.substring(1);
this.showCannedResponseMenu = true;
} else {
this.cannedResponseSearchKey = '';
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue
index cecf1f39e..b602baaa8 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue
@@ -16,7 +16,12 @@
- {{ category.name }}
+
+ {{ category.name }}
+
|
@@ -43,6 +48,8 @@
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue
index 528b55575..562d6bd07 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue
@@ -59,6 +59,7 @@ import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
import CommandBar from 'dashboard/routes/dashboard/commands/commandbar.vue';
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal';
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel';
+import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import portalMixin from '../mixins/portalMixin';
import AddCategory from '../pages/categories/AddCategory';
@@ -72,7 +73,7 @@ export default {
PortalPopover,
AddCategory,
},
- mixins: [portalMixin],
+ mixins: [portalMixin, uiSettingsMixin],
data() {
return {
isSidebarOpen: false,
@@ -231,7 +232,13 @@ export default {
},
updated() {
const slug = this.$route.params.portalSlug;
- if (slug) this.lastActivePortalSlug = slug;
+ if (slug) {
+ this.lastActivePortalSlug = slug;
+ this.updateUISettings({
+ last_active_portal_slug: slug,
+ last_active_locale_code: this.selectedLocaleInPortal,
+ });
+ }
},
methods: {
handleResize() {
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalListItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalListItem.vue
index e0e002002..0354ad22b 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalListItem.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalListItem.vue
@@ -188,13 +188,15 @@
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
index 01c46f5dc..7931016e4 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
@@ -19,15 +19,11 @@
:script="currentInbox.callback_webhook_url"
/>
-
+
{{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }}
-
+
{{
$t(
@@ -36,7 +32,6 @@
}}
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/WidgetBuilder.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/WidgetBuilder.vue
index 8da5183d7..bdc20e14e 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/WidgetBuilder.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/WidgetBuilder.vue
@@ -243,7 +243,7 @@ export default {
this.$t('INBOX_MGMT.WIDGET_BUILDER.SCRIPT_SETTINGS', {
options: JSON.stringify(options),
}) +
- script.substring(13, script.length)
+ script.substring(13)
);
},
getWidgetViewOptions() {
diff --git a/app/javascript/packs/sdk.js b/app/javascript/packs/sdk.js
index 064033841..2fdd6c57b 100755
--- a/app/javascript/packs/sdk.js
+++ b/app/javascript/packs/sdk.js
@@ -10,7 +10,7 @@ import {
getUserCookieName,
hasUserKeys,
} from '../sdk/cookieHelpers';
-import { addClass, removeClass } from '../sdk/DOMHelpers';
+import { addClasses, removeClasses } from '../sdk/DOMHelpers';
import { SDK_SET_BUBBLE_VISIBILITY } from 'shared/constants/sharedFrameEvents';
const runSDK = ({ baseUrl, websiteToken }) => {
if (window.$chatwoot) {
@@ -41,12 +41,12 @@ const runSDK = ({ baseUrl, websiteToken }) => {
let widgetElm = document.querySelector('.woot--bubble-holder');
let widgetHolder = document.querySelector('.woot-widget-holder');
if (visibility === 'hide') {
- addClass(widgetHolder, 'woot-widget--without-bubble');
- addClass(widgetElm, 'woot-hidden');
+ addClasses(widgetHolder, 'woot-widget--without-bubble');
+ addClasses(widgetElm, 'woot-hidden');
window.$chatwoot.hideMessageBubble = true;
} else if (visibility === 'show') {
- removeClass(widgetElm, 'woot-hidden');
- removeClass(widgetHolder, 'woot-widget--without-bubble');
+ removeClasses(widgetElm, 'woot-hidden');
+ removeClasses(widgetHolder, 'woot-widget--without-bubble');
window.$chatwoot.hideMessageBubble = false;
}
IFrameHelper.sendMessage(SDK_SET_BUBBLE_VISIBILITY, {
diff --git a/app/javascript/sdk/DOMHelpers.js b/app/javascript/sdk/DOMHelpers.js
index 2ac6188e6..47a45cc78 100644
--- a/app/javascript/sdk/DOMHelpers.js
+++ b/app/javascript/sdk/DOMHelpers.js
@@ -3,68 +3,20 @@ import { IFrameHelper } from './IFrameHelper';
export const loadCSS = () => {
const css = document.createElement('style');
- css.type = 'text/css';
css.innerHTML = `${SDK_CSS}`;
document.body.appendChild(css);
};
-export const wootOn = (elm, event, fn) => {
- if (document.addEventListener) {
- elm.addEventListener(event, fn, false);
- } else if (document.attachEvent) {
- // <= IE 8 loses scope so need to apply, we add this to object so we
- // can detach later (can't detach anonymous functions)
- // eslint-disable-next-line
- elm[event + fn] = function() {
- // eslint-disable-next-line
- return fn.apply(elm, arguments);
- };
- elm.attachEvent(`on${event}`, elm[event + fn]);
- }
-};
-
-export const classHelper = (classes, action, elm) => {
- let search;
- let replace;
- let i;
- let has = false;
- if (classes) {
- // Trim any whitespace
- const classarray = classes.split(/\s+/);
- for (i = 0; i < classarray.length; i += 1) {
- search = new RegExp(`\\b${classarray[i]}\\b`, 'g');
- replace = new RegExp(` *${classarray[i]}\\b`, 'g');
- if (action === 'remove') {
- // eslint-disable-next-line
- elm.className = elm.className.replace(replace, '');
- } else if (action === 'toggle') {
- // eslint-disable-next-line
- elm.className = elm.className.match(search)
- ? elm.className.replace(replace, '')
- : `${elm.className} ${classarray[i]}`;
- } else if (action === 'has') {
- if (elm.className.match(search)) {
- has = true;
- break;
- }
- }
- }
- }
- return has;
-};
-
-export const addClass = (elm, classes) => {
- if (classes) {
- elm.className += ` ${classes}`;
- }
+export const addClasses = (elm, classes) => {
+ elm.classList.add(...classes.split(' '));
};
export const toggleClass = (elm, classes) => {
- classHelper(classes, 'toggle', elm);
+ elm.classList.toggle(classes);
};
-export const removeClass = (elm, classes) => {
- classHelper(classes, 'remove', elm);
+export const removeClasses = (elm, classes) => {
+ elm.classList.remove(...classes.split(' '));
};
export const onLocationChange = ({ referrerURL, referrerHost }) => {
diff --git a/app/javascript/sdk/IFrameHelper.js b/app/javascript/sdk/IFrameHelper.js
index 47596bce3..bbc2cfafd 100644
--- a/app/javascript/sdk/IFrameHelper.js
+++ b/app/javascript/sdk/IFrameHelper.js
@@ -1,9 +1,8 @@
import Cookies from 'js-cookie';
import {
- wootOn,
- addClass,
+ addClasses,
loadCSS,
- removeClass,
+ removeClasses,
onLocationChangeListener,
} from './DOMHelpers';
import {
@@ -68,7 +67,7 @@ export const IFrameHelper = {
holderClassName += ` woot-widget-holder--flat`;
}
- addClass(widgetHolder, holderClassName);
+ addClasses(widgetHolder, holderClassName);
widgetHolder.appendChild(iframe);
body.appendChild(widgetHolder);
IFrameHelper.initPostMessageCommunication();
@@ -99,7 +98,7 @@ export const IFrameHelper = {
};
},
initWindowSizeListener: () => {
- wootOn(window, 'resize', () => IFrameHelper.toggleCloseButton());
+ window.addEventListener('resize', () => IFrameHelper.toggleCloseButton());
},
preventDefaultScroll: () => {
widgetHolder.addEventListener('wheel', event => {
@@ -241,9 +240,9 @@ export const IFrameHelper = {
event.unreadMessageCount > 0 &&
!bubbleElement.classList.contains('unread-notification')
) {
- addClass(bubbleElement, 'unread-notification');
+ addClasses(bubbleElement, 'unread-notification');
} else if (event.unreadMessageCount === 0) {
- removeClass(bubbleElement, 'unread-notification');
+ removeClasses(bubbleElement, 'unread-notification');
}
},
@@ -284,7 +283,7 @@ export const IFrameHelper = {
target: chatBubble,
});
- addClass(closeBubble, closeBtnClassName);
+ addClasses(closeBubble, closeBtnClassName);
chatIcon.style.background = widgetColor;
closeBubble.style.background = widgetColor;
diff --git a/app/javascript/sdk/bubbleHelpers.js b/app/javascript/sdk/bubbleHelpers.js
index b1ef9110f..5eab20f5d 100644
--- a/app/javascript/sdk/bubbleHelpers.js
+++ b/app/javascript/sdk/bubbleHelpers.js
@@ -1,4 +1,4 @@
-import { addClass, removeClass, toggleClass, wootOn } from './DOMHelpers';
+import { addClasses, removeClasses, toggleClass } from './DOMHelpers';
import { IFrameHelper } from './IFrameHelper';
import { isExpandedView } from './settingsHelper';
@@ -41,14 +41,14 @@ export const createBubbleIcon = ({ className, src, target }) => {
export const createBubbleHolder = hideMessageBubble => {
if (hideMessageBubble) {
- addClass(bubbleHolder, 'woot-hidden');
+ addClasses(bubbleHolder, 'woot-hidden');
}
- addClass(bubbleHolder, 'woot--bubble-holder');
+ addClasses(bubbleHolder, 'woot--bubble-holder');
body.appendChild(bubbleHolder);
};
export const createNotificationBubble = () => {
- addClass(notificationBubble, 'woot--notification');
+ addClasses(notificationBubble, 'woot--notification');
return notificationBubble;
};
@@ -71,15 +71,15 @@ export const onBubbleClick = (props = {}) => {
};
export const onClickChatBubble = () => {
- wootOn(bubbleHolder, 'click', onBubbleClick);
+ bubbleHolder.addEventListener('click', onBubbleClick);
};
export const addUnreadClass = () => {
const holderEl = document.querySelector('.woot-widget-holder');
- addClass(holderEl, 'has-unread-view');
+ addClasses(holderEl, 'has-unread-view');
};
export const removeUnreadClass = () => {
const holderEl = document.querySelector('.woot-widget-holder');
- removeClass(holderEl, 'has-unread-view');
+ removeClasses(holderEl, 'has-unread-view');
};
diff --git a/app/javascript/shared/components/ui/MultiselectDropdown.vue b/app/javascript/shared/components/ui/MultiselectDropdown.vue
index 76908c380..1636ca252 100644
--- a/app/javascript/shared/components/ui/MultiselectDropdown.vue
+++ b/app/javascript/shared/components/ui/MultiselectDropdown.vue
@@ -41,9 +41,18 @@
:class="{ 'dropdown-pane--open': showSearchDropdown }"
class="dropdown-pane"
>
-
- {{ multiselectorTitle }}
-
+
diff --git a/app/javascript/shared/components/ui/MultiselectDropdownItems.vue b/app/javascript/shared/components/ui/MultiselectDropdownItems.vue
index 8aadcb7cb..96aa99323 100644
--- a/app/javascript/shared/components/ui/MultiselectDropdownItems.vue
+++ b/app/javascript/shared/components/ui/MultiselectDropdownItems.vue
@@ -20,6 +20,7 @@
.dropdown-menu__item {
list-style: none;
+ margin-bottom: var(--space-micro);
::v-deep {
a,
.button {
+ display: inline-flex;
+ white-space: nowrap;
width: 100%;
text-align: left;
color: var(--s-700);
- white-space: nowrap;
- display: inline-flex;
- padding: var(--space-small);
- padding-top: var(--space-small);
- padding-bottom: var(--space-small);
- border-radius: var(--border-radius-normal);
&:hover {
background: var(--color-background);
diff --git a/app/javascript/shared/mixins/campaignMixin.js b/app/javascript/shared/mixins/campaignMixin.js
index b9353ff29..ebf98aa37 100644
--- a/app/javascript/shared/mixins/campaignMixin.js
+++ b/app/javascript/shared/mixins/campaignMixin.js
@@ -1,10 +1,10 @@
import { CAMPAIGN_TYPES } from '../constants/campaign';
+
export default {
computed: {
campaignType() {
const pageURL = window.location.href;
- const type = pageURL.substr(pageURL.lastIndexOf('/') + 1);
- return type;
+ return pageURL.substring(pageURL.lastIndexOf('/') + 1);
},
isOngoingType() {
return this.campaignType === CAMPAIGN_TYPES.ONGOING;
diff --git a/app/javascript/survey/views/Response.vue b/app/javascript/survey/views/Response.vue
index 6ec4ec100..828a9cbee 100644
--- a/app/javascript/survey/views/Response.vue
+++ b/app/javascript/survey/views/Response.vue
@@ -93,7 +93,7 @@ export default {
computed: {
surveyId() {
const pageURL = window.location.href;
- return pageURL.substr(pageURL.lastIndexOf('/') + 1);
+ return pageURL.substring(pageURL.lastIndexOf('/') + 1);
},
isRatingSubmitted() {
return this.surveyDetails && this.surveyDetails.rating;
diff --git a/app/javascript/widget/api/agent.js b/app/javascript/widget/api/agent.js
index 0debeccaf..5dceecad7 100644
--- a/app/javascript/widget/api/agent.js
+++ b/app/javascript/widget/api/agent.js
@@ -3,6 +3,5 @@ import { API } from 'widget/helpers/axios';
export const getAvailableAgents = async websiteToken => {
const urlData = endPoints.getAvailableAgents(websiteToken);
- const result = await API.get(urlData.url, { params: urlData.params });
- return result;
+ return API.get(urlData.url, { params: urlData.params });
};
diff --git a/app/javascript/widget/api/campaign.js b/app/javascript/widget/api/campaign.js
index 57d81e084..efa154f41 100644
--- a/app/javascript/widget/api/campaign.js
+++ b/app/javascript/widget/api/campaign.js
@@ -3,8 +3,7 @@ import { API } from 'widget/helpers/axios';
const getCampaigns = async websiteToken => {
const urlData = endPoints.getCampaigns(websiteToken);
- const result = await API.get(urlData.url, { params: urlData.params });
- return result;
+ return API.get(urlData.url, { params: urlData.params });
};
const triggerCampaign = async ({
diff --git a/app/javascript/widget/api/conversation.js b/app/javascript/widget/api/conversation.js
index fdb3842fd..4cf4de25e 100755
--- a/app/javascript/widget/api/conversation.js
+++ b/app/javascript/widget/api/conversation.js
@@ -3,26 +3,22 @@ import { API } from 'widget/helpers/axios';
const createConversationAPI = async content => {
const urlData = endPoints.createConversation(content);
- const result = await API.post(urlData.url, urlData.params);
- return result;
+ return API.post(urlData.url, urlData.params);
};
const sendMessageAPI = async content => {
const urlData = endPoints.sendMessage(content);
- const result = await API.post(urlData.url, urlData.params);
- return result;
+ return API.post(urlData.url, urlData.params);
};
const sendAttachmentAPI = async attachment => {
const urlData = endPoints.sendAttachment(attachment);
- const result = await API.post(urlData.url, urlData.params);
- return result;
+ return API.post(urlData.url, urlData.params);
};
const getMessagesAPI = async ({ before }) => {
const urlData = endPoints.getConversation({ before });
- const result = await API.get(urlData.url, { params: urlData.params });
- return result;
+ return API.get(urlData.url, { params: urlData.params });
};
const getConversationAPI = async () => {
diff --git a/app/javascript/widget/assets/scss/woot.scss b/app/javascript/widget/assets/scss/woot.scss
index 9a2a6a8e6..3f882eb38 100755
--- a/app/javascript/widget/assets/scss/woot.scss
+++ b/app/javascript/widget/assets/scss/woot.scss
@@ -61,10 +61,7 @@ body {
.is-flat-design {
.chat-bubble {
- border-bottom-left-radius: 0 !important;
- border-bottom-right-radius: 0 !important;
- border-top-left-radius: 0 !important;
- border-top-right-radius: 0 !important;
+ border-radius: 0 !important;
box-shadow: none;
}
diff --git a/app/javascript/widget/components/AgentMessage.vue b/app/javascript/widget/components/AgentMessage.vue
index c338dce83..6b1fb782b 100755
--- a/app/javascript/widget/components/AgentMessage.vue
+++ b/app/javascript/widget/components/AgentMessage.vue
@@ -104,8 +104,7 @@ export default {
) {
return false;
}
- if (!this.message.content) return false;
- return true;
+ return this.message.content;
},
readableTime() {
const { created_at: createdAt = '' } = this.message;
diff --git a/app/javascript/widget/components/ChatAttachment.vue b/app/javascript/widget/components/ChatAttachment.vue
index 87da3f5df..0412ca5fb 100755
--- a/app/javascript/widget/components/ChatAttachment.vue
+++ b/app/javascript/widget/components/ChatAttachment.vue
@@ -54,9 +54,9 @@ export default {
},
async onFileUpload(file) {
if (this.globalConfig.directUploadsEnabled) {
- this.onDirectFileUpload(file);
+ await this.onDirectFileUpload(file);
} else {
- this.onIndirectFileUpload(file);
+ await this.onIndirectFileUpload(file);
}
},
async onDirectFileUpload(file) {
diff --git a/app/javascript/widget/components/ChatHeaderExpanded.vue b/app/javascript/widget/components/ChatHeaderExpanded.vue
index c1823f504..f82f66c46 100755
--- a/app/javascript/widget/components/ChatHeaderExpanded.vue
+++ b/app/javascript/widget/components/ChatHeaderExpanded.vue
@@ -7,7 +7,12 @@
class="flex items-start"
:class="[avatarUrl ? 'justify-between' : 'justify-end']"
>
-
+
{
- if (
+ return !(
(isUserEmailAvailable && field.name === 'emailAddress') ||
(isUserPhoneNumberAvailable && field.name === 'phoneNumber')
- ) {
- return false;
- }
- return true;
+ );
});
},
enabledPreChatFields() {
diff --git a/app/javascript/widget/components/TeamAvailability.vue b/app/javascript/widget/components/TeamAvailability.vue
index 1f4c5d046..a3a6b0c02 100644
--- a/app/javascript/widget/components/TeamAvailability.vue
+++ b/app/javascript/widget/components/TeamAvailability.vue
@@ -13,7 +13,7 @@
}}
- {{ replyWaitMeessage }}
+ {{ replyWaitMessage }}
@@ -75,7 +75,7 @@ export default {
}
return anyAgentOnline;
},
- replyWaitMeessage() {
+ replyWaitMessage() {
const { workingHoursEnabled } = this.channelConfig;
if (this.isOnline) {
diff --git a/app/javascript/widget/components/UnreadMessageList.vue b/app/javascript/widget/components/UnreadMessageList.vue
index 19afe49d1..6683c585e 100644
--- a/app/javascript/widget/components/UnreadMessageList.vue
+++ b/app/javascript/widget/components/UnreadMessageList.vue
@@ -107,13 +107,12 @@ export default {
.clear-button {
background: transparent;
color: $color-woot;
- padding: 0;
border: 0;
font-weight: $font-weight-bold;
font-size: $font-size-medium;
transition: all 0.3s var(--ease-in-cubic);
margin-left: $space-smaller;
- padding-right: $space-one;
+ padding: 0 $space-one 0 0;
&:hover {
transform: translateX($space-smaller);
diff --git a/app/javascript/widget/helpers/IframeEventHelper.js b/app/javascript/widget/helpers/IframeEventHelper.js
index 953802df8..40c0e1f1c 100644
--- a/app/javascript/widget/helpers/IframeEventHelper.js
+++ b/app/javascript/widget/helpers/IframeEventHelper.js
@@ -10,7 +10,7 @@ export const loadedEventConfig = () => {
export const getExtraSpaceToScroll = () => {
// This function calculates the extra space needed for the view to
- // accomodate the height of close button + height of
+ // accommodate the height of close button + height of
// read messages button. So that scrollbar won't appear
const unreadMessageWrap = document.querySelector('.unread-messages');
const unreadCloseWrap = document.querySelector('.close-unread-wrap');
diff --git a/app/javascript/widget/helpers/utils.js b/app/javascript/widget/helpers/utils.js
index 1a4b2c1d6..a4d5dafa3 100755
--- a/app/javascript/widget/helpers/utils.js
+++ b/app/javascript/widget/helpers/utils.js
@@ -3,13 +3,6 @@ import { WOOT_PREFIX } from './constants';
export const isEmptyObject = obj =>
Object.keys(obj).length === 0 && obj.constructor === Object;
-export const arrayToHashById = array =>
- array.reduce((map, obj) => {
- const newMap = map;
- newMap[obj.id] = obj;
- return newMap;
- }, {});
-
export const sendMessage = msg => {
window.parent.postMessage(
`chatwoot-widget:${JSON.stringify({ ...msg })}`,
@@ -22,9 +15,7 @@ export const IFrameHelper = {
sendMessage,
isAValidEvent: e => {
const isDataAString = typeof e.data === 'string';
- const isAValidWootEvent =
- isDataAString && e.data.indexOf(WOOT_PREFIX) === 0;
- return isAValidWootEvent;
+ return isDataAString && e.data.indexOf(WOOT_PREFIX) === 0;
},
getMessage: e => JSON.parse(e.data.replace(WOOT_PREFIX, '')),
};
diff --git a/app/javascript/widget/store/modules/conversation/getters.js b/app/javascript/widget/store/modules/conversation/getters.js
index 9d1c067f4..74e582348 100644
--- a/app/javascript/widget/store/modules/conversation/getters.js
+++ b/app/javascript/widget/store/modules/conversation/getters.js
@@ -32,7 +32,7 @@ export const getters = {
},
getUnreadMessageCount: _state => {
const { userLastSeenAt } = _state.meta;
- const count = Object.values(_state.conversations).filter(chat => {
+ return Object.values(_state.conversations).filter(chat => {
const { created_at: createdAt, message_type: messageType } = chat;
const isOutGoing = messageType === MESSAGE_TYPE.OUTGOING;
const hasNotSeen = userLastSeenAt
@@ -40,7 +40,6 @@ export const getters = {
: true;
return hasNotSeen && isOutGoing;
}).length;
- return count;
},
getUnreadTextMessages: (_state, _getters) => {
const unreadCount = _getters.getUnreadMessageCount;
@@ -50,7 +49,6 @@ export const getters = {
return messageType === MESSAGE_TYPE.OUTGOING;
});
const maxUnreadCount = Math.min(unreadCount, 3);
- const allUnreadMessages = unreadAgentMessages.splice(-maxUnreadCount);
- return allUnreadMessages;
+ return unreadAgentMessages.splice(-maxUnreadCount);
},
};
diff --git a/app/javascript/widget/store/modules/conversation/helpers.js b/app/javascript/widget/store/modules/conversation/helpers.js
index 44e2a6729..2ebac5242 100644
--- a/app/javascript/widget/store/modules/conversation/helpers.js
+++ b/app/javascript/widget/store/modules/conversation/helpers.js
@@ -29,7 +29,7 @@ const shouldShowAvatar = (message, nextMessage) => {
export const groupConversationBySender = conversationsForADate =>
conversationsForADate.map((message, index) => {
- let showAvatar = false;
+ let showAvatar;
const isLastMessage = index === conversationsForADate.length - 1;
if (isASubmittedFormMessage(message)) {
showAvatar = false;
diff --git a/app/javascript/widget/store/modules/conversation/mutations.js b/app/javascript/widget/store/modules/conversation/mutations.js
index f47971f07..ca6dafada 100644
--- a/app/javascript/widget/store/modules/conversation/mutations.js
+++ b/app/javascript/widget/store/modules/conversation/mutations.js
@@ -88,8 +88,7 @@ export const mutations = {
},
toggleAgentTypingStatus($state, { status }) {
- const isTyping = status === 'on';
- $state.uiFlags.isAgentTyping = isTyping;
+ $state.uiFlags.isAgentTyping = status === 'on';
},
setMetaUserLastSeenAt($state, lastSeen) {
diff --git a/app/javascript/widget/store/modules/conversationLabels.js b/app/javascript/widget/store/modules/conversationLabels.js
index 3fbcd230d..3ae600082 100644
--- a/app/javascript/widget/store/modules/conversationLabels.js
+++ b/app/javascript/widget/store/modules/conversationLabels.js
@@ -9,14 +9,14 @@ export const actions = {
try {
await conversationLabels.create(label);
} catch (error) {
- // Ingore error
+ // Ignore error
}
},
destroy: async (_, label) => {
try {
await conversationLabels.destroy(label);
} catch (error) {
- // Ingore error
+ // Ignore error
}
},
};
diff --git a/app/mailboxes/mailbox_helper.rb b/app/mailboxes/mailbox_helper.rb
index 1519343ca..216d5c2c3 100644
--- a/app/mailboxes/mailbox_helper.rb
+++ b/app/mailboxes/mailbox_helper.rb
@@ -34,7 +34,7 @@ module MailboxHelper
end
def create_contact
- @contact_inbox = ::ContactBuilder.new(
+ @contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: processed_mail.original_sender,
inbox: @inbox,
contact_attributes: {
diff --git a/app/models/channel/facebook_page.rb b/app/models/channel/facebook_page.rb
index 2153708e2..bba45f326 100644
--- a/app/models/channel/facebook_page.rb
+++ b/app/models/channel/facebook_page.rb
@@ -37,16 +37,11 @@ class Channel::FacebookPage < ApplicationRecord
end
def create_contact_inbox(instagram_id, name)
- ActiveRecord::Base.transaction do
- contact = inbox.account.contacts.create!(name: name)
- ::ContactInbox.create!(
- contact_id: contact.id,
- inbox_id: inbox.id,
- source_id: instagram_id
- )
- rescue StandardError => e
- Rails.logger.error e
- end
+ @contact_inbox = ::ContactInboxWithContactBuilder.new({
+ source_id: instagram_id,
+ inbox: inbox,
+ contact_attributes: { name: name }
+ }).perform
end
def subscribe
diff --git a/app/models/channel/twitter_profile.rb b/app/models/channel/twitter_profile.rb
index 4f6fa7ba1..d0f765e9f 100644
--- a/app/models/channel/twitter_profile.rb
+++ b/app/models/channel/twitter_profile.rb
@@ -32,16 +32,11 @@ class Channel::TwitterProfile < ApplicationRecord
end
def create_contact_inbox(profile_id, name, additional_attributes)
- ActiveRecord::Base.transaction do
- contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name)
- ::ContactInbox.create!(
- contact_id: contact.id,
- inbox_id: inbox.id,
- source_id: profile_id
- )
- rescue StandardError => e
- Rails.logger.error e
- end
+ ::ContactInboxWithContactBuilder.new({
+ source_id: profile_id,
+ inbox: inbox,
+ contact_attributes: { name: name, additional_attributes: additional_attributes }
+ }).perform
end
def twitter_client
diff --git a/app/models/channel/web_widget.rb b/app/models/channel/web_widget.rb
index b85443633..59d392892 100644
--- a/app/models/channel/web_widget.rb
+++ b/app/models/channel/web_widget.rb
@@ -98,19 +98,9 @@ class Channel::WebWidget < ApplicationRecord
end
def create_contact_inbox(additional_attributes = {})
- ActiveRecord::Base.transaction do
- contact = inbox.account.contacts.create!(
- name: ::Haikunator.haikunate(1000),
- additional_attributes: additional_attributes
- )
- contact_inbox = ::ContactInbox.create!(
- contact_id: contact.id,
- inbox_id: inbox.id,
- source_id: SecureRandom.uuid
- )
- contact_inbox
- rescue StandardError => e
- Rails.logger.error e
- end
+ ::ContactInboxWithContactBuilder.new({
+ inbox: inbox,
+ contact_attributes: { additional_attributes: additional_attributes }
+ }).perform
end
end
diff --git a/app/models/working_hour.rb b/app/models/working_hour.rb
index 885165da2..01b972bd4 100644
--- a/app/models/working_hour.rb
+++ b/app/models/working_hour.rb
@@ -40,7 +40,10 @@ class WorkingHour < ApplicationRecord
validate :open_all_day_and_closed_all_day
def self.today
- find_by(day_of_week: Date.current.wday)
+ # While getting the day of the week, consider the timezone as well. `first` would
+ # return the first working hour from the list of working hours available per week.
+ inbox = first.inbox
+ find_by(day_of_week: Time.zone.now.in_time_zone(inbox.timezone).to_date.wday)
end
def open_at?(time)
diff --git a/app/services/line/incoming_message_service.rb b/app/services/line/incoming_message_service.rb
index 535c03dc7..48a52eb8f 100644
--- a/app/services/line/incoming_message_service.rb
+++ b/app/services/line/incoming_message_service.rb
@@ -81,7 +81,7 @@ class Line::IncomingMessageService
end
def set_contact
- contact_inbox = ::ContactBuilder.new(
+ contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: line_contact_info['userId'],
inbox: inbox,
contact_attributes: contact_attributes
diff --git a/app/services/sms/incoming_message_service.rb b/app/services/sms/incoming_message_service.rb
index 7ee6e3e63..7aa22b19e 100644
--- a/app/services/sms/incoming_message_service.rb
+++ b/app/services/sms/incoming_message_service.rb
@@ -37,7 +37,7 @@ class Sms::IncomingMessageService
end
def set_contact
- contact_inbox = ::ContactBuilder.new(
+ contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: params[:from],
inbox: @inbox,
contact_attributes: contact_attributes
diff --git a/app/services/telegram/incoming_message_service.rb b/app/services/telegram/incoming_message_service.rb
index a26bda14e..e8ab8a72c 100644
--- a/app/services/telegram/incoming_message_service.rb
+++ b/app/services/telegram/incoming_message_service.rb
@@ -31,7 +31,7 @@ class Telegram::IncomingMessageService
end
def set_contact
- contact_inbox = ::ContactBuilder.new(
+ contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: params[:message][:from][:id],
inbox: inbox,
contact_attributes: contact_attributes
diff --git a/app/services/twilio/incoming_message_service.rb b/app/services/twilio/incoming_message_service.rb
index 50c77111c..4473131df 100644
--- a/app/services/twilio/incoming_message_service.rb
+++ b/app/services/twilio/incoming_message_service.rb
@@ -47,7 +47,7 @@ class Twilio::IncomingMessageService
end
def set_contact
- contact_inbox = ::ContactBuilder.new(
+ contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: params[:From],
inbox: inbox,
contact_attributes: contact_attributes
diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb
index e55d5fd26..da08e02d6 100644
--- a/app/services/whatsapp/incoming_message_base_service.rb
+++ b/app/services/whatsapp/incoming_message_base_service.rb
@@ -12,7 +12,7 @@ class Whatsapp::IncomingMessageBaseService
set_conversation
- return if @processed_params[:messages].blank?
+ return if @processed_params[:messages].blank? || unprocessable_message_type?
@message = @conversation.messages.build(
content: message_content(@processed_params[:messages].first),
@@ -48,7 +48,7 @@ class Whatsapp::IncomingMessageBaseService
contact_params = @processed_params[:contacts]&.first
return if contact_params.blank?
- contact_inbox = ::ContactBuilder.new(
+ contact_inbox = ::ContactInboxWithContactBuilder.new(
source_id: contact_params[:wa_id],
inbox: inbox,
contact_attributes: { name: contact_params.dig(:profile, :name), phone_number: "+#{@processed_params[:messages].first[:from]}" }
@@ -86,6 +86,10 @@ class Whatsapp::IncomingMessageBaseService
@processed_params[:messages].first[:type]
end
+ def unprocessable_message_type?
+ %w[reaction contacts].include?(message_type)
+ end
+
def attach_files
return if %w[text button interactive].include?(message_type)
diff --git a/db/seeds.rb b/db/seeds.rb
index c4c7eda71..ead862669 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -46,8 +46,13 @@ unless Rails.env.production?
inbox = Inbox.create!(channel: web_widget, account: account, name: 'Acme Support')
InboxMember.create!(user: user, inbox: inbox)
- contact = Contact.create!(name: 'jane', email: 'jane@example.com', phone_number: '+2320000', account: account)
- contact_inbox = ContactInbox.create!(inbox: inbox, contact: contact, source_id: user.id, hmac_verified: true)
+ contact = ::ContactInboxWithContactBuilder.new(
+ source_id: user.id,
+ inbox: inbox,
+ hmac_verified: true,
+ contact_attributes: { name: 'jane', email: 'jane@example.com', phone_number: '+2320000' }
+ ).perform&.contact
+
conversation = Conversation.create!(
account: account,
inbox: inbox,
diff --git a/spec/builders/contact_inbox_builder_spec.rb b/spec/builders/contact_inbox_builder_spec.rb
index 47210f6ca..46b0d749c 100644
--- a/spec/builders/contact_inbox_builder_spec.rb
+++ b/spec/builders/contact_inbox_builder_spec.rb
@@ -12,8 +12,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id,
+ contact: contact,
+ inbox: twilio_inbox,
source_id: contact.phone_number
).perform
@@ -23,8 +23,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id
+ contact: contact,
+ inbox: twilio_inbox
).perform
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
@@ -33,8 +33,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id,
+ contact: contact,
+ inbox: twilio_inbox,
source_id: '+224213223422'
).perform
@@ -44,12 +44,23 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id
+ contact: contact,
+ inbox: twilio_inbox
).perform
expect(contact_inbox.source_id).to eq(contact.phone_number)
end
+
+ it 'raises error when contact phone number is not present and no source id is provided' do
+ contact.update!(phone_number: nil)
+
+ expect do
+ described_class.new(
+ contact: contact,
+ inbox: twilio_inbox
+ ).perform
+ end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number')
+ end
end
describe 'twilio whatsapp inbox' do
@@ -59,8 +70,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id,
+ contact: contact,
+ inbox: twilio_inbox,
source_id: "whatsapp:#{contact.phone_number}"
).perform
@@ -70,8 +81,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id
+ contact: contact,
+ inbox: twilio_inbox
).perform
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
@@ -80,8 +91,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id,
+ contact: contact,
+ inbox: twilio_inbox,
source_id: 'whatsapp:+555555'
).perform
@@ -91,12 +102,23 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twilio_inbox.id
+ contact: contact,
+ inbox: twilio_inbox
).perform
expect(contact_inbox.source_id).to eq("whatsapp:#{contact.phone_number}")
end
+
+ it 'raises error when contact phone number is not present and no source id is provided' do
+ contact.update!(phone_number: nil)
+
+ expect do
+ described_class.new(
+ contact: contact,
+ inbox: twilio_inbox
+ ).perform
+ end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number')
+ end
end
describe 'whatsapp inbox' do
@@ -105,8 +127,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: whatsapp_inbox.id,
+ contact: contact,
+ inbox: whatsapp_inbox,
source_id: contact.phone_number&.delete('+')
).perform
@@ -116,8 +138,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: whatsapp_inbox.id
+ contact: contact,
+ inbox: whatsapp_inbox
).perform
expect(contact_inbox.id).to be(existing_contact_inbox.id)
@@ -126,8 +148,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: whatsapp_inbox.id,
+ contact: contact,
+ inbox: whatsapp_inbox,
source_id: '555555'
).perform
@@ -137,12 +159,23 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: whatsapp_inbox.id
+ contact: contact,
+ inbox: whatsapp_inbox
).perform
expect(contact_inbox.source_id).to eq(contact.phone_number&.delete('+'))
end
+
+ it 'raises error when contact phone number is not present and no source id is provided' do
+ contact.update!(phone_number: nil)
+
+ expect do
+ described_class.new(
+ contact: contact,
+ inbox: whatsapp_inbox
+ ).perform
+ end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number')
+ end
end
describe 'sms inbox' do
@@ -152,8 +185,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: sms_inbox.id,
+ contact: contact,
+ inbox: sms_inbox,
source_id: contact.phone_number
).perform
@@ -163,8 +196,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: sms_inbox.id
+ contact: contact,
+ inbox: sms_inbox
).perform
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
@@ -173,8 +206,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: sms_inbox.id,
+ contact: contact,
+ inbox: sms_inbox,
source_id: '+224213223422'
).perform
@@ -184,12 +217,23 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: sms_inbox.id
+ contact: contact,
+ inbox: sms_inbox
).perform
expect(contact_inbox.source_id).to eq(contact.phone_number)
end
+
+ it 'raises error when contact phone number is not present and no source id is provided' do
+ contact.update!(phone_number: nil)
+
+ expect do
+ described_class.new(
+ contact: contact,
+ inbox: sms_inbox
+ ).perform
+ end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number')
+ end
end
describe 'email inbox' do
@@ -199,8 +243,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: email_inbox.id,
+ contact: contact,
+ inbox: email_inbox,
source_id: contact.email
).perform
@@ -210,8 +254,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with email and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: email_inbox.id
+ contact: contact,
+ inbox: email_inbox
).perform
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
@@ -220,8 +264,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: email_inbox.id,
+ contact: contact,
+ inbox: email_inbox,
source_id: 'xyc@xyc.com'
).perform
@@ -231,12 +275,23 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with contact email when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: email_inbox.id
+ contact: contact,
+ inbox: email_inbox
).perform
expect(contact_inbox.source_id).to eq(contact.email)
end
+
+ it 'raises error when contact email is not present and no source id is provided' do
+ contact.update!(email: nil)
+
+ expect do
+ described_class.new(
+ contact: contact,
+ inbox: email_inbox
+ ).perform
+ end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact email')
+ end
end
describe 'api inbox' do
@@ -246,8 +301,8 @@ describe ::ContactInboxBuilder do
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: 'test')
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: api_inbox.id,
+ contact: contact,
+ inbox: api_inbox,
source_id: 'test'
).perform
@@ -257,8 +312,8 @@ describe ::ContactInboxBuilder do
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: SecureRandom.uuid)
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: api_inbox.id,
+ contact: contact,
+ inbox: api_inbox,
source_id: 'test'
).perform
@@ -268,61 +323,12 @@ describe ::ContactInboxBuilder do
it 'creates a contact inbox with SecureRandom.uuid when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: api_inbox.id
+ contact: contact,
+ inbox: api_inbox
).perform
expect(contact_inbox.source_id).not_to be_nil
end
end
-
- describe 'web widget' do
- let!(:website_channel) { create(:channel_widget, account: account) }
- let!(:website_inbox) { create(:inbox, channel: website_channel, account: account) }
-
- it 'does not create contact inbox' do
- contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: website_inbox.id,
- source_id: 'test'
- ).perform
-
- expect(contact_inbox).to be_nil
- end
- end
-
- describe 'facebook inbox' do
- before do
- stub_request(:post, /graph.facebook.com/)
- end
-
- let!(:facebook_channel) { create(:channel_facebook_page, account: account) }
- let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }
-
- it 'does not create contact inbox' do
- contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: facebook_inbox.id,
- source_id: 'test'
- ).perform
-
- expect(contact_inbox).to be_nil
- end
- end
-
- describe 'twitter inbox' do
- let!(:twitter_channel) { create(:channel_twitter_profile, account: account) }
- let!(:twitter_inbox) { create(:inbox, channel: twitter_channel, account: account) }
-
- it 'does not create contact inbox' do
- contact_inbox = described_class.new(
- contact_id: contact.id,
- inbox_id: twitter_inbox.id,
- source_id: 'test'
- ).perform
-
- expect(contact_inbox).to be_nil
- end
- end
end
end
diff --git a/spec/builders/contact_builder_spec.rb b/spec/builders/contact_inbox_with_contact_builder_spec.rb
similarity index 98%
rename from spec/builders/contact_builder_spec.rb
rename to spec/builders/contact_inbox_with_contact_builder_spec.rb
index 29df0da22..e76d199d4 100644
--- a/spec/builders/contact_builder_spec.rb
+++ b/spec/builders/contact_inbox_with_contact_builder_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe ::ContactBuilder do
+describe ::ContactInboxWithContactBuilder do
let(:account) { create(:account) }
let(:inbox) { create(:inbox, account: account) }
let(:contact) { create(:contact, email: 'xyc@example.com', phone_number: '+23423424123', account: account, identifier: '123') }
diff --git a/spec/jobs/webhooks/whatsapp_events_job_spec.rb b/spec/jobs/webhooks/whatsapp_events_job_spec.rb
index 2a28fe41f..00fde3d41 100644
--- a/spec/jobs/webhooks/whatsapp_events_job_spec.rb
+++ b/spec/jobs/webhooks/whatsapp_events_job_spec.rb
@@ -62,6 +62,59 @@ RSpec.describe Webhooks::WhatsappEventsJob, type: :job do
job.perform_now(wb_params)
end
+ it 'Ignore reaction type message and stop raising error' do
+ other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
+ validate_provider_config: false)
+ wb_params = {
+ phone_number: channel.phone_number,
+ object: 'whatsapp_business_account',
+ entry: [{
+ changes: [{
+ value: {
+ contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }],
+ messages: [{
+ from: '1111981136571', reaction: { emoji: '👍' }, timestamp: '1664799904', type: 'reaction'
+ }],
+ metadata: {
+ phone_number_id: other_channel.provider_config['phone_number_id'],
+ display_phone_number: other_channel.phone_number.delete('+')
+ }
+ }
+ }]
+ }]
+ }.with_indifferent_access
+ expect do
+ Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
+ end.not_to change(Message, :count)
+ end
+
+ it 'Ignore contacts type message and stop raising error' do
+ other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
+ validate_provider_config: false)
+ wb_params = {
+ phone_number: channel.phone_number,
+ object: 'whatsapp_business_account',
+ entry: [{
+ changes: [{
+ value: {
+ contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }],
+ messages: [{ from: '1111981136571',
+ contacts: [{ phones: [{ phone: '+1987654' }], name: { first_name: 'contact name' } }],
+ timestamp: '1664799904',
+ type: 'contacts' }],
+ metadata: {
+ phone_number_id: other_channel.provider_config['phone_number_id'],
+ display_phone_number: other_channel.phone_number.delete('+')
+ }
+ }
+ }]
+ }]
+ }.with_indifferent_access
+ expect do
+ Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
+ end.not_to change(Message, :count)
+ end
+
it 'will not enque Whatsapp::IncomingMessageWhatsappCloudService when invalid phone number id' do
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
validate_provider_config: false)
diff --git a/spec/models/working_hour_spec.rb b/spec/models/working_hour_spec.rb
index c126f6f87..a5018a28e 100644
--- a/spec/models/working_hour_spec.rb
+++ b/spec/models/working_hour_spec.rb
@@ -88,4 +88,18 @@ RSpec.describe WorkingHour do
'Validation failed: open_all_day and closed_all_day cannot be true at the same time')
end
end
+
+ context 'when on monday 9am in Sydney timezone' do
+ let(:inbox) { create(:inbox) }
+
+ before do
+ Time.zone = 'Australia/Sydney'
+ inbox.update(timezone: 'Australia/Sydney')
+ travel_to '10.10.2022 9:00 AEDT'
+ end
+
+ it 'is considered working hour' do
+ expect(described_class.today.open_now?).to be true
+ end
+ end
end
diff --git a/spec/services/contacts/filter_service_spec.rb b/spec/services/contacts/filter_service_spec.rb
index 5f52a1a3c..7e6661aed 100644
--- a/spec/services/contacts/filter_service_spec.rb
+++ b/spec/services/contacts/filter_service_spec.rb
@@ -167,6 +167,7 @@ describe ::Contacts::FilterService do
context 'with x_days_before filter' do
before do
+ Time.zone = 'UTC'
el_contact.update(last_activity_at: (Time.zone.today - 4.days))
cs_contact.update(last_activity_at: (Time.zone.today - 5.days))
en_contact.update(last_activity_at: (Time.zone.today - 2.days))
diff --git a/spec/services/conversations/filter_service_spec.rb b/spec/services/conversations/filter_service_spec.rb
index ed489bd30..a17351ec3 100644
--- a/spec/services/conversations/filter_service_spec.rb
+++ b/spec/services/conversations/filter_service_spec.rb
@@ -309,6 +309,7 @@ describe ::Conversations::FilterService do
context 'with x_days_before filter' do
before do
+ Time.zone = 'UTC'
en_conversation_1.update!(last_activity_at: (Time.zone.today - 4.days))
en_conversation_2.update!(last_activity_at: (Time.zone.today - 5.days))
user_2_assigned_conversation.update!(last_activity_at: (Time.zone.today - 2.days))
|