diff --git a/.scss-lint.yml b/.scss-lint.yml
index 19fae0cf1..1cc029441 100644
--- a/.scss-lint.yml
+++ b/.scss-lint.yml
@@ -282,3 +282,4 @@ exclude:
- 'app/javascript/widget/assets/scss/_reset.scss'
- 'app/javascript/widget/assets/scss/sdk.css'
- 'app/assets/stylesheets/administrate/reset/_normalize.scss'
+ - 'app/javascript/shared/assets/stylesheets/*.scss'
diff --git a/app/javascript/dashboard/api/inbox/message.js b/app/javascript/dashboard/api/inbox/message.js
index c9681f685..465095f44 100644
--- a/app/javascript/dashboard/api/inbox/message.js
+++ b/app/javascript/dashboard/api/inbox/message.js
@@ -20,9 +20,10 @@ class MessageApi extends ApiClient {
});
}
- sendAttachment([conversationId, { file }]) {
+ sendAttachment([conversationId, { file, isPrivate = false }]) {
const formData = new FormData();
formData.append('attachments[]', file, file.name);
+ formData.append('private', isPrivate);
return axios({
method: 'post',
url: `${this.url}/${conversationId}/messages`,
diff --git a/app/javascript/dashboard/assets/scss/_variables.scss b/app/javascript/dashboard/assets/scss/_variables.scss
index 1798d918b..c3cbe0c18 100644
--- a/app/javascript/dashboard/assets/scss/_variables.scss
+++ b/app/javascript/dashboard/assets/scss/_variables.scss
@@ -46,8 +46,8 @@ $color-gray: #6e6f73;
$color-light-gray: #999a9b;
$color-border: #e0e6ed;
$color-border-light: #f0f4f5;
-$color-background: #f4f6fb;
$color-border-dark: #cad0d4;
+$color-background: #f4f6fb;
$color-background-light: #f9fafc;
$color-white: #fff;
$color-body: #3c4858;
diff --git a/app/javascript/dashboard/assets/scss/app.scss b/app/javascript/dashboard/assets/scss/app.scss
index 3a6f62972..3d01856ef 100644
--- a/app/javascript/dashboard/assets/scss/app.scss
+++ b/app/javascript/dashboard/assets/scss/app.scss
@@ -1,5 +1,7 @@
@import 'shared/assets/fonts/inter';
-
+@import 'shared/assets/stylesheets/colors';
+@import 'shared/assets/stylesheets/spacing';
+@import 'shared/assets/stylesheets/font-size';
@import 'variables';
@import '~spinkit/scss/spinners/7-three-bounce';
diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
index 7a67b4e46..e0805c08c 100644
--- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
@@ -5,6 +5,18 @@
:is-contact-panel-open="isContactPanelOpen"
@contactPanelToggle="onToggleContactPanel"
/>
+
-
@@ -210,3 +222,19 @@ export default {
},
};
+
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
index ec6fb7ca7..00d5d5dff 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
@@ -108,7 +108,7 @@ export default {
data() {
return {
message: '',
- isPrivate: false,
+ isPrivateTabActive: false,
isFocused: false,
showEmojiPicker: false,
showCannedResponsesList: false,
@@ -117,6 +117,12 @@ export default {
},
computed: {
...mapGetters({ currentChat: 'getSelectedChat' }),
+ isPrivate() {
+ if (this.currentChat.can_reply) {
+ return this.isPrivateTabActive;
+ }
+ return true;
+ },
inboxId() {
return this.currentChat.inbox_id;
},
@@ -214,6 +220,13 @@ export default {
},
},
watch: {
+ currentChat(conversation) {
+ if (conversation.can_reply) {
+ this.isPrivateTabActive = false;
+ } else {
+ this.isPrivateTabActive = true;
+ }
+ },
message(updatedMessage) {
if (this.isPrivate) {
return;
@@ -278,11 +291,11 @@ export default {
}, 100);
},
setPrivateReplyMode() {
- this.isPrivate = true;
+ this.isPrivateTabActive = true;
this.$refs.messageInput.focus();
},
setReplyMode() {
- this.isPrivate = false;
+ this.isPrivateTabActive = false;
this.$refs.messageInput.focus();
},
emojiOnClick(emoji) {
@@ -327,7 +340,10 @@ export default {
}
this.isUploading = true;
this.$store
- .dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])
+ .dispatch('sendAttachment', [
+ this.currentChat.id,
+ { file: file.file, isPrivate: this.isPrivate },
+ ])
.then(() => {
this.isUploading = false;
this.$emit('scrollToMessage');
diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json
index b07e4e1f1..44d71f195 100644
--- a/app/javascript/dashboard/i18n/locale/en/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/en/conversation.json
@@ -9,6 +9,8 @@
"CLICK_HERE": "Click here",
"LOADING_INBOXES": "Loading inboxes",
"LOADING_CONVERSATIONS": "Loading Conversations",
+ "CANNOT_REPLY": "You cannot reply due to",
+ "24_HOURS_WINDOW": "24 hour message window restriction",
"DOWNLOAD": "Download",
"HEADER": {
"RESOLVE_ACTION": "Resolve",
diff --git a/app/javascript/dashboard/store/modules/conversations/actions.js b/app/javascript/dashboard/store/modules/conversations/actions.js
index 357d895c0..b237a822d 100644
--- a/app/javascript/dashboard/store/modules/conversations/actions.js
+++ b/app/javascript/dashboard/store/modules/conversations/actions.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import * as types from '../../mutation-types';
import ConversationApi from '../../../api/inbox/conversation';
import MessageApi from '../../../api/inbox/message';
+import { MESSAGE_TYPE } from 'widget/helpers/constants';
// actions
const actions = {
@@ -136,6 +137,12 @@ const actions = {
addMessage({ commit }, 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) {
diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js
index a7ac8b8e2..81380b547 100644
--- a/app/javascript/dashboard/store/modules/conversations/index.js
+++ b/app/javascript/dashboard/store/modules/conversations/index.js
@@ -160,6 +160,16 @@ export const mutations = {
[types.default.SET_ACTIVE_INBOX](_state, inboxId) {
_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 {
diff --git a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
index d44753732..d9b4ad753 100644
--- a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
@@ -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]]);
+ });
+ });
});
diff --git a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
index ca7173746..d4d364f8f 100644
--- a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
@@ -43,4 +43,15 @@ describe('#mutations', () => {
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);
+ });
+ });
});
diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js
index 4c72137fb..f58bf0988 100755
--- a/app/javascript/dashboard/store/mutation-types.js
+++ b/app/javascript/dashboard/store/mutation-types.js
@@ -4,6 +4,7 @@ export default {
SET_CURRENT_USER: 'SET_CURRENT_USER',
SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID',
SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY',
+
// Chat List
RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST',
SET_ALL_CONVERSATION: 'SET_ALL_CONVERSATION',
@@ -17,7 +18,6 @@ export default {
UPDATE_ASSIGNEE: 'UPDATE_ASSIGNEE',
UPDATE_CONVERSATION_CONTACT: 'UPDATE_CONVERSATION_CONTACT',
- // Active chat
SET_CURRENT_CHAT_WINDOW: 'SET_CURRENT_CHAT_WINDOW',
CLEAR_CURRENT_CHAT_WINDOW: 'CLEAR_CURRENT_CHAT_WINDOW',
CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
@@ -33,6 +33,8 @@ export default {
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
+ SET_CONVERSATION_CAN_REPLY: 'SET_CONVERSATION_CAN_REPLY',
+
// Inboxes
SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG',
SET_INBOXES: 'SET_INBOXES',
diff --git a/app/javascript/shared/assets/stylesheets/colors.scss b/app/javascript/shared/assets/stylesheets/colors.scss
new file mode 100644
index 000000000..40d05776e
--- /dev/null
+++ b/app/javascript/shared/assets/stylesheets/colors.scss
@@ -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;
+}
diff --git a/app/javascript/shared/assets/stylesheets/font-size.scss b/app/javascript/shared/assets/stylesheets/font-size.scss
new file mode 100644
index 000000000..aacfc5efe
--- /dev/null
+++ b/app/javascript/shared/assets/stylesheets/font-size.scss
@@ -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;
+}
diff --git a/app/javascript/shared/assets/stylesheets/spacing.scss b/app/javascript/shared/assets/stylesheets/spacing.scss
new file mode 100644
index 000000000..0fcfb9fd1
--- /dev/null
+++ b/app/javascript/shared/assets/stylesheets/spacing.scss
@@ -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;
+}
diff --git a/app/models/channel/api.rb b/app/models/channel/api.rb
index 5f080f232..7f3f18111 100644
--- a/app/models/channel/api.rb
+++ b/app/models/channel/api.rb
@@ -16,4 +16,8 @@ class Channel::Api < ApplicationRecord
belongs_to :account
has_one :inbox, as: :channel, dependent: :destroy
+
+ def has_24_hour_messaging_window?
+ false
+ end
end
diff --git a/app/models/channel/email.rb b/app/models/channel/email.rb
index 303d59619..a87419678 100644
--- a/app/models/channel/email.rb
+++ b/app/models/channel/email.rb
@@ -26,6 +26,10 @@ class Channel::Email < ApplicationRecord
has_one :inbox, as: :channel, dependent: :destroy
before_validation :ensure_forward_to_address, on: :create
+ def has_24_hour_messaging_window?
+ false
+ end
+
private
def ensure_forward_to_address
diff --git a/app/models/channel/facebook_page.rb b/app/models/channel/facebook_page.rb
index bcad26387..a3bfaf1f0 100644
--- a/app/models/channel/facebook_page.rb
+++ b/app/models/channel/facebook_page.rb
@@ -28,6 +28,10 @@ class Channel::FacebookPage < ApplicationRecord
after_create_commit :subscribe
before_destroy :unsubscribe
+ def has_24_hour_messaging_window?
+ true
+ end
+
def subscribe
# ref https://developers.facebook.com/docs/messenger-platform/reference/webhook-events
response = Facebook::Messenger::Subscriptions.subscribe(
diff --git a/app/models/channel/twilio_sms.rb b/app/models/channel/twilio_sms.rb
index 31b268126..81c38793a 100644
--- a/app/models/channel/twilio_sms.rb
+++ b/app/models/channel/twilio_sms.rb
@@ -30,6 +30,10 @@ class Channel::TwilioSms < ApplicationRecord
has_one :inbox, as: :channel, dependent: :destroy
+ def has_24_hour_messaging_window?
+ true
+ end
+
def name
'Twilio SMS'
end
diff --git a/app/models/channel/twitter_profile.rb b/app/models/channel/twitter_profile.rb
index dd6ee1f92..1430e471d 100644
--- a/app/models/channel/twitter_profile.rb
+++ b/app/models/channel/twitter_profile.rb
@@ -26,6 +26,10 @@ class Channel::TwitterProfile < ApplicationRecord
before_destroy :unsubscribe
+ def has_24_hour_messaging_window?
+ false
+ 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)
diff --git a/app/models/channel/web_widget.rb b/app/models/channel/web_widget.rb
index 5b3762a5b..59a9895a0 100644
--- a/app/models/channel/web_widget.rb
+++ b/app/models/channel/web_widget.rb
@@ -27,6 +27,10 @@ class Channel::WebWidget < ApplicationRecord
has_one :inbox, as: :channel, dependent: :destroy
has_secure_token :website_token
+ def has_24_hour_messaging_window?
+ false
+ end
+
def web_widget_script
"