diff --git a/.scss-lint.yml b/.scss-lint.yml index 59284ef1c..215e9c49e 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -1,3 +1,7 @@ linters: LeadingZero: enabled: false + +exclude: + - 'app/javascript/widget/assets/scss/_reset.scss' + - 'app/javascript/widget/assets/scss/sdk.css' diff --git a/Gemfile b/Gemfile index 893be6119..1fb109bbb 100644 --- a/Gemfile +++ b/Gemfile @@ -53,6 +53,8 @@ gem 'telegram-bot-ruby' gem 'twitter' # facebook client gem 'koala' +# Random name generator +gem 'haikunator' ##--- gems for debugging and error reporting ---## # static analysis diff --git a/Gemfile.lock b/Gemfile.lock index 205747213..c74e87f2d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -192,6 +192,7 @@ GEM foreman (0.86.0) globalid (0.4.2) activesupport (>= 4.2.0) + haikunator (1.1.0) hashie (3.6.0) http (3.3.0) addressable (~> 2.3) @@ -455,6 +456,7 @@ DEPENDENCIES faker figaro foreman + haikunator hashie jbuilder (~> 2.5) kaminari diff --git a/app/controllers/api/v1/widget/inboxes_controller.rb b/app/controllers/api/v1/widget/inboxes_controller.rb new file mode 100644 index 000000000..2e0269db0 --- /dev/null +++ b/app/controllers/api/v1/widget/inboxes_controller.rb @@ -0,0 +1,26 @@ +class Api::V1::Widget::InboxesController < ApplicationController + def create + ActiveRecord::Base.transaction do + channel = web_widgets.create!( + website_name: permitted_params[:website_name], + website_url: permitted_params[:website_url] + ) + inbox = inboxes.create!(name: permitted_params[:website_name], channel: channel) + render json: inbox + end + end + + private + + def inboxes + current_account.inboxes + end + + def web_widgets + current_account.web_widgets + end + + def permitted_params + params.fetch(:website).permit(:website_name, :website_url) + end +end diff --git a/app/controllers/api/v1/widget/messages_controller.rb b/app/controllers/api/v1/widget/messages_controller.rb index 8bc2cf446..662fd927e 100644 --- a/app/controllers/api/v1/widget/messages_controller.rb +++ b/app/controllers/api/v1/widget/messages_controller.rb @@ -1,28 +1,68 @@ -class Api::V1::Widget::MessagesController < ApplicationController - # TODO: move widget apis to different controller. - skip_before_action :set_current_user, only: [:create_incoming] - skip_before_action :check_subscription, only: [:create_incoming] - skip_around_action :handle_with_exception, only: [:create_incoming] +class Api::V1::Widget::MessagesController < ActionController::Base + before_action :set_conversation, only: [:create] - def create_incoming - builder = Integrations::Widget::IncomingMessageBuilder.new(incoming_message_params) - builder.perform - render json: builder.message + def index + @messages = conversation.nil? ? [] : message_finder.perform end - def create_outgoing - builder = Integrations::Widget::OutgoingMessageBuilder.new(outgoing_message_params) - builder.perform - render json: builder.message + def create + @message = conversation.messages.new(message_params) + @message.save! end private - def incoming_message_params - params.require(:message).permit(:contact_id, :inbox_id, :content) + def conversation + @conversation ||= ::Conversation.find_by( + contact_id: cookie_params[:contact_id], + inbox_id: cookie_params[:inbox_id] + ) end - def outgoing_message_params - params.require(:message).permit(:user_id, :inbox_id, :content, :conversation_id) + def set_conversation + @conversation = ::Conversation.create!(conversation_params) if conversation.nil? + end + + def message_params + { + account_id: conversation.account_id, + inbox_id: conversation.inbox_id, + message_type: :incoming, + content: permitted_params[:content] + } + end + + def conversation_params + { + account_id: inbox.account_id, + inbox_id: inbox.id, + contact_id: cookie_params[:contact_id] + } + end + + def inbox + @inbox ||= ::Inbox.find_by(id: cookie_params[:inbox_id]) + end + + def cookie_params + JSON.parse(cookies.signed[cookie_name]).symbolize_keys + end + + def message_finder_params + { + filter_internal_messages: true + } + end + + def message_finder + @message_finder ||= MessageFinder.new(conversation, message_finder_params) + end + + def cookie_name + 'cw_conversation_' + params[:website_token] + end + + def permitted_params + params.fetch(:message).permit(:content) end end diff --git a/app/controllers/widgets_controller.rb b/app/controllers/widgets_controller.rb new file mode 100644 index 000000000..733e99df4 --- /dev/null +++ b/app/controllers/widgets_controller.rb @@ -0,0 +1,47 @@ +class WidgetsController < ActionController::Base + before_action :set_web_widget + before_action :set_contact + before_action :build_contact + + private + + def set_web_widget + @web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token]) + end + + def set_contact + return if cookie_params[:source_id].nil? + + contact_inbox = ::ContactInbox.find_by( + inbox_id: @web_widget.inbox.id, + source_id: cookie_params[:source_id] + ) + + @contact = contact_inbox.contact + end + + def build_contact + return if @contact.present? + + contact_inbox = @web_widget.create_contact_inbox + @contact = contact_inbox.contact + + cookies.signed[cookie_name] = JSON.generate( + source_id: contact_inbox.source_id, + contact_id: @contact.id, + inbox_id: @web_widget.inbox.id + ).to_s + end + + def cookie_params + cookies.signed[cookie_name] ? JSON.parse(cookies.signed[cookie_name]).symbolize_keys : {} + end + + def permitted_params + params.permit(:website_token) + end + + def cookie_name + 'cw_conversation_' + permitted_params[:website_token] + end +end diff --git a/app/finders/message_finder.rb b/app/finders/message_finder.rb index bcac94418..e839e4946 100644 --- a/app/finders/message_finder.rb +++ b/app/finders/message_finder.rb @@ -10,11 +10,17 @@ class MessageFinder private + def messages + return @conversation.messages if @params[:filter_internal_messages].blank? + + @conversation.messages.where.not('private = ? OR message_type = ?', true, 2) + end + def current_messages if @params[:before].present? - @conversation.messages.reorder('created_at desc').where('id < ?', @params[:before]).limit(20).reverse + messages.reorder('created_at desc').where('id < ?', @params[:before]).limit(20).reverse else - @conversation.messages.reorder('created_at desc').limit(20).reverse + messages.reorder('created_at desc').limit(20).reverse end end end diff --git a/app/javascript/dashboard/api/channel/webChannel.js b/app/javascript/dashboard/api/channel/webChannel.js new file mode 100644 index 000000000..7fc5fb2db --- /dev/null +++ b/app/javascript/dashboard/api/channel/webChannel.js @@ -0,0 +1,9 @@ +import ApiClient from '../ApiClient'; + +class WebChannel extends ApiClient { + constructor() { + super('widget/inboxes'); + } +} + +export default new WebChannel(); diff --git a/app/javascript/dashboard/assets/images/channels/website.png b/app/javascript/dashboard/assets/images/channels/website.png new file mode 100644 index 000000000..fdc909bc2 Binary files /dev/null and b/app/javascript/dashboard/assets/images/channels/website.png differ diff --git a/app/javascript/dashboard/assets/scss/_foundation-custom.scss b/app/javascript/dashboard/assets/scss/_foundation-custom.scss index 0a70eaf89..f918ed97c 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-custom.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-custom.scss @@ -25,3 +25,14 @@ border-radius: $space-smaller; font-size: $font-size-mini; } + +code { + border: 0; + font-family: 'Monaco'; + font-size: $font-size-mini; + + &.hljs { + background: $color-background; + padding: $space-two; + } +} diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 2b771a973..a996aae43 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -121,7 +121,7 @@ export default { fetchData() { if (this.chatLists.length === 0) { this.$store.dispatch('fetchAllConversations', { - inbox: this.conversationInbox, + inboxId: this.conversationInbox ? this.conversationInbox : undefined, assigneeStatus: this.allMessageType, convStatus: this.activeStatusTab, }); diff --git a/app/javascript/dashboard/components/layout/SidebarItem.vue b/app/javascript/dashboard/components/layout/SidebarItem.vue index c48998ba8..087467616 100644 --- a/app/javascript/dashboard/components/layout/SidebarItem.vue +++ b/app/javascript/dashboard/components/layout/SidebarItem.vue @@ -22,7 +22,7 @@
+ + <%= yield %> + +diff --git a/app/views/widgets/index.html.erb b/app/views/widgets/index.html.erb new file mode 100644 index 000000000..83f7d3e4e --- /dev/null +++ b/app/views/widgets/index.html.erb @@ -0,0 +1,18 @@ + + +
+
+ <%= csrf_meta_tags %> + + <%= javascript_pack_tag 'widget' %> + <%= stylesheet_pack_tag 'widget' %> + +
+