fix: Add a check for 24 hour window before sending a message (#1084)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
parent
12ee7e5d82
commit
0f2d3418f9
26 changed files with 292 additions and 9 deletions
|
@ -282,3 +282,4 @@ exclude:
|
||||||
- 'app/javascript/widget/assets/scss/_reset.scss'
|
- 'app/javascript/widget/assets/scss/_reset.scss'
|
||||||
- 'app/javascript/widget/assets/scss/sdk.css'
|
- 'app/javascript/widget/assets/scss/sdk.css'
|
||||||
- 'app/assets/stylesheets/administrate/reset/_normalize.scss'
|
- 'app/assets/stylesheets/administrate/reset/_normalize.scss'
|
||||||
|
- 'app/javascript/shared/assets/stylesheets/*.scss'
|
||||||
|
|
|
@ -20,9 +20,10 @@ class MessageApi extends ApiClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAttachment([conversationId, { file }]) {
|
sendAttachment([conversationId, { file, isPrivate = false }]) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('attachments[]', file, file.name);
|
formData.append('attachments[]', file, file.name);
|
||||||
|
formData.append('private', isPrivate);
|
||||||
return axios({
|
return axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `${this.url}/${conversationId}/messages`,
|
url: `${this.url}/${conversationId}/messages`,
|
||||||
|
|
|
@ -46,8 +46,8 @@ $color-gray: #6e6f73;
|
||||||
$color-light-gray: #999a9b;
|
$color-light-gray: #999a9b;
|
||||||
$color-border: #e0e6ed;
|
$color-border: #e0e6ed;
|
||||||
$color-border-light: #f0f4f5;
|
$color-border-light: #f0f4f5;
|
||||||
$color-background: #f4f6fb;
|
|
||||||
$color-border-dark: #cad0d4;
|
$color-border-dark: #cad0d4;
|
||||||
|
$color-background: #f4f6fb;
|
||||||
$color-background-light: #f9fafc;
|
$color-background-light: #f9fafc;
|
||||||
$color-white: #fff;
|
$color-white: #fff;
|
||||||
$color-body: #3c4858;
|
$color-body: #3c4858;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@import 'shared/assets/fonts/inter';
|
@import 'shared/assets/fonts/inter';
|
||||||
|
@import 'shared/assets/stylesheets/colors';
|
||||||
|
@import 'shared/assets/stylesheets/spacing';
|
||||||
|
@import 'shared/assets/stylesheets/font-size';
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
|
|
||||||
@import '~spinkit/scss/spinners/7-three-bounce';
|
@import '~spinkit/scss/spinners/7-three-bounce';
|
||||||
|
|
|
@ -5,6 +5,18 @@
|
||||||
:is-contact-panel-open="isContactPanelOpen"
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
@contactPanelToggle="onToggleContactPanel"
|
@contactPanelToggle="onToggleContactPanel"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="!currentChat.can_reply" class="messenger-policy--banner">
|
||||||
|
<span>
|
||||||
|
{{ $t('CONVERSATION.CANNOT_REPLY') }}
|
||||||
|
<a
|
||||||
|
href="https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/"
|
||||||
|
rel="noopener noreferrer nofollow"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ $t('CONVERSATION.24_HOURS_WINDOW') }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<ul class="conversation-panel">
|
<ul class="conversation-panel">
|
||||||
<transition name="slide-up">
|
<transition name="slide-up">
|
||||||
<li>
|
<li>
|
||||||
|
@ -210,3 +222,19 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.messenger-policy--banner {
|
||||||
|
background: var(--r-400);
|
||||||
|
color: var(--white);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
padding: var(--space-slab) var(--space-normal);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--white);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
message: '',
|
message: '',
|
||||||
isPrivate: false,
|
isPrivateTabActive: false,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
showCannedResponsesList: false,
|
showCannedResponsesList: false,
|
||||||
|
@ -117,6 +117,12 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ currentChat: 'getSelectedChat' }),
|
...mapGetters({ currentChat: 'getSelectedChat' }),
|
||||||
|
isPrivate() {
|
||||||
|
if (this.currentChat.can_reply) {
|
||||||
|
return this.isPrivateTabActive;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
inboxId() {
|
inboxId() {
|
||||||
return this.currentChat.inbox_id;
|
return this.currentChat.inbox_id;
|
||||||
},
|
},
|
||||||
|
@ -214,6 +220,13 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
currentChat(conversation) {
|
||||||
|
if (conversation.can_reply) {
|
||||||
|
this.isPrivateTabActive = false;
|
||||||
|
} else {
|
||||||
|
this.isPrivateTabActive = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
message(updatedMessage) {
|
message(updatedMessage) {
|
||||||
if (this.isPrivate) {
|
if (this.isPrivate) {
|
||||||
return;
|
return;
|
||||||
|
@ -278,11 +291,11 @@ export default {
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
setPrivateReplyMode() {
|
setPrivateReplyMode() {
|
||||||
this.isPrivate = true;
|
this.isPrivateTabActive = true;
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
},
|
},
|
||||||
setReplyMode() {
|
setReplyMode() {
|
||||||
this.isPrivate = false;
|
this.isPrivateTabActive = false;
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
},
|
},
|
||||||
emojiOnClick(emoji) {
|
emojiOnClick(emoji) {
|
||||||
|
@ -327,7 +340,10 @@ export default {
|
||||||
}
|
}
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])
|
.dispatch('sendAttachment', [
|
||||||
|
this.currentChat.id,
|
||||||
|
{ file: file.file, isPrivate: this.isPrivate },
|
||||||
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
this.$emit('scrollToMessage');
|
this.$emit('scrollToMessage');
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
"CLICK_HERE": "Click here",
|
"CLICK_HERE": "Click here",
|
||||||
"LOADING_INBOXES": "Loading inboxes",
|
"LOADING_INBOXES": "Loading inboxes",
|
||||||
"LOADING_CONVERSATIONS": "Loading Conversations",
|
"LOADING_CONVERSATIONS": "Loading Conversations",
|
||||||
|
"CANNOT_REPLY": "You cannot reply due to",
|
||||||
|
"24_HOURS_WINDOW": "24 hour message window restriction",
|
||||||
"DOWNLOAD": "Download",
|
"DOWNLOAD": "Download",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Resolve",
|
"RESOLVE_ACTION": "Resolve",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
||||||
import * as types from '../../mutation-types';
|
import * as types from '../../mutation-types';
|
||||||
import ConversationApi from '../../../api/inbox/conversation';
|
import ConversationApi from '../../../api/inbox/conversation';
|
||||||
import MessageApi from '../../../api/inbox/message';
|
import MessageApi from '../../../api/inbox/message';
|
||||||
|
import { MESSAGE_TYPE } from 'widget/helpers/constants';
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
const actions = {
|
const actions = {
|
||||||
|
@ -136,6 +137,12 @@ const actions = {
|
||||||
|
|
||||||
addMessage({ commit }, message) {
|
addMessage({ commit }, message) {
|
||||||
commit(types.default.ADD_MESSAGE, message);
|
commit(types.default.ADD_MESSAGE, message);
|
||||||
|
if (message.message_type === MESSAGE_TYPE.INCOMING) {
|
||||||
|
commit(types.default.SET_CONVERSATION_CAN_REPLY, {
|
||||||
|
conversationId: message.conversation_id,
|
||||||
|
canReply: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateMessage({ commit }, message) {
|
updateMessage({ commit }, message) {
|
||||||
|
|
|
@ -160,6 +160,16 @@ export const mutations = {
|
||||||
[types.default.SET_ACTIVE_INBOX](_state, inboxId) {
|
[types.default.SET_ACTIVE_INBOX](_state, inboxId) {
|
||||||
_state.currentInbox = inboxId ? parseInt(inboxId, 10) : null;
|
_state.currentInbox = inboxId ? parseInt(inboxId, 10) : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[types.default.SET_CONVERSATION_CAN_REPLY](
|
||||||
|
_state,
|
||||||
|
{ conversationId, canReply }
|
||||||
|
) {
|
||||||
|
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||||
|
if (chat) {
|
||||||
|
Vue.set(chat, 'can_reply', canReply);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -127,4 +127,30 @@ describe('#actions', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('#addMessage', () => {
|
||||||
|
it('sends correct mutations if message is incoming', () => {
|
||||||
|
const message = {
|
||||||
|
id: 1,
|
||||||
|
message_type: 0,
|
||||||
|
conversation_id: 1,
|
||||||
|
};
|
||||||
|
actions.addMessage({ commit }, message);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.ADD_MESSAGE, message],
|
||||||
|
[
|
||||||
|
types.default.SET_CONVERSATION_CAN_REPLY,
|
||||||
|
{ conversationId: 1, canReply: true },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct mutations if message is not an incoming message', () => {
|
||||||
|
const message = {
|
||||||
|
id: 1,
|
||||||
|
message_type: 1,
|
||||||
|
conversation_id: 1,
|
||||||
|
};
|
||||||
|
actions.addMessage({ commit }, message);
|
||||||
|
expect(commit.mock.calls).toEqual([[types.default.ADD_MESSAGE, message]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,4 +43,15 @@ describe('#mutations', () => {
|
||||||
expect(state.selectedChatId).toEqual(1);
|
expect(state.selectedChatId).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#SET_CONVERSATION_CAN_REPLY', () => {
|
||||||
|
it('set canReply flag', () => {
|
||||||
|
const state = { allConversations: [{ id: 1, can_reply: false }] };
|
||||||
|
mutations[types.SET_CONVERSATION_CAN_REPLY](state, {
|
||||||
|
conversationId: 1,
|
||||||
|
canReply: true,
|
||||||
|
});
|
||||||
|
expect(state.allConversations[0].can_reply).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default {
|
||||||
SET_CURRENT_USER: 'SET_CURRENT_USER',
|
SET_CURRENT_USER: 'SET_CURRENT_USER',
|
||||||
SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID',
|
SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID',
|
||||||
SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY',
|
SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY',
|
||||||
|
|
||||||
// Chat List
|
// Chat List
|
||||||
RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST',
|
RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST',
|
||||||
SET_ALL_CONVERSATION: 'SET_ALL_CONVERSATION',
|
SET_ALL_CONVERSATION: 'SET_ALL_CONVERSATION',
|
||||||
|
@ -17,7 +18,6 @@ export default {
|
||||||
UPDATE_ASSIGNEE: 'UPDATE_ASSIGNEE',
|
UPDATE_ASSIGNEE: 'UPDATE_ASSIGNEE',
|
||||||
UPDATE_CONVERSATION_CONTACT: 'UPDATE_CONVERSATION_CONTACT',
|
UPDATE_CONVERSATION_CONTACT: 'UPDATE_CONVERSATION_CONTACT',
|
||||||
|
|
||||||
// Active chat
|
|
||||||
SET_CURRENT_CHAT_WINDOW: 'SET_CURRENT_CHAT_WINDOW',
|
SET_CURRENT_CHAT_WINDOW: 'SET_CURRENT_CHAT_WINDOW',
|
||||||
CLEAR_CURRENT_CHAT_WINDOW: 'CLEAR_CURRENT_CHAT_WINDOW',
|
CLEAR_CURRENT_CHAT_WINDOW: 'CLEAR_CURRENT_CHAT_WINDOW',
|
||||||
CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
|
CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
|
||||||
|
@ -33,6 +33,8 @@ export default {
|
||||||
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
|
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
|
||||||
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
||||||
|
|
||||||
|
SET_CONVERSATION_CAN_REPLY: 'SET_CONVERSATION_CAN_REPLY',
|
||||||
|
|
||||||
// Inboxes
|
// Inboxes
|
||||||
SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG',
|
SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG',
|
||||||
SET_INBOXES: 'SET_INBOXES',
|
SET_INBOXES: 'SET_INBOXES',
|
||||||
|
|
69
app/javascript/shared/assets/stylesheets/colors.scss
Normal file
69
app/javascript/shared/assets/stylesheets/colors.scss
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
:root {
|
||||||
|
--white: #fff;
|
||||||
|
|
||||||
|
--w-50: #E3F2FF;
|
||||||
|
--w-100: #BBDDFF;
|
||||||
|
--w-200: #8FC9FF;
|
||||||
|
--w-300: #61B3FF;
|
||||||
|
--w-400: #3FA3FF;
|
||||||
|
--w-500: #1F93FF;
|
||||||
|
--w-600: #2284F0;
|
||||||
|
--w-700: #2272DC;
|
||||||
|
--w-800: #2161CA;
|
||||||
|
--w-900: #1F41AB;
|
||||||
|
|
||||||
|
--g-50: #E6F8E6;
|
||||||
|
--g-100: #C4EEC2;
|
||||||
|
--g-200: #9DE29A;
|
||||||
|
--g-300: #6FD86F;
|
||||||
|
--g-400: #44CE4B;
|
||||||
|
--g-500: #00C41D;
|
||||||
|
--g-600: #00B412;
|
||||||
|
--g-700: #00A200;
|
||||||
|
--g-800: #009000;
|
||||||
|
--g-900: #007000;
|
||||||
|
|
||||||
|
--y-50: #FFFEE8;
|
||||||
|
--y-100: #FFFAC5;
|
||||||
|
--y-200: #FFF69E;
|
||||||
|
--y-300: #FEF176;
|
||||||
|
--y-400: #FCEC56;
|
||||||
|
--y-500: #F9E736;
|
||||||
|
--y-600: #FFDD3A;
|
||||||
|
--y-700: #FFC532;
|
||||||
|
--y-800: #FDAD2A;
|
||||||
|
--y-900: #F9841B;
|
||||||
|
|
||||||
|
--s-50: #E7EEFB;
|
||||||
|
--s-100: #C8D6E6;
|
||||||
|
--s-200: #ABBACE;
|
||||||
|
--s-300: #8C9EB6;
|
||||||
|
--s-400: #7489A4;
|
||||||
|
--s-500: #5D7592;
|
||||||
|
--s-600: #506781;
|
||||||
|
--s-700: #40546B;
|
||||||
|
--s-800: #314155;
|
||||||
|
--s-900: #1F2D3D;
|
||||||
|
|
||||||
|
--b-50:#F8F9FE;
|
||||||
|
--b-100: #F2F3F7;
|
||||||
|
--b-200: #E9EAEF;
|
||||||
|
--b-300: #DADBDF;
|
||||||
|
--b-400: #B6B7BB;
|
||||||
|
--b-500: #96979C;
|
||||||
|
--b-600: #6E6F73;
|
||||||
|
--b-700: #5A5B5F;
|
||||||
|
--b-800: #3C3D40;
|
||||||
|
--b-900: #1B1C1F;
|
||||||
|
|
||||||
|
--r-50: #FFEBEE;
|
||||||
|
--r-100: #FFCCD1;
|
||||||
|
--r-200: #F69898;
|
||||||
|
--r-300: #EF6F6F;
|
||||||
|
--r-400: #F94B4A;
|
||||||
|
--r-500: #FF382D;
|
||||||
|
--r-600: #F02B2D;
|
||||||
|
--r-700: #DE1E27;
|
||||||
|
--r-800: #D11320;
|
||||||
|
--r-900: #C30011;
|
||||||
|
}
|
13
app/javascript/shared/assets/stylesheets/font-size.scss
Normal file
13
app/javascript/shared/assets/stylesheets/font-size.scss
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
:root {
|
||||||
|
--font-size-nano: 0.8rem;
|
||||||
|
--font-size-micro: 1.0rem;
|
||||||
|
--font-size-mini: 1.2rem;
|
||||||
|
--font-size-small: 1.4rem;
|
||||||
|
--font-size-default: 1.6rem;
|
||||||
|
--font-size-medium: 1.8rem;
|
||||||
|
--font-size-large: 2.2rem;
|
||||||
|
--font-size-big: 2.4rem;
|
||||||
|
--font-size-bigger: 3.0rem;
|
||||||
|
--font-size-mega: 3.4rem;
|
||||||
|
--font-size-giga: 4.0rem;
|
||||||
|
}
|
16
app/javascript/shared/assets/stylesheets/spacing.scss
Normal file
16
app/javascript/shared/assets/stylesheets/spacing.scss
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
:root {
|
||||||
|
// spaces
|
||||||
|
--space-zero: 0;
|
||||||
|
--space-micro: 0.2rem;
|
||||||
|
--space-smaller: 0.4rem;
|
||||||
|
--space-small: 0.8rem;
|
||||||
|
--space-one: 1rem;
|
||||||
|
--space-slab: 1.2rem;
|
||||||
|
--space-normal: 1.6rem;
|
||||||
|
--space-two: 2.0rem;
|
||||||
|
--space-medium: 2.4rem;
|
||||||
|
--space-large: 3.2rem;
|
||||||
|
--space-larger: 4.8rem;
|
||||||
|
--space-jumbo: 6.4rem;
|
||||||
|
--space-mega: 10.0rem;
|
||||||
|
}
|
|
@ -16,4 +16,8 @@ class Channel::Api < ApplicationRecord
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,6 +26,10 @@ class Channel::Email < ApplicationRecord
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
before_validation :ensure_forward_to_address, on: :create
|
before_validation :ensure_forward_to_address, on: :create
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def ensure_forward_to_address
|
def ensure_forward_to_address
|
||||||
|
|
|
@ -28,6 +28,10 @@ class Channel::FacebookPage < ApplicationRecord
|
||||||
after_create_commit :subscribe
|
after_create_commit :subscribe
|
||||||
before_destroy :unsubscribe
|
before_destroy :unsubscribe
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def subscribe
|
def subscribe
|
||||||
# ref https://developers.facebook.com/docs/messenger-platform/reference/webhook-events
|
# ref https://developers.facebook.com/docs/messenger-platform/reference/webhook-events
|
||||||
response = Facebook::Messenger::Subscriptions.subscribe(
|
response = Facebook::Messenger::Subscriptions.subscribe(
|
||||||
|
|
|
@ -30,6 +30,10 @@ class Channel::TwilioSms < ApplicationRecord
|
||||||
|
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
'Twilio SMS'
|
'Twilio SMS'
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,6 +26,10 @@ class Channel::TwitterProfile < ApplicationRecord
|
||||||
|
|
||||||
before_destroy :unsubscribe
|
before_destroy :unsubscribe
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def create_contact_inbox(profile_id, name, additional_attributes)
|
def create_contact_inbox(profile_id, name, additional_attributes)
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name)
|
contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name)
|
||||||
|
|
|
@ -27,6 +27,10 @@ class Channel::WebWidget < ApplicationRecord
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
has_secure_token :website_token
|
has_secure_token :website_token
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def web_widget_script
|
def web_widget_script
|
||||||
"<script>
|
"<script>
|
||||||
(function(d,t) {
|
(function(d,t) {
|
||||||
|
|
|
@ -58,6 +58,16 @@ class Conversation < ApplicationRecord
|
||||||
|
|
||||||
acts_as_taggable_on :labels
|
acts_as_taggable_on :labels
|
||||||
|
|
||||||
|
def can_reply?
|
||||||
|
return true unless inbox&.channel&.has_24_hour_messaging_window?
|
||||||
|
|
||||||
|
last_incoming_message = messages.incoming.last
|
||||||
|
|
||||||
|
return false if last_incoming_message.nil?
|
||||||
|
|
||||||
|
Time.current < last_incoming_message.created_at + 24.hours
|
||||||
|
end
|
||||||
|
|
||||||
def update_assignee(agent = nil)
|
def update_assignee(agent = nil)
|
||||||
update!(assignee: agent)
|
update!(assignee: agent)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,10 +6,11 @@ class Conversations::EventDataPresenter < SimpleDelegator
|
||||||
def push_data
|
def push_data
|
||||||
{
|
{
|
||||||
additional_attributes: additional_attributes,
|
additional_attributes: additional_attributes,
|
||||||
|
can_reply: can_reply?,
|
||||||
|
channel: inbox.try(:channel_type),
|
||||||
id: display_id,
|
id: display_id,
|
||||||
inbox_id: inbox_id,
|
inbox_id: inbox_id,
|
||||||
messages: push_messages,
|
messages: push_messages,
|
||||||
channel: inbox.try(:channel_type),
|
|
||||||
meta: push_meta,
|
meta: push_meta,
|
||||||
status: status,
|
status: status,
|
||||||
unread_count: unread_incoming_messages.count,
|
unread_count: unread_incoming_messages.count,
|
||||||
|
|
|
@ -16,6 +16,7 @@ end
|
||||||
json.inbox_id conversation.inbox_id
|
json.inbox_id conversation.inbox_id
|
||||||
json.status conversation.status
|
json.status conversation.status
|
||||||
json.muted conversation.muted?
|
json.muted conversation.muted?
|
||||||
|
json.can_reply conversation.can_reply?
|
||||||
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
|
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
|
||||||
json.user_last_seen_at conversation.user_last_seen_at.to_i
|
json.user_last_seen_at conversation.user_last_seen_at.to_i
|
||||||
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
|
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
|
||||||
|
|
|
@ -315,6 +315,7 @@ RSpec.describe Conversation, type: :model do
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
status: conversation.status,
|
status: conversation.status,
|
||||||
timestamp: conversation.created_at.to_i,
|
timestamp: conversation.created_at.to_i,
|
||||||
|
can_reply: true,
|
||||||
channel: 'Channel::WebWidget',
|
channel: 'Channel::WebWidget',
|
||||||
user_last_seen_at: conversation.user_last_seen_at.to_i,
|
user_last_seen_at: conversation.user_last_seen_at.to_i,
|
||||||
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
||||||
|
@ -347,4 +348,45 @@ RSpec.describe Conversation, type: :model do
|
||||||
expect(conversation.status).to eq('bot')
|
expect(conversation.status).to eq('bot')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#can_reply?' do
|
||||||
|
describe 'on channels without 24 hour restriction' do
|
||||||
|
let(:conversation) { create(:conversation) }
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
expect(conversation.can_reply?).to eq true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'on channels with 24 hour restriction' do
|
||||||
|
let!(:facebook_channel) { create(:channel_facebook_page) }
|
||||||
|
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: facebook_channel.account) }
|
||||||
|
let!(:conversation) { create(:conversation, inbox: facebook_inbox, account: facebook_channel.account) }
|
||||||
|
|
||||||
|
it 'returns false if there are no incoming messages' do
|
||||||
|
expect(conversation.can_reply?).to eq false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return false if last incoming message is outside of 24 hour window' do
|
||||||
|
create(
|
||||||
|
:message,
|
||||||
|
account: conversation.account,
|
||||||
|
inbox: facebook_inbox,
|
||||||
|
conversation: conversation,
|
||||||
|
created_at: Time.now - 25.hours
|
||||||
|
)
|
||||||
|
expect(conversation.can_reply?).to eq false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if last incoming message is inside 24 hour window' do
|
||||||
|
create(
|
||||||
|
:message,
|
||||||
|
account: conversation.account,
|
||||||
|
inbox: facebook_inbox,
|
||||||
|
conversation: conversation
|
||||||
|
)
|
||||||
|
expect(conversation.can_reply?).to eq true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,6 +22,7 @@ RSpec.describe Conversations::EventDataPresenter do
|
||||||
messages: [],
|
messages: [],
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
status: conversation.status,
|
status: conversation.status,
|
||||||
|
can_reply: conversation.can_reply?,
|
||||||
channel: conversation.inbox.channel_type,
|
channel: conversation.inbox.channel_type,
|
||||||
timestamp: conversation.created_at.to_i,
|
timestamp: conversation.created_at.to_i,
|
||||||
user_last_seen_at: conversation.user_last_seen_at.to_i,
|
user_last_seen_at: conversation.user_last_seen_at.to_i,
|
||||||
|
|
Loading…
Reference in a new issue