activeadmin-chat 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []