simple_chat 0.1.0

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +178 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/stylesheets/simple_chat/application.css +726 -0
  6. data/app/assets/stylesheets/simple_chat/application.tailwind.css +1 -0
  7. data/app/controllers/simple_chat/application_controller.rb +16 -0
  8. data/app/controllers/simple_chat/chat_members_controller.rb +27 -0
  9. data/app/controllers/simple_chat/chat_rooms_controller.rb +74 -0
  10. data/app/controllers/simple_chat/messages_controller.rb +77 -0
  11. data/app/helpers/simple_chat/application_helper.rb +4 -0
  12. data/app/helpers/simple_chat/chat_rooms_helper.rb +4 -0
  13. data/app/helpers/simple_chat/messages_helper.rb +4 -0
  14. data/app/jobs/simple_chat/application_job.rb +4 -0
  15. data/app/mailers/simple_chat/application_mailer.rb +6 -0
  16. data/app/models/simple_chat/application_record.rb +5 -0
  17. data/app/models/simple_chat/chat_member.rb +6 -0
  18. data/app/models/simple_chat/chat_room.rb +22 -0
  19. data/app/models/simple_chat/message.rb +8 -0
  20. data/app/views/layouts/simple_chat/application.html.erb +24 -0
  21. data/app/views/simple_chat/chat_rooms/_chat_room.html.erb +7 -0
  22. data/app/views/simple_chat/chat_rooms/_form.html.erb +22 -0
  23. data/app/views/simple_chat/chat_rooms/edit.html.erb +12 -0
  24. data/app/views/simple_chat/chat_rooms/index.html.erb +14 -0
  25. data/app/views/simple_chat/chat_rooms/new.html.erb +11 -0
  26. data/app/views/simple_chat/chat_rooms/show.html.erb +80 -0
  27. data/app/views/simple_chat/messages/_form.html.erb +28 -0
  28. data/app/views/simple_chat/messages/_message.html.erb +28 -0
  29. data/app/views/simple_chat/messages/create.turbo_stream.erb +10 -0
  30. data/app/views/simple_chat/messages/edit.html.erb +12 -0
  31. data/app/views/simple_chat/messages/index.html.erb +14 -0
  32. data/app/views/simple_chat/messages/new.html.erb +11 -0
  33. data/app/views/simple_chat/messages/show.html.erb +8 -0
  34. data/config/routes.rb +7 -0
  35. data/config/tailwind.config.js +15 -0
  36. data/db/migrate/20260406070146_create_simple_chat_chat_rooms.rb +9 -0
  37. data/db/migrate/20260406080846_create_simple_chat_chat_members.rb +11 -0
  38. data/db/migrate/20260406081003_create_simple_chat_messages.rb +12 -0
  39. data/db/migrate/20260503095220_add_channel_hash_to_solid_cable_messages.rb +6 -0
  40. data/lib/generators/simple_chat/install_generator.rb +21 -0
  41. data/lib/simple_chat/configuration.rb +16 -0
  42. data/lib/simple_chat/engine.rb +13 -0
  43. data/lib/simple_chat/version.rb +3 -0
  44. data/lib/simple_chat.rb +42 -0
  45. data/lib/tasks/simple_chat_tasks.rake +40 -0
  46. metadata +146 -0
