activeadmin-chat 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +149 -0
  4. data/Rakefile +29 -0
  5. data/app/assets/config/active_admin/chat/manifest.js +0 -0
  6. data/app/assets/stylesheets/active_admin/chat.scss +130 -0
  7. data/app/assets/stylesheets/active_admin/chat/colors.scss +7 -0
  8. data/app/channels/active_admin/chat/admin_channel.rb +21 -0
  9. data/app/channels/active_admin/chat/base_channel.rb +28 -0
  10. data/app/channels/active_admin/chat/user_channel.rb +19 -0
  11. data/app/helpers/active_admin/chat/chat_helper.rb +13 -0
  12. data/app/views/active_admin/chat/_chat.html.erb +7 -0
  13. data/app/views/active_admin/chat/_list.html.erb +12 -0
  14. data/app/views/active_admin/chat/_message.html.erb +5 -0
  15. data/app/views/active_admin/chat/_messages.html.erb +3 -0
  16. data/app/views/active_admin/chat/show.html.erb +17 -0
  17. data/config/routes.rb +9 -0
  18. data/lib/active_admin/chat.rb +42 -0
  19. data/lib/active_admin/chat/application.rb +29 -0
  20. data/lib/active_admin/chat/engine.rb +6 -0
  21. data/lib/active_admin/chat/extensions.rb +13 -0
  22. data/lib/active_admin/chat/extensions/application.rb +83 -0
  23. data/lib/active_admin/chat/extensions/index_table_for.rb +23 -0
  24. data/lib/active_admin/chat/message_presenter.rb +24 -0
  25. data/lib/active_admin/chat/version.rb +5 -0
  26. data/lib/activeadmin-chat.rb +3 -0
  27. data/lib/generators/active_admin/chat/install/install_generator.rb +123 -0
  28. data/lib/generators/active_admin/chat/install/templates/initializers/active_admin/chat.rb.erb +8 -0
  29. data/lib/generators/active_admin/chat/install/templates/pages/chat.rb +2 -0
  30. metadata +270 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8d98f77adf289e587a3fe87574aea5b04c0dbec
4
+ data.tar.gz: a39d14ff43fb865d774a38027ac81c437a2d78d3
5
+ SHA512:
6
+ metadata.gz: ce8c7420247974431bef63d479c20df168745b7ec0ae71b2dac44ad7357fc798d3cca68cab3ce44bfcaaefc12d39c5fd71bfe7b30f1ec8554e9c55b97c4ff383
7
+ data.tar.gz: 37dcb743df2be501579b0259f03af19a077e2ee871cbc8bbe9f7d16740e0990dc20852c87f6632c5493baeb5aeade99ee9dd5da611bdf6c10edb260b8ade6a07
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Rootstrap
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # ActiveAdmin::Chat
2
+
3
+ [![Build Status](https://travis-ci.org/rootstrap/activeadmin-chat.svg?branch=master)](https://travis-ci.org/rootstrap/activeadmin-chat)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/7a8d43aef79218e8f772/maintainability)](https://codeclimate.com/github/rootstrap/activeadmin-chat/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/7a8d43aef79218e8f772/test_coverage)](https://codeclimate.com/github/rootstrap/activeadmin-chat/test_coverage)
6
+
7
+ Get a chat for your ActiveAdmin app out of the box.
8
+
9
+ ![](images/activeadmin-chat.gif)
10
+
11
+ ## Prerequisites
12
+ - It assumes you have models for your admins and users in place.
13
+ - It assumes that your users have an `email` attribute.
14
+ - For production you need to configure ActionCable by providing it with a Redis or Postgres connection in `config/cable.yml`.
15
+
16
+ ## Installation
17
+ Add to Gemfile:
18
+ ```ruby
19
+ gem 'activeadmin-chat'
20
+ ```
21
+
22
+ And then run:
23
+ ```bash
24
+ $ bundle install
25
+ ```
26
+
27
+ This gem requires Webpacker for the Javascript. Add the npm package:
28
+ ```bash
29
+ $ yarn add activeadmin-chat
30
+ ```
31
+
32
+ And install it:
33
+ ```bash
34
+ $ yarn install
35
+ ```
36
+
37
+ Install `ActiveAdmin::Chat`:
38
+ ```bash
39
+ $ rails generate active_admin:chat:install
40
+ ```
41
+ It will generate:
42
+ - `Conversation` and `Message` models and migrations.
43
+ - Initializer that configures the model names for conversation, message, admin user and user.
44
+ - Default chat page.
45
+
46
+ You can customize the namings of the models when installing `ActiveAdmin::Chat` with the usage of the `--conversation_model_name`, `--message_model_name`, `--admin_user_model_name` and `--user_model_name` flags.
47
+
48
+ For example:
49
+ ```bash
50
+ $ rails generate active_admin:chat:install --conversation_model_name=chat
51
+ ```
52
+
53
+ Once you've successfully installed `ActiveAdmin::Chat`, run:
54
+ ```bash
55
+ $ rails db:migrate
56
+ ```
57
+
58
+ Add including of CSS to `app/assets/stylesheets/active_admin.css.scss`:
59
+ ```css
60
+ @import 'active_admin/chat';
61
+ ```
62
+
63
+ Create a file named `app/javascript/packs/activeadmin-chat.js`, with the following content:
64
+ ```js
65
+ import 'activeadmin-chat';
66
+ ```
67
+
68
+ ### Example diagram
69
+ ![](images/activeadmin-chat_diagram.png?raw=true "Chat diagram")
70
+
71
+ ## Usage
72
+ All you need to get the chat up and running is to authenticate your users in the websocket connection in `app/channels/application_cable/connection.rb`. It's important that you identify them as the `current_user` here, this will be used by the gem internally.
73
+
74
+ For example with the `devise` gem:
75
+ ```ruby
76
+ module ApplicationCable
77
+ class Connection < ActionCable::Connection::Base
78
+ identified_by :current_user
79
+
80
+ def connect
81
+ self.current_user = find_verified_user
82
+ end
83
+
84
+ private
85
+
86
+ def find_verified_user
87
+ verified_user = env['warden'].user
88
+
89
+ reject_unauthorized_connection unless verified_user
90
+
91
+ verified_user
92
+ end
93
+ end
94
+ end
95
+ ```
96
+
97
+ ## Customization
98
+ You can change the chat page name and namespace the same way you do with other ActiveAdmin pages.
99
+
100
+ If you'd like to change the chat styling you can override the following classes:
101
+ - `.active-admin-chat__title`: Title above the conversations list
102
+ - `.active-admin-chat__conversations-list-container`, `.active-admin-chat__conversations-list` and `.active-admin-chat__conversation-item`: List of conversations
103
+ - `.active-admin-chat__conversation-history-container` and `.active-admin-chat__conversation-history`: Conversation panel
104
+ - `.active-admin-chat__message-container`: Message in the conversation panel
105
+ - `.active-admin-chat__send-message-container`: Send message button
106
+
107
+ If you'd like to change the whole HTML you can do it in the chat page by specifying its content:
108
+ ```ruby
109
+ ActiveAdmin.register_chat 'Chat' do
110
+ content do
111
+ # your code
112
+ end
113
+ end
114
+ ```
115
+
116
+ If you'd like to change the controller actions you can do it in the chat page by specifying its controller:
117
+ ```ruby
118
+ ActiveAdmin.register_chat 'Chat' do
119
+ controller do
120
+ def show
121
+ # your code
122
+ end
123
+ end
124
+ end
125
+ ```
126
+
127
+
128
+ If you'd like to add a button in the user's index in the admin dashboard to start a chat with a user:
129
+ ```ruby
130
+ ActiveAdmin.register_chat User do
131
+ index do
132
+ # your code
133
+ actions do |user|
134
+ send_message_link user
135
+ end
136
+ end
137
+ end
138
+ ```
139
+
140
+ ## Contributing
141
+ Bug reports (please use Issues) and pull requests are welcome on GitHub at https://github.com/rootstrap/activeadmin-chat. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
142
+
143
+ ## License
144
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
145
+
146
+ ## Credits
147
+ **Active Admin Chat** is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/activeadmin-chat/contributors).
148
+
149
+ [<img src="https://s3-us-west-1.amazonaws.com/rootstrap.com/img/rs.png" width="100"/>](http://www.rootstrap.com)
@@ -0,0 +1,29 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActiveAdmin::Chat'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+ require 'rubocop/rake_task'
24
+ require 'rspec/core/rake_task'
25
+
26
+ RuboCop::RakeTask.new
27
+ RSpec::Core::RakeTask.new
28
+
29
+ task default: %i[rubocop spec]
@@ -0,0 +1,130 @@
1
+ @import 'chat/colors';
2
+
3
+ .active-admin-chat__chat {
4
+ height: calc(100vh - 270px);
5
+
6
+ .active-admin-chat__conversations-list-container {
7
+ display: inline-block;
8
+ height: 100%;
9
+ width: 30%;
10
+ }
11
+
12
+ .active-admin-chat__title {
13
+ background-color: $active-admin-chat__lighter-grey;
14
+ background-image: linear-gradient(180deg, $active-admin-chat__lighter-grey, $active-admin-chat__light-grey);
15
+ border: solid 1px $active-admin-chat__grey;
16
+ border-color: $active-admin-chat__light-grey $active-admin-chat__light-grey $active-admin-chat__grey;
17
+ box-shadow: 0 1px 3px rgba($active-admin-chat__black, 0.12), 0 0 1px $active-admin-chat__white inset;
18
+ font-size: 14px;
19
+ font-weight: bold;
20
+ margin: 0;
21
+ padding: 10px 12px 8px;
22
+ text-shadow: $active-admin-chat__white 0 1px 0;
23
+ }
24
+
25
+ .active-admin-chat__conversations-list {
26
+ height: calc(100% - 34px);
27
+ margin: 0;
28
+ overflow-y: auto;
29
+ padding: 0;
30
+
31
+ a {
32
+ color: inherit;
33
+ text-decoration: inherit;
34
+ }
35
+ }
36
+
37
+ .active-admin-chat__conversation-item {
38
+ border-bottom: 1px solid $active-admin-chat__lighter-grey;
39
+ cursor: pointer;
40
+ list-style: none;
41
+ padding: 10px 12px 8px;
42
+ vertical-align: top;
43
+
44
+ &:nth-child(even) {
45
+ background-color: $active-admin-chat__lightest-grey;
46
+ }
47
+
48
+ &:hover {
49
+ background-color: $active-admin-chat__lighter-grey;
50
+ }
51
+
52
+ &.selected {
53
+ background-color: $active-admin-chat__grey;
54
+
55
+ &:hover {
56
+ background-color: $active-admin-chat__grey;
57
+ }
58
+ }
59
+ }
60
+
61
+ .active-admin-chat__conversation-history-container {
62
+ display: inline-block;
63
+ float: right;
64
+ height: 100%;
65
+ width: 70%;
66
+ }
67
+
68
+ .active-admin-chat__conversation-history {
69
+ background-color: $active-admin-chat__lighter-grey;
70
+ display: flex;
71
+ flex-direction: column;
72
+ height: calc(100% - 61px);
73
+ overflow: auto;
74
+ padding: 0 15px;
75
+
76
+ &.no-messages {
77
+ font-size: 24px;
78
+ margin: auto;
79
+ padding: 50px;
80
+ }
81
+ }
82
+
83
+ .active-admin-chat__message-container {
84
+ &:first-child {
85
+ margin-top: auto;
86
+ }
87
+
88
+ div {
89
+ background-color: $active-admin-chat__white;
90
+ border-radius: 10px 10px 10px 0;
91
+ display: inline-block;
92
+ margin: 3px;
93
+ max-width: 70%;
94
+ padding: 5px;
95
+ word-break: break-word;
96
+
97
+ p {
98
+ font-size: 12px;
99
+ margin: 0 0 0 2px;
100
+ }
101
+
102
+ span {
103
+ font-size: 10px;
104
+ }
105
+ }
106
+
107
+ &.admin {
108
+ text-align: right;
109
+
110
+ div {
111
+ background-color: $active-admin-chat__green;
112
+ border-radius: 10px 10px 0;
113
+ }
114
+ }
115
+ }
116
+
117
+ .active-admin-chat__send-message-container {
118
+ background-color: $active-admin-chat__lighter-grey;
119
+ height: 61px;
120
+
121
+ input {
122
+ border: 0;
123
+ border-radius: 25px;
124
+ margin: 10px 30px;
125
+ outline: none;
126
+ padding: 15px;
127
+ width: calc(100% - 90px);
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,7 @@
1
+ $active-admin-chat__lightest-grey: #fafafa;
2
+ $active-admin-chat__lighter-grey: #efefef;
3
+ $active-admin-chat__light-grey: #d4d4d4;
4
+ $active-admin-chat__grey: #cdcdcd;
5
+ $active-admin-chat__black: #000;
6
+ $active-admin-chat__white: #fff;
7
+ $active-admin-chat__green: #cbe6b8;
@@ -0,0 +1,21 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class AdminChannel < ActiveAdmin::Chat::BaseChannel
4
+ private
5
+
6
+ def conversation
7
+ return unless admin? && conversation_id.present?
8
+
9
+ @conversation ||= ActiveAdmin::Chat.conversation_klass.find_by(id: conversation_id)
10
+ end
11
+
12
+ def admin?
13
+ current_user.instance_of?(ActiveAdmin::Chat.admin_user_klass)
14
+ end
15
+
16
+ def conversation_id
17
+ params[:conversation_id]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class BaseChannel < ApplicationCable::Channel
4
+ def subscribed
5
+ conversation ? stream_for(conversation) : reject
6
+ end
7
+
8
+ def speak(data)
9
+ message = conversation.public_send(
10
+ ActiveAdmin::Chat.message_relation_name.pluralize
11
+ ).create!(
12
+ sender: current_user,
13
+ content: data['message']
14
+ )
15
+
16
+ message = ActiveAdmin::Chat::MessagePresenter.new(message)
17
+ ActiveAdmin::Chat::AdminChannel.broadcast_to(conversation, message)
18
+ ActiveAdmin::Chat::UserChannel.broadcast_to(conversation, message)
19
+ end
20
+
21
+ private
22
+
23
+ def conversation
24
+ raise NotImplementedError
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class UserChannel < ActiveAdmin::Chat::BaseChannel
4
+ private
5
+
6
+ def conversation
7
+ return unless user?
8
+
9
+ @conversation ||= ActiveAdmin::Chat.conversation_klass.find_by(
10
+ "#{ActiveAdmin::Chat.user_relation_name}_id": current_user.id
11
+ )
12
+ end
13
+
14
+ def user?
15
+ current_user.instance_of?(ActiveAdmin::Chat.user_klass)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ module ChatHelper
4
+ def admin_class(message)
5
+ 'admin' if message.sender.class == ActiveAdmin::Chat.admin_user_klass
6
+ end
7
+
8
+ def selected_class(conversation, active_conversation)
9
+ 'selected' if active_conversation && active_conversation == conversation
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ <div class='active-admin-chat__chat'>
2
+ <%= render partial: 'active_admin/chat/list' %>
3
+
4
+ <div class='active-admin-chat__conversation-history-container'>
5
+ <div class='active-admin-chat__conversation-history'></div>
6
+ </div>
7
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class='active-admin-chat__conversations-list-container'>
2
+ <h3 class='active-admin-chat__title'><%= ActiveAdmin::Chat.conversation_relation_name.pluralize.capitalize %></h3>
3
+ <ul class='active-admin-chat__conversations-list'>
4
+ <% conversations.each do |conversation| %>
5
+ <a href='<%= "/#{ActiveAdmin::Chat.namespace}/#{ActiveAdmin::Chat.page_name}/#{conversation.id}" %>' >
6
+ <li id='conversation-<%= conversation.id %>' class='active-admin-chat__conversation-item <%= selected_class(conversation, active_conversation) %>'>
7
+ <%= conversation.public_send(:"#{ActiveAdmin::Chat.user_relation_name}").email %>
8
+ </li>
9
+ </a>
10
+ <% end %>
11
+ </ul>
12
+ </div>
@@ -0,0 +1,5 @@
1
+ <div id="message-<%= message.id %>" data-time="<%= message.created_at.iso8601(3) %>" class="active-admin-chat__message-container <%= admin_class(message) %>">
2
+ <div>
3
+ <p><%= message.content %></p>
4
+ </div>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <% messages.each do |message| %>
2
+ <%= render partial: 'active_admin/chat/message', locals: { message: message } %>
3
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <div class='active-admin-chat__chat'>
2
+ <%= render partial: 'active_admin/chat/list' %>
3
+
4
+ <div class='active-admin-chat__conversation-history-container'>
5
+ <div class='active-admin-chat__conversation-history'>
6
+ <% if active_conversation && messages.empty? %>
7
+ <div class='active-admin-chat__conversation-history no-messages'>No messages</div>
8
+ <% else %>
9
+ <%= render partial: 'active_admin/chat/messages' %>
10
+ <% end %>
11
+ </div>
12
+ <div class='active-admin-chat__send-message-container'>
13
+ <input type='text' id='send-message' placeholder="Send message" />
14
+ </div>
15
+ </div>
16
+ </div>
17
+ <%= javascript_pack_tag 'activeadmin-chat' %>
@@ -0,0 +1,9 @@
1
+ Rails.application.routes.draw do
2
+ if ActiveAdmin::Chat.method_defined?(:page_name) && ActiveAdmin::Chat.page_name
3
+ namespace ActiveAdmin::Chat.namespace do
4
+ get "#{ActiveAdmin::Chat.page_name}/:id", to: "#{ActiveAdmin::Chat.page_name}#show"
5
+ post "#{ActiveAdmin::Chat.user_model_name}/:#{ActiveAdmin::Chat.user_relation_name}_id/#{ActiveAdmin::Chat.page_name}",
6
+ to: "#{ActiveAdmin::Chat.page_name}#create"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_admin'
2
+ require 'active_admin/chat/engine'
3
+ require 'active_admin/chat/extensions'
4
+ require 'active_admin/chat/extensions/application'
5
+ require 'active_admin/chat/application'
6
+ require 'active_admin/chat/extensions/index_table_for'
7
+ require 'active_admin/chat/message_presenter'
8
+ require 'webpacker'
9
+
10
+ module ActiveAdmin
11
+ module Chat
12
+ extend self
13
+
14
+ def application
15
+ @application ||= ::ActiveAdmin::Chat::Application.new
16
+ end
17
+
18
+ def setup
19
+ yield(application)
20
+ end
21
+
22
+ delegate :conversation_klass, to: :application
23
+ delegate :message_klass, to: :application
24
+ delegate :admin_user_klass, to: :application
25
+ delegate :user_klass, to: :application
26
+ delegate :conversation_relation_name, to: :application
27
+ delegate :message_relation_name, to: :application
28
+ delegate :admin_user_relation_name, to: :application
29
+ delegate :user_relation_name, to: :application
30
+ delegate :page_name, to: :application
31
+ delegate :namespace, to: :application
32
+ delegate :user_model_name, to: :application
33
+ delegate :messages_per_page, to: :application
34
+ end
35
+ end
36
+
37
+ ::ActiveAdmin.send :include, ActiveAdmin::Chat::Extensions
38
+ ::ActiveAdmin::Application.send :include, ActiveAdmin::Chat::Extensions::Application
39
+ ::ActiveAdmin::Views::IndexAsTable::IndexTableFor.send(
40
+ :prepend,
41
+ ActiveAdmin::Chat::Extensions::Views::IndexAsTable::IndexTableFor
42
+ )
@@ -0,0 +1,29 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class Application
4
+ attr_accessor :conversation_model_name, :message_model_name, :admin_user_model_name,
5
+ :user_model_name, :page_name, :namespace, :messages_per_page
6
+
7
+ def initialize
8
+ @conversation_model_name = 'conversation'
9
+ @message_model_name = 'message'
10
+ @admin_user_model_name = 'admin_user'
11
+ @user_model_name = 'user'
12
+ @namespace = 'admin'
13
+ @messages_per_page = 25
14
+ end
15
+
16
+ %i[conversation message admin_user user].each do |attribute|
17
+ define_method :"#{attribute}_klass" do
18
+ public_send("#{attribute}_model_name").pluralize.classify.constantize
19
+ end
20
+ end
21
+
22
+ %i[conversation message admin_user user].each do |attribute|
23
+ define_method :"#{attribute}_relation_name" do
24
+ public_send("#{attribute}_model_name").split('/').last
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ module Extensions
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ delegate :register_chat, to: :application
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,83 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ module Extensions
4
+ module Application
5
+ def register_chat(name, options = {}, &block)
6
+ setup_page_configs(name, options[:namespace])
7
+
8
+ register_page name, options do
9
+ content do
10
+ render 'active_admin/chat/chat'
11
+ end
12
+
13
+ controller do
14
+ layout 'active_admin', only: :show
15
+ helper_method :messages, :active_conversation, :conversations
16
+
17
+ def show
18
+ respond_to do |format|
19
+ format.html { render 'active_admin/chat/show' }
20
+ format.json do
21
+ render json: ActiveAdmin::Chat::MessagePresenter.all(messages)
22
+ end
23
+ end
24
+ end
25
+
26
+ def create
27
+ conversation =
28
+ ActiveAdmin::Chat.conversation_klass
29
+ .find_or_create_by!(
30
+ "#{user_relation_name_id}": params[:"#{user_relation_name_id}"]
31
+ )
32
+ redirect_to action: 'show', id: conversation
33
+ end
34
+
35
+ def active_conversation
36
+ @active_conversation ||= ActiveAdmin::Chat.conversation_klass.find_by(
37
+ id: params[:id]
38
+ )
39
+ end
40
+
41
+ def conversations
42
+ @conversations ||= ActiveAdmin::Chat.conversation_klass
43
+ .includes(ActiveAdmin::Chat.user_relation_name)
44
+ end
45
+
46
+ def messages
47
+ return [] unless active_conversation
48
+
49
+ page_messages = active_conversation.public_send(
50
+ ActiveAdmin::Chat.message_relation_name.pluralize
51
+ ).includes(:sender).order(created_at: :desc)
52
+
53
+ if params[:created_at].present?
54
+ page_messages = page_messages.where('created_at < ?',
55
+ DateTime.parse(params[:created_at]))
56
+ end
57
+ page_messages.limit(ActiveAdmin::Chat.messages_per_page).reverse
58
+ end
59
+
60
+ private
61
+
62
+ def user_relation_name_id
63
+ "#{ActiveAdmin::Chat.user_relation_name}_id"
64
+ end
65
+ end
66
+
67
+ # customize default chat
68
+ instance_eval(&block)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def setup_page_configs(page_name, namespace)
75
+ ActiveAdmin::Chat.setup do |config|
76
+ config.page_name = page_name.downcase
77
+ config.namespace = namespace.downcase if namespace
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ module Extensions
4
+ module Views
5
+ module IndexAsTable
6
+ module IndexTableFor
7
+ def send_message_link(resource)
8
+ text_node link_to 'Send Message',
9
+ send_message_path(resource.id),
10
+ method: :post
11
+ end
12
+
13
+ private
14
+
15
+ def send_message_path(id)
16
+ "#{ActiveAdmin::Chat.user_model_name}/#{id}/#{ActiveAdmin::Chat.page_name}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ class MessagePresenter
4
+ attr_reader :message
5
+
6
+ def initialize(message)
7
+ @message = message
8
+ end
9
+
10
+ def self.all(messages)
11
+ { messages: messages.map { |m| new(m) } }
12
+ end
13
+
14
+ def as_json(*)
15
+ {
16
+ id: message.id,
17
+ message: message.content,
18
+ date: message.created_at.iso8601(3),
19
+ is_admin: message.sender.class == ActiveAdmin::Chat.admin_user_klass
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ VERSION = '1.0.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_admin/chat'
@@ -0,0 +1,123 @@
1
+ module ActiveAdmin
2
+ module Chat
3
+ module Generators
4
+ class InstallGenerator < Rails::Generators::NamedBase
5
+ argument :name, default: ''
6
+ class_option :conversation_model_name, type: :string, default: 'conversation'
7
+ class_option :message_model_name, type: :string, default: 'message'
8
+ class_option :admin_user_model_name, type: :string, default: 'admin_user'
9
+ class_option :user_model_name, type: :string, default: 'user'
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ def normalize_config
14
+ @conversation_model_name = options['conversation_model_name'].underscore.singularize
15
+ @message_model_name = options['message_model_name'].underscore.singularize
16
+ @admin_user_model_name = options['admin_user_model_name'].underscore.singularize
17
+ @user_model_name = options['user_model_name'].underscore.singularize
18
+ @messages_per_page = 25
19
+ end
20
+
21
+ def copy_initializer
22
+ template 'initializers/active_admin/chat.rb.erb',
23
+ 'config/initializers/active_admin/chat.rb'
24
+ end
25
+
26
+ def create_models
27
+ unless model_exists?(@conversation_model_name)
28
+ Rails::Generators.invoke(
29
+ 'active_record:model', [
30
+ @conversation_model_name,
31
+ "#{@user_model_name.split('/').last}_id:integer:index",
32
+ '--migration',
33
+ '--timestamps'
34
+ ]
35
+ )
36
+ end
37
+
38
+ unless model_exists?(@message_model_name)
39
+ Rails::Generators.invoke(
40
+ 'active_record:model', [
41
+ @message_model_name,
42
+ 'content:string',
43
+ 'sender:references{polymorphic}',
44
+ "#{@conversation_model_name.split('/').last}_id:integer:index",
45
+ 'created_at:datetime:index',
46
+ 'updated_at:datetime',
47
+ '--migration',
48
+ '--no-timestamps'
49
+ ]
50
+ )
51
+ end
52
+ end
53
+
54
+ def inject_models_contents
55
+ if model_exists?(@conversation_model_name)
56
+ inject_into_class(
57
+ model_path(@conversation_model_name),
58
+ @conversation_model_name.classify,
59
+ conversation_contents
60
+ )
61
+ end
62
+
63
+ if model_exists?(@message_model_name)
64
+ inject_into_class(
65
+ model_path(@message_model_name),
66
+ @message_model_name.classify,
67
+ message_contents
68
+ )
69
+ end
70
+ end
71
+
72
+ def insert_chat_page
73
+ template 'pages/chat.rb', 'app/admin/chat.rb'
74
+ end
75
+
76
+ private
77
+
78
+ def conversation_contents
79
+ buffer = <<-CONTENT
80
+ #{add_belongs_to(@user_model_name)}
81
+ #{add_has_many(@message_model_name)}
82
+ CONTENT
83
+
84
+ buffer
85
+ end
86
+
87
+ def message_contents
88
+ buffer = <<-CONTENT
89
+ #{add_belongs_to(@conversation_model_name)}
90
+ CONTENT
91
+
92
+ buffer
93
+ end
94
+
95
+ def add_belongs_to(model_name)
96
+ if model_name.include?('/')
97
+ "belongs_to :#{model_name.split('/').last}, class_name: '#{model_name.classify}'"
98
+ else
99
+ "belongs_to :#{model_name}"
100
+ end
101
+ end
102
+
103
+ def add_has_many(model_name)
104
+ relation_name = model_name.split('/').last.pluralize
105
+
106
+ if model_name.include?('/')
107
+ "has_many :#{relation_name}, class_name: #{model_name.classify}, dependent: :destroy"
108
+ else
109
+ "has_many :#{relation_name}, dependent: :destroy"
110
+ end
111
+ end
112
+
113
+ def model_exists?(model_name)
114
+ File.exist?(File.join(destination_root, model_path(model_name)))
115
+ end
116
+
117
+ def model_path(model_name)
118
+ File.join('app', 'models', "#{model_name}.rb")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,8 @@
1
+ ActiveAdmin::Chat.setup do |config|
2
+ # Specify the names of the models required for the chat
3
+ config.conversation_model_name = '<%= @conversation_model_name %>'
4
+ config.message_model_name = '<%= @message_model_name %>'
5
+ config.admin_user_model_name = '<%= @admin_user_model_name %>'
6
+ config.user_model_name = '<%= @user_model_name %>'
7
+ config.messages_per_page = <%= @messages_per_page %>
8
+ end
@@ -0,0 +1,2 @@
1
+ ActiveAdmin.register_chat 'Chat' do
2
+ end
metadata ADDED
@@ -0,0 +1,270 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activeadmin-chat
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Santiago Bartesaghi
8
+ - Federico Aldunate
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-10-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activeadmin
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rails
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '5.2'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '5.2'
42
+ - !ruby/object:Gem::Dependency
43
+ name: webpacker
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '5.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '5.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: action-cable-testing
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 0.4.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 0.4.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: byebug
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 10.0.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 10.0.0
84
+ - !ruby/object:Gem::Dependency
85
+ name: capybara
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 3.32.2
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 3.32.2
98
+ - !ruby/object:Gem::Dependency
99
+ name: factory_bot_rails
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 4.11.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 4.11.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: generator_spec
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 0.9.4
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 0.9.4
126
+ - !ruby/object:Gem::Dependency
127
+ name: puma
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 4.3.3
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 4.3.3
140
+ - !ruby/object:Gem::Dependency
141
+ name: rspec-rails
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '3.8'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '3.8'
154
+ - !ruby/object:Gem::Dependency
155
+ name: rubocop
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: 0.59.2
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: 0.59.2
168
+ - !ruby/object:Gem::Dependency
169
+ name: selenium-webdriver
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '3.0'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: '3.0'
182
+ - !ruby/object:Gem::Dependency
183
+ name: simplecov
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - "~>"
187
+ - !ruby/object:Gem::Version
188
+ version: 0.17.1
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: 0.17.1
196
+ - !ruby/object:Gem::Dependency
197
+ name: sqlite3
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: 1.3.0
203
+ type: :development
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: 1.3.0
210
+ description: ActiveAdmin chat plugin
211
+ email:
212
+ - santiago.bartesaghi@rootstrap.com
213
+ - federico@rootstrap.com
214
+ executables: []
215
+ extensions: []
216
+ extra_rdoc_files: []
217
+ files:
218
+ - MIT-LICENSE
219
+ - README.md
220
+ - Rakefile
221
+ - app/assets/config/active_admin/chat/manifest.js
222
+ - app/assets/stylesheets/active_admin/chat.scss
223
+ - app/assets/stylesheets/active_admin/chat/colors.scss
224
+ - app/channels/active_admin/chat/admin_channel.rb
225
+ - app/channels/active_admin/chat/base_channel.rb
226
+ - app/channels/active_admin/chat/user_channel.rb
227
+ - app/helpers/active_admin/chat/chat_helper.rb
228
+ - app/views/active_admin/chat/_chat.html.erb
229
+ - app/views/active_admin/chat/_list.html.erb
230
+ - app/views/active_admin/chat/_message.html.erb
231
+ - app/views/active_admin/chat/_messages.html.erb
232
+ - app/views/active_admin/chat/show.html.erb
233
+ - config/routes.rb
234
+ - lib/active_admin/chat.rb
235
+ - lib/active_admin/chat/application.rb
236
+ - lib/active_admin/chat/engine.rb
237
+ - lib/active_admin/chat/extensions.rb
238
+ - lib/active_admin/chat/extensions/application.rb
239
+ - lib/active_admin/chat/extensions/index_table_for.rb
240
+ - lib/active_admin/chat/message_presenter.rb
241
+ - lib/active_admin/chat/version.rb
242
+ - lib/activeadmin-chat.rb
243
+ - lib/generators/active_admin/chat/install/install_generator.rb
244
+ - lib/generators/active_admin/chat/install/templates/initializers/active_admin/chat.rb.erb
245
+ - lib/generators/active_admin/chat/install/templates/pages/chat.rb
246
+ homepage: https://github.com/rootstrap/activeadmin-chat
247
+ licenses:
248
+ - MIT
249
+ metadata: {}
250
+ post_install_message:
251
+ rdoc_options: []
252
+ require_paths:
253
+ - lib
254
+ required_ruby_version: !ruby/object:Gem::Requirement
255
+ requirements:
256
+ - - ">="
257
+ - !ruby/object:Gem::Version
258
+ version: 2.5.0
259
+ required_rubygems_version: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - ">="
262
+ - !ruby/object:Gem::Version
263
+ version: '0'
264
+ requirements: []
265
+ rubyforge_project:
266
+ rubygems_version: 2.5.2.3
267
+ signing_key:
268
+ specification_version: 4
269
+ summary: ActiveAdmin chat plugin
270
+ test_files: []