@@ -0,0 +1,16 @@
1
+ module SimpleChat
2
+ class ApplicationController < ::ApplicationController
3
+ helper_method :simple_chat_current_user
4
+
5
+ def simple_chat_current_user
6
+ method = SimpleChat.configuration.current_user_method
7
+ if respond_to?(method, true)
8
+ send(method)
9
+ elsif defined?(Current) && Current.respond_to?(:user)
10
+ Current.user
11
+ else
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module SimpleChat
2
+ class ChatMembersController < ApplicationController
3
+ before_action :set_chat_room
4
+
5
+ def create
6
+ @chat_member = @chat_room.chat_members.find_or_initialize_by(user: simple_chat_current_user)
7
+
8
+ if @chat_member.save
9
+ redirect_to @chat_room, notice: "You are now a member of this chat room."
10
+ else
11
+ redirect_to @chat_room, alert: "Unable to join chat room."
12
+ end
13
+ end
14
+
15
+ def destroy
16
+ @chat_member = @chat_room.chat_members.find_by(user: simple_chat_current_user)
17
+ @chat_member&.destroy
18
+ redirect_to chat_rooms_path, notice: "You have left the chat room."
19
+ end
20
+
21
+ private
22
+
23
+ def set_chat_room
24
+ @chat_room = ChatRoom.find(params[:chat_room_id])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ module SimpleChat
2
+ class ChatRoomsController < ApplicationController
3
+ before_action :set_chat_room, only: %i[ show edit update destroy ]
4
+ before_action :ensure_member, only: %i[ show edit update destroy ]
5
+
6
+ # GET /chat_rooms
7
+ def index
8
+ if simple_chat_current_user
9
+ @chat_rooms = ChatRoom.for_user(simple_chat_current_user)
10
+ else
11
+ @chat_rooms = ChatRoom.none
12
+ end
13
+ end
14
+
15
+ # GET /chat_rooms/1
16
+ def show
17
+ @messages = @chat_room.messages.select(&:persisted?)
18
+ @message = @chat_room.messages.build
19
+ end
20
+
21
+ # GET /chat_rooms/new
22
+ def new
23
+ @chat_room = ChatRoom.new
24
+ end
25
+
26
+ # GET /chat_rooms/1/edit
27
+ def edit
28
+ end
29
+
30
+ # POST /chat_rooms
31
+ def create
32
+ @chat_room = ChatRoom.new(chat_room_params)
33
+
34
+ if @chat_room.save
35
+ @chat_room.chat_members.create(user: simple_chat_current_user) if simple_chat_current_user
36
+ redirect_to @chat_room, notice: "Chat room was successfully created and you have been added as a member."
37
+ else
38
+ render :new, status: :unprocessable_content
39
+ end
40
+ end
41
+
42
+ # PATCH/PUT /chat_rooms/1
43
+ def update
44
+ if @chat_room.update(chat_room_params)
45
+ redirect_to @chat_room, notice: "Chat room was successfully updated.", status: :see_other
46
+ else
47
+ render :edit, status: :unprocessable_content
48
+ end
49
+ end
50
+
51
+ # DELETE /chat_rooms/1
52
+ def destroy
53
+ @chat_room.destroy!
54
+ redirect_to chat_rooms_path, notice: "Chat room was successfully destroyed.", status: :see_other
55
+ end
56
+
57
+ private
58
+ # Use callbacks to share common setup or constraints between actions.
59
+ def set_chat_room
60
+ @chat_room = ChatRoom.find(params.expect(:id))
61
+ end
62
+
63
+ # Only allow a list of trusted parameters through.
64
+ def chat_room_params
65
+ params.expect(chat_room: [ :title ])
66
+ end
67
+
68
+ def ensure_member
69
+ unless @chat_room.is_member?(simple_chat_current_user)
70
+ redirect_to chat_rooms_path, alert: "You are not a member of this chat room."
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,77 @@
1
+ module SimpleChat
2
+ class MessagesController < ApplicationController
3
+ before_action :set_message, only: %i[ show edit update destroy ]
4
+
5
+ # GET /messages
6
+ def index
7
+ if params[:chat_room_id]
8
+ @chat_room = ChatRoom.find(params[:chat_room_id])
9
+ @messages = @chat_room.messages
10
+ else
11
+ @messages = Message.all
12
+ end
13
+ end
14
+
15
+ # GET /messages/1
16
+ def show
17
+ end
18
+
19
+ # GET /messages/new
20
+ def new
21
+ @message = Message.new(chat_room_id: params[:chat_room_id])
22
+ @chat_room = @message.chat_room
23
+ end
24
+
25
+ # GET /messages/1/edit
26
+ def edit
27
+ end
28
+
29
+ # POST /messages
30
+ def create
31
+ @message = Message.new(message_params)
32
+ @message.user = simple_chat_current_user
33
+ @chat_room = @message.chat_room
34
+
35
+ if @chat_room && !@chat_room.is_member?(simple_chat_current_user)
36
+ redirect_to @chat_room, alert: "You must be a member of this chat room to post messages."
37
+ return
38
+ end
39
+
40
+ if @message.save
41
+ respond_to do |format|
42
+ format.html { redirect_to @chat_room || @message, notice: "Message was successfully created." }
43
+ format.turbo_stream
44
+ end
45
+ else
46
+ render :new, status: :unprocessable_content
47
+ end
48
+ end
49
+
50
+ # PATCH/PUT /messages/1
51
+ def update
52
+ if @message.update(message_params)
53
+ redirect_to @message, notice: "Message was successfully updated.", status: :see_other
54
+ else
55
+ render :edit, status: :unprocessable_content
56
+ end
57
+ end
58
+
59
+ # DELETE /messages/1
60
+ def destroy
61
+ chat_room = @message.chat_room
62
+ @message.destroy!
63
+ redirect_to messages_path(chat_room_id: chat_room&.id), notice: "Message was successfully destroyed.", status: :see_other
64
+ end
65
+
66
+ private
67
+ def set_message
68
+ @message = Message.find(params[:id])
69
+ @chat_room = @message.chat_room
70
+ end
71
+
72
+ # Only allow a list of trusted parameters through.
73
+ def message_params
74
+ params.expect(message: [ :content, :chat_room_id ])
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleChat
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleChat
2
+ module ChatRoomsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleChat
2
+ module MessagesHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SimpleChat
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module SimpleChat
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module SimpleChat
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module SimpleChat
2
+ class ChatMember < ApplicationRecord
3
+ belongs_to :chat_room
4
+ belongs_to :user, class_name: SimpleChat.configuration.chat_user_model
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleChat
2
+ class ChatRoom < ApplicationRecord
3
+ has_many :messages, dependent: :destroy
4
+ has_many :chat_members, dependent: :destroy
5
+ validates :title, uniqueness: true
6
+
7
+ scope :for_user, ->(user) { joins(:chat_members).where(simple_chat_chat_members: { user_id: user.id }) }
8
+
9
+ def is_member?(user)
10
+ chat_members.exists?(user: user)
11
+ end
12
+
13
+ private
14
+ def is_group_chat
15
+ chat_members.count > 2
16
+ end
17
+
18
+ def last_message_id
19
+ messages.last.id
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ module SimpleChat
2
+ class Message < ApplicationRecord
3
+ belongs_to :chat_room
4
+ belongs_to :user, class_name: SimpleChat.configuration.chat_user_model
5
+
6
+ after_create_commit -> { broadcast_append_later_to "simple_chat", chat_room, target: "messages_list", partial: "simple_chat/messages/message", locals: { message: self } }
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Simple chat</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "simple_chat/application", "data-turbo-track": "reload" %>
11
+ <%= javascript_importmap_tags %>
12
+ </head>
13
+ <body>
14
+ <% if notice %>
15
+ <p style="color: green"><%= notice %></p>
16
+ <% end %>
17
+ <% if alert %>
18
+ <p style="color: red"><%= alert %></p>
19
+ <% end %>
20
+
21
+ <%= yield %>
22
+
23
+ </body>
24
+ </html>
@@ -0,0 +1,7 @@
1
+ <div id="<%= dom_id chat_room %>">
2
+ <div>
3
+ <strong>Title:</strong>
4
+ <%= chat_room.title %>
5
+ </div>
6
+
7
+ </div>
@@ -0,0 +1,22 @@
1
+ <%= form_with(model: chat_room) do |form| %>
2
+ <% if chat_room.errors.any? %>
3
+ <div style="color: red">
4
+ <h2><%= pluralize(chat_room.errors.count, "error") %> prohibited this chat_room from being saved:</h2>
5
+
6
+ <ul>
7
+ <% chat_room.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div>
15
+ <%= form.label :title, style: "display: block" %>
16
+ <%= form.text_field :title %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= form.submit %>
21
+ </div>
22
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <% content_for :title, "Editing chat room" %>
2
+
3
+ <h1>Editing chat room</h1>
4
+
5
+ <%= render "form", chat_room: @chat_room %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Show this chat room", @chat_room %> |
11
+ <%= link_to "Back to chat rooms", chat_rooms_path %>
12
+ </div>
@@ -0,0 +1,14 @@
1
+ <% content_for :title, "Chat rooms" %>
2
+
3
+ <h1>Chat rooms</h1>
4
+
5
+ <div id="chat_rooms">
6
+ <% @chat_rooms.each do |chat_room| %>
7
+ <%= render chat_room %>
8
+ <p>
9
+ <%= link_to "Show this chat room", chat_room %>
10
+ </p>
11
+ <% end %>
12
+ </div>
13
+
14
+ <%= link_to "New chat room", new_chat_room_path %>
@@ -0,0 +1,11 @@
1
+ <% content_for :title, "New chat room" %>
2
+
3
+ <h1>New chat room</h1>
4
+
5
+ <%= render "form", chat_room: @chat_room %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Back to chat rooms", chat_rooms_path %>
11
+ </div>
@@ -0,0 +1,80 @@
1
+ <div class="mx-auto max-w-4xl px-4 py-8">
2
+ <div class="flex justify-between items-center mb-6 border-b pb-4">
3
+ <h1 class="text-2xl font-bold text-gray-900">
4
+ <%= @chat_room.title %>
5
+ </h1>
6
+ <div class="flex gap-2">
7
+ <%= link_to "Edit", edit_chat_room_path(@chat_room), class: "text-sm text-blue-600 hover:text-blue-800" %>
8
+ <%= link_to "Back", chat_rooms_path, class: "text-sm text-gray-600 hover:text-gray-800" %>
9
+ </div>
10
+ </div>
11
+
12
+ <%= turbo_stream_from "simple_chat", @chat_room %>
13
+
14
+ <div class="bg-white shadow sm:rounded-lg overflow-hidden flex flex-col h-[600px] border border-gray-200">
15
+ <div id="messages_list_container" class="flex-grow overflow-y-auto p-6 bg-gray-50">
16
+ <ul id="messages_list" role="list" class="space-y-6">
17
+ <%= render @messages %>
18
+ </ul>
19
+ </div>
20
+ <script>
21
+ (function() {
22
+ const scrollToBottom = () => {
23
+ const container = document.getElementById('messages_list_container');
24
+ if (container) {
25
+ // Smooth scroll to bottom
26
+ container.scrollTo({
27
+ top: container.scrollHeight,
28
+ behavior: 'smooth'
29
+ });
30
+ }
31
+ };
32
+
33
+ const setupScroll = () => {
34
+ const container = document.getElementById('messages_list_container');
35
+ const list = document.getElementById('messages_list');
36
+
37
+ if (container && list) {
38
+ // Initial scroll
39
+ container.scrollTop = container.scrollHeight;
40
+
41
+ // Scroll when new messages are added via MutationObserver
42
+ const observer = new MutationObserver((mutations) => {
43
+ scrollToBottom();
44
+ });
45
+ observer.observe(list, { childList: true });
46
+
47
+ // Cleanup previous observer if any (though this script runs once per page load)
48
+ window._chatScrollObserver?.disconnect();
49
+ window._chatScrollObserver = observer;
50
+ }
51
+ };
52
+
53
+ // Run on initial load
54
+ if (document.readyState === 'loading') {
55
+ document.addEventListener('DOMContentLoaded', setupScroll);
56
+ } else {
57
+ setupScroll();
58
+ }
59
+
60
+ // Also handle Turbo navigation
61
+ document.addEventListener("turbo:load", setupScroll);
62
+ document.addEventListener("turbo:render", setupScroll);
63
+ })();
64
+ </script>
65
+
66
+ <div id="new-message" class="border-t border-gray-200 p-4 bg-gray-50">
67
+ <div class="flex gap-x-3">
68
+ <div class="size-6 flex-none rounded-full bg-gray-200 flex items-center justify-center ring-1 ring-gray-300">
69
+ <span class="text-[10px] font-bold text-gray-500">?</span>
70
+ </div>
71
+ <%= render "simple_chat/messages/form", message: @message %>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="mt-6 flex justify-between items-center">
77
+ <%= button_to "Leave Chat Room", chat_room_chat_member_path(@chat_room, "current"), method: :delete, data: { confirm: "Are you sure?" }, class: "text-sm text-red-600 hover:text-red-800 font-medium" %>
78
+ <%= button_to "Destroy Room", @chat_room, method: :delete, data: { confirm: "Permanent delete. Are you sure?" }, class: "text-xs text-gray-400 hover:text-red-400" %>
79
+ </div>
80
+ </div>
@@ -0,0 +1,28 @@
1
+ <%= form_with(model: message, class: "relative flex-auto") do |form| %>
2
+ <% if message.errors.any? %>
3
+ <div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mb-4 w-full text-xs">
4
+ <ul>
5
+ <% message.errors.each do |error| %>
6
+ <li><%= error.full_message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </div>
10
+ <% end %>
11
+
12
+ <% if message.chat_room_id.present? %>
13
+ <%= form.hidden_field :chat_room_id %>
14
+ <% else %>
15
+ <div class="mb-2">
16
+ <%= form.collection_select :chat_room_id, SimpleChat::ChatRoom.all, :id, :title, { prompt: "Select Chat Room" }, class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" %>
17
+ </div>
18
+ <% end %>
19
+
20
+ <div class="overflow-hidden rounded-lg pb-12 outline-1 -outline-offset-1 outline-gray-300 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-indigo-600 bg-white">
21
+ <label for="message_content" class="sr-only">Add your message</label>
22
+ <%= form.text_area :content, id: "message_content", rows: 2, placeholder: "Add your message...", class: "block w-full resize-none bg-transparent px-3 py-1.5 text-base text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm/6" %>
23
+ </div>
24
+
25
+ <div class="absolute inset-x-0 bottom-0 flex justify-end py-2 pr-2 pl-3">
26
+ <%= form.submit "Send Message", class: "rounded-md bg-white px-2.5 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 cursor-pointer" %>
27
+ </div>
28
+ <% end %>
@@ -0,0 +1,28 @@
1
+ <%# Using local variable message instead of calling helper to avoid issues in background jobs %>
2
+ <% current_user = local_assigns[:current_user] || (respond_to?(:simple_chat_current_user) ? simple_chat_current_user : nil) %>
3
+ <li id="<%= dom_id message %>" class="relative flex gap-x-4">
4
+ <div class="absolute top-0 -bottom-6 left-0 flex w-6 justify-center">
5
+ <div class="w-px bg-gray-200"></div>
6
+ </div>
7
+
8
+ <div class="relative mt-3 size-6 flex-none rounded-full bg-gray-50 flex items-center justify-center ring-1 ring-gray-200">
9
+ <span class="text-[10px] font-bold text-gray-500"><%= message.user.respond_to?(:name) ? message.user.name[0].upcase : message.user_id %></span>
10
+ </div>
11
+
12
+ <div class="flex-auto rounded-md p-3 ring-1 ring-gray-200 ring-inset <%= message.user == current_user ? 'bg-blue-50/50 ring-blue-100' : 'bg-white' %>">
13
+ <div class="flex justify-between gap-x-4">
14
+ <div class="py-0.5 text-xs/5 text-gray-500">
15
+ <span class="font-medium text-gray-900"><%= message.user.respond_to?(:name) ? message.user.name : "User ##{message.user_id}" %></span>
16
+ <% if message.user == current_user %>
17
+ <span class="ml-1 text-[10px] text-blue-500 font-semibold">(You)</span>
18
+ <% end %>
19
+ </div>
20
+ <time datetime="<%= message.created_at.iso8601 %>" class="flex-none py-0.5 text-xs/5 text-gray-400">
21
+ <%= time_ago_in_words(message.created_at) %> ago
22
+ </time>
23
+ </div>
24
+ <p class="text-sm/6 text-gray-600">
25
+ <%= message.content %>
26
+ </p>
27
+ </div>
28
+ </li>
@@ -0,0 +1,10 @@
1
+ <%= turbo_stream.replace "new-message" do %>
2
+ <div id="new-message" class="border-t border-gray-200 p-4 bg-gray-50">
3
+ <div class="flex gap-x-3">
4
+ <div class="size-6 flex-none rounded-full bg-gray-200 flex items-center justify-center ring-1 ring-gray-300">
5
+ <span class="text-[10px] font-bold text-gray-500">?</span>
6
+ </div>
7
+ <%= render "simple_chat/messages/form", message: @chat_room.messages.build %>
8
+ </div>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <% content_for :title, "Editing message" %>
2
+
3
+ <h1>Editing message</h1>
4
+
5
+ <%= render "form", message: @message %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Show this message", @message %> |
11
+ <%= link_to "Back to messages", messages_path(chat_room_id: @chat_room&.id) %>
12
+ </div>
@@ -0,0 +1,14 @@
1
+ <% content_for :title, "Messages" %>
2
+
3
+ <h1>Messages<%= " for #{@chat_room.title}" if @chat_room %></h1>
4
+
5
+ <% @messages.each do |message| %>
6
+ <%= render message %>
7
+ <p>
8
+ <%= link_to "Show this message", message %>
9
+ </p>
10
+ <% end %>
11
+
12
+ <%= link_to "New message", new_message_path(chat_room_id: @chat_room&.id) %>
13
+ | <%= link_to "Back to chat room", @chat_room if @chat_room %>
14
+ <%= "| #{link_to 'Back to chat rooms', chat_rooms_path}" if @chat_room.nil? %>
@@ -0,0 +1,11 @@
1
+ <% content_for :title, "New message" %>
2
+
3
+ <h1>New message</h1>
4
+
5
+ <%= render "form", message: @message %>
6
+
7
+ <br>
8
+
9
+ <div>
10
+ <%= link_to "Back to messages", messages_path(chat_room_id: @chat_room&.id) %>
11
+ </div>
@@ -0,0 +1,8 @@
1
+ <%= render @message %>
2
+
3
+ <div>
4
+ <%= link_to "Edit this message", edit_message_path(@message) %> |
5
+ <%= link_to "Back to messages", messages_path(chat_room_id: @chat_room&.id) %>
6
+
7
+ <%= button_to "Destroy this message", @message, method: :delete %>
8
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ SimpleChat::Engine.routes.draw do
2
+ root to: "chat_rooms#index"
3
+ resources :chat_rooms do
4
+ resources :chat_members, only: [:create, :destroy]
5
+ end
6
+ resources :messages
7
+ end
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ content: [
3
+ './app/views/**/*.html.erb',
4
+ './app/helpers/**/*.rb',
5
+ './app/javascript/**/*.js',
6
+ './test/dummy/app/views/**/*.html.erb',
7
+ './test/dummy/app/javascript/**/*.js',
8
+ ],
9
+ theme: {
10
+ extend: {},
11
+ },
12
+ plugins: [
13
+ require('@tailwindcss/forms'),
14
+ ],
15
+ }
@@ -0,0 +1,9 @@
1
+ class CreateSimpleChatChatRooms < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :simple_chat_chat_rooms do |t|
4
+ t.string :title
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ class CreateSimpleChatChatMembers < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :simple_chat_chat_members do |t|
4
+ t.references :chat_room, null: false, foreign_key: { to_table: :simple_chat_chat_rooms }
5
+ t.integer :user_id, null: false
6
+
7
+ t.timestamps
8
+ end
9
+ add_index :simple_chat_chat_members, :user_id
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class CreateSimpleChatMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :simple_chat_messages do |t|
4
+ t.references :chat_room, null: false, foreign_key: { to_table: :simple_chat_chat_rooms }
5
+ t.integer :user_id, null: false
6
+ t.text :content
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :simple_chat_messages, :user_id
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class AddChannelHashToSolidCableMessages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :solid_cable_messages, :channel_hash, :integer, limit: 8, null: false
4
+ add_index :solid_cable_messages, :channel_hash
5
+ end
6
+ end