]> git.openstreetmap.org Git - rails.git/commitdiff
Add basic structures for UserMute and Message muting logic
authorGregory Igelmund <github.com@grekko.de>
Wed, 13 Dec 2023 18:06:28 +0000 (13:06 -0500)
committerGregory Igelmund <github.com@grekko.de>
Tue, 19 Dec 2023 17:57:47 +0000 (12:57 -0500)
Including models, migration, controllers, views & locales.

18 files changed:
app/abilities/ability.rb
app/controllers/messages_controller.rb
app/controllers/user_mutes_controller.rb [new file with mode: 0644]
app/models/message.rb
app/models/user.rb
app/models/user_mute.rb [new file with mode: 0644]
app/views/application/_settings_menu.html.erb
app/views/messages/_heading.html.erb
app/views/messages/_message_summary.html.erb
app/views/messages/muted.html.erb [new file with mode: 0644]
app/views/user_mutes/index.html.erb [new file with mode: 0644]
app/views/users/show.html.erb
config/locales/en.yml
config/routes.rb
db/migrate/20231010201451_create_user_mutes.rb [new file with mode: 0644]
db/migrate/20231010203028_add_muted_flag_to_messages.rb [new file with mode: 0644]
db/structure.sql
script/deliver-message

index f9348f68e707ae9897bd0a2e2690e7e4187573d0..b9da5d08ac596166124884cc2864eb31155be342 100644 (file)
@@ -47,13 +47,14 @@ class Ability
         can [:show], :dashboard
         can [:new, :create, :edit, :update, :comment, :subscribe, :unsubscribe], DiaryEntry
         can [:make_friend, :remove_friend], Friendship
-        can [:new, :create, :reply, :show, :inbox, :outbox, :mark, :destroy], Message
+        can [:new, :create, :reply, :show, :inbox, :outbox, :muted, :mark, :unmute, :destroy], Message
         can [:close, :reopen], Note
         can [:show, :edit, :update], :preference
         can [:edit, :update], :profile
         can [:new, :create], Report
         can [:mine, :new, :create, :edit, :update, :destroy], Trace
         can [:account, :go_public], User
+        can [:index, :create, :destroy], UserMute
 
         if user.moderator?
           can [:hide, :unhide, :hidecomment, :unhidecomment], DiaryEntry
index adb53b43ba3842d8cd0b3faf5aaa898abde4b213..2ca86fc028dca2dbe9af4c0a8ec14b71665c3ca9 100644 (file)
@@ -47,7 +47,7 @@ class MessagesController < ApplicationController
       render :action => "new"
     elsif @message.save
       flash[:notice] = t ".message_sent"
-      UserMailer.message_notification(@message).deliver_later
+      UserMailer.message_notification(@message).deliver_later if @message.notify_recipient?
       redirect_to :action => :inbox
     else
       @title = t "messages.new.title"
@@ -107,6 +107,13 @@ class MessagesController < ApplicationController
     @title = t ".title"
   end
 
+  # Display the list of muted messages received by the user.
+  def muted
+    @title = t ".title"
+
+    redirect_to inbox_messages_path if current_user.muted_messages.none?
+  end
+
   # Set the message as being read or unread.
   def mark
     @message = Message.where(:recipient => current_user).or(Message.where(:sender => current_user)).find(params[:message_id])
@@ -127,6 +134,23 @@ class MessagesController < ApplicationController
     render :action => "no_such_message", :status => :not_found
   end
 
+  # Moves message into Inbox by unsetting the muted-flag
+  def unmute
+    message = current_user.muted_messages.find(params[:message_id])
+
+    if message.unmute
+      flash[:notice] = t(".notice")
+    else
+      flash[:error] = t(".error")
+    end
+
+    if current_user.muted_messages.none?
+      redirect_to inbox_messages_path
+    else
+      redirect_to muted_messages_path
+    end
+  end
+
   private
 
   ##
diff --git a/app/controllers/user_mutes_controller.rb b/app/controllers/user_mutes_controller.rb
new file mode 100644 (file)
index 0000000..2068ab6
--- /dev/null
@@ -0,0 +1,45 @@
+class UserMutesController < ApplicationController
+  include UserMethods
+
+  layout "site"
+
+  before_action :authorize_web
+  before_action :set_locale
+
+  authorize_resource
+
+  before_action :lookup_user, :only => [:create, :destroy]
+  before_action :check_database_readable
+  before_action :check_database_writable, :only => [:create, :destroy]
+
+  def index
+    @muted_users = current_user.muted_users
+    @title = t ".title"
+
+    redirect_to edit_account_path unless @muted_users.any?
+  end
+
+  def create
+    user_mute = current_user.mutes.build(:subject => @user)
+
+    if user_mute.save
+      flash[:notice] = t(".notice", :name => user_mute.subject.display_name)
+    else
+      flash[:error] = t(".error", :name => user_mute.subject.display_name, :full_message => user_mute.errors.full_messages.to_sentence.humanize)
+    end
+
+    redirect_back_or_to user_mutes_path(current_user)
+  end
+
+  def destroy
+    user_mute = current_user.mutes.find_by!(:subject => @user)
+
+    if user_mute.destroy
+      flash[:notice] = t(".notice", :name => user_mute.subject.display_name)
+    else
+      flash[:error] = t(".error")
+    end
+
+    redirect_back_or_to user_mutes_path(current_user)
+  end
+end
index 7c12769d3a3e39f081caeb16dd0417a858a0ddc2..665e2d721376b897738691c73cf4a62c0bc3bd4b 100644 (file)
@@ -12,6 +12,7 @@
 #  to_user_visible   :boolean          default(TRUE), not null
 #  from_user_visible :boolean          default(TRUE), not null
 #  body_format       :enum             default("markdown"), not null
+#  muted             :boolean          default(FALSE), not null
 #
 # Indexes
 #
@@ -32,6 +33,10 @@ class Message < ApplicationRecord
   validates :body, :sent_on, :presence => true
   validates :title, :body, :characters => true
 
+  scope :muted, -> { where(:muted => true) }
+
+  before_create :set_muted
+
   def self.from_mail(mail, from, to)
     if mail.multipart?
       if mail.text_part
@@ -65,4 +70,18 @@ class Message < ApplicationRecord
     sha256 << id.to_s
     Base64.urlsafe_encode64(sha256.digest)[0, 8]
   end
+
+  def notify_recipient?
+    !muted?
+  end
+
+  def unmute
+    update(:muted => false)
+  end
+
+  private
+
+  def set_muted
+    self.muted ||= UserMute.active?(:owner => recipient, :subject => sender)
+  end
 end
index 1942a25cc237a784394b91f9ba64c844f36e3309..5790d81e51d1be60d561eaa934aa863e2aad913b 100644 (file)
@@ -51,9 +51,10 @@ class User < ApplicationRecord
   has_many :diary_comments, -> { order(:created_at => :desc) }, :inverse_of => :user
   has_many :diary_entry_subscriptions, :class_name => "DiaryEntrySubscription"
   has_many :diary_subscriptions, :through => :diary_entry_subscriptions, :source => :diary_entry
-  has_many :messages, -> { where(:to_user_visible => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :foreign_key => :to_user_id
-  has_many :new_messages, -> { where(:to_user_visible => true, :message_read => false).order(:sent_on => :desc) }, :class_name => "Message", :foreign_key => :to_user_id
+  has_many :messages, -> { where(:to_user_visible => true, :muted => false).order(:sent_on => :desc).preload(:sender, :recipient) }, :foreign_key => :to_user_id
+  has_many :new_messages, -> { where(:to_user_visible => true, :muted => false, :message_read => false).order(:sent_on => :desc) }, :class_name => "Message", :foreign_key => :to_user_id
   has_many :sent_messages, -> { where(:from_user_visible => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :class_name => "Message", :foreign_key => :from_user_id
+  has_many :muted_messages, -> { where(:to_user_visible => true, :muted => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :class_name => "Message", :foreign_key => :to_user_id
   has_many :friendships, -> { joins(:befriendee).where(:users => { :status => %w[active confirmed] }) }
   has_many :friends, :through => :friendships, :source => :befriendee
   has_many :tokens, :class_name => "UserToken", :dependent => :destroy
@@ -75,6 +76,9 @@ class User < ApplicationRecord
   has_many :blocks_created, :class_name => "UserBlock", :foreign_key => :creator_id, :inverse_of => :creator
   has_many :blocks_revoked, :class_name => "UserBlock", :foreign_key => :revoker_id, :inverse_of => :revoker
 
+  has_many :mutes, -> { order(:created_at => :desc) }, :class_name => "UserMute", :foreign_key => :owner_id, :inverse_of => :owner
+  has_many :muted_users, :through => :mutes, :source => :subject
+
   has_many :roles, :class_name => "UserRole"
 
   has_many :issues, :class_name => "Issue", :foreign_key => :reported_user_id, :inverse_of => :reported_user
diff --git a/app/models/user_mute.rb b/app/models/user_mute.rb
new file mode 100644 (file)
index 0000000..9bee39b
--- /dev/null
@@ -0,0 +1,34 @@
+# == Schema Information
+#
+# Table name: user_mutes
+#
+#  id         :bigint(8)        not null, primary key
+#  owner_id   :bigint(8)        not null
+#  subject_id :bigint(8)        not null
+#  created_at :datetime         not null
+#  updated_at :datetime         not null
+#
+# Indexes
+#
+#  index_user_mutes_on_owner_id_and_subject_id  (owner_id,subject_id) UNIQUE
+#
+# Foreign Keys
+#
+#  fk_rails_...  (owner_id => users.id)
+#  fk_rails_...  (subject_id => users.id)
+#
+class UserMute < ApplicationRecord
+  belongs_to :owner, :class_name => "User"
+  belongs_to :subject, :class_name => "User"
+
+  validates :subject, :uniqueness => { :scope => :owner_id, :message => :is_already_muted }
+
+  def self.active?(owner:, subject:)
+    !subject.administrator? &&
+      !subject.moderator? &&
+      exists?(
+        :owner => owner,
+        :subject => subject
+      )
+  end
+end
index 9ce9755a2c5f3e452e016f2ce0fb6e6551250c40..8477a11a05af10c542e3b9dd17433be9904d3f64 100644 (file)
     <li class="nav-item">
       <%= link_to t(".oauth2_authorizations"), oauth_authorized_applications_path, :class => "nav-link #{'active' if controller_name == 'oauth2_authorized_applications'}" %>
     </li>
+    <% if current_user.muted_users.any? %>
+      <li class="nav-item">
+        <%= link_to t(".muted_users"), user_mutes_path, :class => "nav-link #{'active' if controller_name == 'user_mutes'}" %>
+      </li>
+    <% end %>
   </ul>
 <% end %>
index 12c77d3f5c9ed7706b673a3719ed90e94281bd61..90995ed88d999fb3ef896595d9ffbacc074a0ce1 100644 (file)
@@ -3,7 +3,9 @@
 <% content_for :heading do %>
   <h1><%= t("users.show.my messages") %></h1>
   <ul class="nav nav-tabs">
-    <% { ".my_inbox" => inbox_messages_path, ".my_outbox" => outbox_messages_path }.each do |i18n_key, path| %>
+    <% { ".my_inbox" => inbox_messages_path, ".my_outbox" => outbox_messages_path, ".muted_messages" => muted_messages_path }.each do |i18n_key, path| %>
+    <% next if path == muted_messages_path && current_user.muted_messages.none? %>
+
     <li class="nav-item">
       <% if path == active_link_path %>
         <a class="nav-link active"><%= t(i18n_key) %></a>
index b2a1bc26868e82de84ee3cac44a0357c313a36b2..4a552e83e05479a7d6df58d9cd0f23311c3742ef 100644 (file)
@@ -6,5 +6,8 @@
     <%= button_to t(".unread_button"), message_mark_path(message_summary, :mark => "unread"), :remote => true, :class => "btn btn-sm btn-primary", :form => { :class => "inbox-mark-unread", :hidden => !message_summary.message_read? } %>
     <%= button_to t(".read_button"), message_mark_path(message_summary, :mark => "read"), :remote => true, :class => "btn btn-sm btn-primary", :form => { :class => "inbox-mark-read", :hidden => message_summary.message_read? } %>
     <%= button_to t(".destroy_button"), message_path(message_summary, :referer => request.fullpath), :method => :delete, :remote => true, :class => "btn btn-sm btn-danger", :form_class => "inbox-destroy" %>
+    <% if message_summary.muted? %>
+      <%= button_to t(".unmute_button"), message_unmute_path(message_summary), :method => :patch, :class => "btn btn-sm btn-secondary" %>
+    <% end %>
   </td>
 </tr>
diff --git a/app/views/messages/muted.html.erb b/app/views/messages/muted.html.erb
new file mode 100644 (file)
index 0000000..40c74e9
--- /dev/null
@@ -0,0 +1,9 @@
+<% content_for :head do %>
+  <%= javascript_include_tag "messages" %>
+<% end %>
+
+<%= render :partial => "heading", :locals => { :active_link_path => muted_messages_path } %>
+
+<h4><%= t ".messages", :count => current_user.muted_messages.size %></h4>
+
+<%= render :partial => "messages_table", :locals => { :columns => %w[from subject date], :messages => current_user.muted_messages, :inner_partial => "message_summary" } %>
diff --git a/app/views/user_mutes/index.html.erb b/app/views/user_mutes/index.html.erb
new file mode 100644 (file)
index 0000000..cf9e7ed
--- /dev/null
@@ -0,0 +1,38 @@
+<% content_for :heading do %>
+  <h1><%= t ".my_muted_users" %></h1>
+<% end %>
+
+<%= render :partial => "settings_menu" %>
+
+<h4>
+  <%= t ".you_have_muted_n_users", :count => @muted_users.size %>
+</h4>
+<p>
+  <%= t ".user_mute_explainer" %>
+  <em><%= t ".user_mute_admins_and_moderators" %></em>
+</p>
+
+<% if @muted_users.any? %>
+  <table class="table">
+    <thead>
+      <tr>
+        <th><%= t ".table.thead.muted_user" %></th>
+        <th class="d-flex justify-content-end"><%= t ".table.thead.actions" %></th>
+      </tr>
+    </thead>
+    <tbody>
+      <% @muted_users.each do |user| %>
+        <tr>
+          <td>
+            <%= user_thumbnail_tiny user %>
+            <%= link_to user.display_name, user_path(user) %>
+          </td>
+          <td class="text-nowrap text-end">
+            <%= link_to t(".table.tbody.unmute"), user_mute_path(user), :method => :delete, :class => "btn btn-sm btn-primary" %>
+            <%= link_to t(".table.tbody.send_message"), new_message_path(user), :class => "btn btn-sm btn-secondary" %>
+          </td>
+        </tr>
+      <% end %>
+    </tbody>
+  </table>
+<% end %>
index 81fda926b7a081b333add6d07f6d3aaa87989668..0c803ebb03cf3492919d24210c0c6ed4716502c2 100644 (file)
                 <%= report_link(t(".report"), @user) %>
               </li>
             <% end %>
+
+            <% if current_user and UserMute.exists?(owner: current_user, subject: @user) %>
+              <li>
+              <%= link_to t(".destroy_mute"), user_mute_path(@user), :method => :delete %>
+              </li>
+            <% elsif current_user %>
+              <li>
+              <%= link_to t(".create_mute"), user_mute_path(@user), :method => :post, :title => t("user_mutes.index.user_mute_explainer") %>
+              </li>
+            <% end %>
           </ul>
         </nav>
       <% end %>
index c68e676223e64645b0ba4fad48e4dd0783927c08..7ff2bfd0676ed460f1b4a66afc209529e34da16c 100644 (file)
@@ -40,6 +40,12 @@ en:
       messages:
         invalid_email_address: does not appear to be a valid e-mail address
         email_address_not_routable: is not routable
+      models:
+        user_mute:
+          attributes:
+            subject:
+              format: "%{message}"
+          is_already_muted: "is already muted"
     # Translates all the model names, which is used in error handling on the website
     models:
       acl: "Access Control List"
@@ -1673,8 +1679,6 @@ en:
   messages:
     inbox:
       title: "Inbox"
-      my_inbox: "My Inbox"
-      my_outbox: "My Outbox"
       messages: "You have %{new_messages} and %{old_messages}"
       new_messages:
         one: "%{count} new message"
@@ -1695,6 +1699,7 @@ en:
       read_button: "Mark as read"
       reply_button: "Reply"
       destroy_button: "Delete"
+      unmute_button: "Move to Inbox"
     new:
       title: "Send message"
       send_message_to_html: "Send a new message to %{name}"
@@ -1708,14 +1713,17 @@ en:
       body: "Sorry there is no message with that id."
     outbox:
       title: "Outbox"
-      my_inbox: "My Inbox"
-      my_outbox: "My Outbox"
       actions: "Actions"
       messages:
         one: "You have %{count} sent message"
         other: "You have %{count} sent messages"
       no_sent_messages_html: "You have no sent messages yet. Why not get in touch with some of the %{people_mapping_nearby_link}?"
       people_mapping_nearby: "people mapping nearby"
+    muted:
+      title: "Muted Messages"
+      messages:
+        one: "%{count} muted message"
+        other: "You have %{count} muted messages"
     reply:
       wrong_user: "You are logged in as `%{user}' but the message you have asked to reply to was not sent to that user. Please login as the correct user in order to reply."
     show:
@@ -1727,12 +1735,16 @@ en:
       wrong_user: "You are logged in as `%{user}' but the message you have asked to read was not sent by or to that user. Please login as the correct user in order to read it."
     sent_message_summary:
       destroy_button: "Delete"
-    heading:
+    heading: 
       my_inbox: "My Inbox"
       my_outbox: "My Outbox"
+      muted_messages: "Muted messages"
     mark:
       as_read: "Message marked as read"
       as_unread: "Message marked as unread"
+    unmute:
+      notice: "Message has been moved to Inbox"
+      error: "The message could not be moved to the Inbox."
     destroy:
       destroyed: "Message deleted"
   passwords:
@@ -2562,6 +2574,7 @@ en:
       oauth1_settings: OAuth 1 settings
       oauth2_applications: OAuth 2 applications
       oauth2_authorizations: OAuth 2 authorizations
+      muted_users: Muted Users
   oauth:
     authorize:
       title: "Authorize access to your account"
@@ -2749,6 +2762,8 @@ en:
       my_dashboard: My Dashboard
       blocks on me: Blocks on Me
       blocks by me: Blocks by Me
+      create_mute: Mute this User
+      destroy_mute: Unmute this User
       edit_profile: Edit Profile
       send message: Send Message
       diary: Diary
@@ -2939,6 +2954,29 @@ en:
       showing_page: "Page %{page}"
       next: "Next »"
       previous: "« Previous"
+  user_mutes:
+    index:
+      title: "Muted Users"
+      my_muted_users: "My muted users"
+      you_have_muted_n_users:
+        one: "You have muted %{count} User"
+        other: "You have muted %{count} users"
+      user_mute_explainer: "Messages of muted users are moved into a separate Inbox and you won't receive email notifications."
+      user_mute_admins_and_moderators: "You can mute Admins and Moderators but their messages will not be muted."
+      table:
+        thead:
+          muted_user: "Muted User"
+          actions: "Actions"
+        tbody:
+          unmute: "Unmute"
+          send_message: "Send message"
+
+    create:
+      notice: "You muted %{name}."
+      error: "%{name} could not be muted. %{full_message}."
+    destroy:
+      notice: "You unmuted %{name}."
+      error: "User could not be unmuted. Please try again."
   notes:
     index:
       title: "Notes submitted or commented on by %{user}"
index 2b67e360e871776b3420d88cdef499b7546f528d..110a67a4984a80b7f17182f92432b01f7d1a7e65 100644 (file)
@@ -282,9 +282,12 @@ OpenStreetMap::Application.routes.draw do
   # messages
   resources :messages, :only => [:create, :show, :destroy] do
     post :mark
+    patch :unmute
+
     match :reply, :via => [:get, :post]
     collection do
       get :inbox
+      get :muted
       get :outbox
     end
   end
@@ -293,6 +296,12 @@ OpenStreetMap::Application.routes.draw do
   get "/message/new/:display_name" => "messages#new", :as => "new_message"
   get "/message/read/:message_id", :to => redirect(:path => "/messages/%{message_id}")
 
+  # muting users
+  scope "/user/:display_name" do
+    resource :user_mute, :only => [:create, :destroy], :path => "mute"
+  end
+  resources :user_mutes, :only => [:index]
+
   # oauth admin pages (i.e: for setting up new clients, etc...)
   scope "/user/:display_name" do
     resources :oauth_clients
diff --git a/db/migrate/20231010201451_create_user_mutes.rb b/db/migrate/20231010201451_create_user_mutes.rb
new file mode 100644 (file)
index 0000000..8cb6ff8
--- /dev/null
@@ -0,0 +1,15 @@
+class CreateUserMutes < ActiveRecord::Migration[7.0]
+  def change
+    create_table :user_mutes do |t|
+      t.references :owner, :null => false, :index => false
+      t.references :subject, :null => false, :index => false
+
+      t.timestamps
+
+      t.foreign_key :users, :column => :owner_id
+      t.foreign_key :users, :column => :subject_id
+
+      t.index [:owner_id, :subject_id], :unique => true
+    end
+  end
+end
diff --git a/db/migrate/20231010203028_add_muted_flag_to_messages.rb b/db/migrate/20231010203028_add_muted_flag_to_messages.rb
new file mode 100644 (file)
index 0000000..e517aea
--- /dev/null
@@ -0,0 +1,5 @@
+class AddMutedFlagToMessages < ActiveRecord::Migration[7.0]
+  def change
+    add_column :messages, :muted, :boolean, :default => false, :null => false
+  end
+end
index ba60918f0ee1a93c8333aef7544fe52ed647cf0f..0563417cdf07f81577a51a8dab9770d8b57ea423 100644 (file)
@@ -951,7 +951,8 @@ CREATE TABLE public.messages (
     to_user_id bigint NOT NULL,
     to_user_visible boolean DEFAULT true NOT NULL,
     from_user_visible boolean DEFAULT true NOT NULL,
-    body_format public.format_enum DEFAULT 'markdown'::public.format_enum NOT NULL
+    body_format public.format_enum DEFAULT 'markdown'::public.format_enum NOT NULL,
+    muted boolean DEFAULT false NOT NULL
 );
 
 
@@ -1454,6 +1455,38 @@ CREATE SEQUENCE public.user_blocks_id_seq
 ALTER SEQUENCE public.user_blocks_id_seq OWNED BY public.user_blocks.id;
 
 
+--
+-- Name: user_mutes; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE public.user_mutes (
+    id bigint NOT NULL,
+    owner_id bigint NOT NULL,
+    subject_id bigint NOT NULL,
+    created_at timestamp(6) without time zone NOT NULL,
+    updated_at timestamp(6) without time zone NOT NULL
+);
+
+
+--
+-- Name: user_mutes_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.user_mutes_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+--
+-- Name: user_mutes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.user_mutes_id_seq OWNED BY public.user_mutes.id;
+
+
 --
 -- Name: user_preferences; Type: TABLE; Schema: public; Owner: -
 --
@@ -1835,6 +1868,13 @@ ALTER TABLE ONLY public.reports ALTER COLUMN id SET DEFAULT nextval('public.repo
 ALTER TABLE ONLY public.user_blocks ALTER COLUMN id SET DEFAULT nextval('public.user_blocks_id_seq'::regclass);
 
 
+--
+-- Name: user_mutes id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_mutes ALTER COLUMN id SET DEFAULT nextval('public.user_mutes_id_seq'::regclass);
+
+
 --
 -- Name: user_roles id; Type: DEFAULT; Schema: public; Owner: -
 --
@@ -2216,6 +2256,14 @@ ALTER TABLE ONLY public.user_blocks
     ADD CONSTRAINT user_blocks_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: user_mutes user_mutes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_mutes
+    ADD CONSTRAINT user_mutes_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: user_preferences user_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: -
 --
@@ -2734,6 +2782,13 @@ CREATE INDEX index_reports_on_user_id ON public.reports USING btree (user_id);
 CREATE INDEX index_user_blocks_on_user_id ON public.user_blocks USING btree (user_id);
 
 
+--
+-- Name: index_user_mutes_on_owner_id_and_subject_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE UNIQUE INDEX index_user_mutes_on_owner_id_and_subject_id ON public.user_mutes USING btree (owner_id, subject_id);
+
+
 --
 -- Name: messages_from_user_id_idx; Type: INDEX; Schema: public; Owner: -
 --
@@ -3107,6 +3162,14 @@ ALTER TABLE ONLY public.oauth_access_grants
     ADD CONSTRAINT fk_rails_330c32d8d9 FOREIGN KEY (resource_owner_id) REFERENCES public.users(id) NOT VALID;
 
 
+--
+-- Name: user_mutes fk_rails_591dad3359; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_mutes
+    ADD CONSTRAINT fk_rails_591dad3359 FOREIGN KEY (owner_id) REFERENCES public.users(id);
+
+
 --
 -- Name: oauth_access_tokens fk_rails_732cb83ab7; Type: FK CONSTRAINT; Schema: public; Owner: -
 --
@@ -3155,6 +3218,14 @@ ALTER TABLE ONLY public.oauth_applications
     ADD CONSTRAINT fk_rails_cc886e315a FOREIGN KEY (owner_id) REFERENCES public.users(id) NOT VALID;
 
 
+--
+-- Name: user_mutes fk_rails_e9dd4fb6c3; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_mutes
+    ADD CONSTRAINT fk_rails_e9dd4fb6c3 FOREIGN KEY (subject_id) REFERENCES public.users(id);
+
+
 --
 -- Name: oauth_access_tokens fk_rails_ee63f25419; Type: FK CONSTRAINT; Schema: public; Owner: -
 --
@@ -3514,6 +3585,8 @@ INSERT INTO "schema_migrations" (version) VALUES
 ('20231117170422'),
 ('20231101222146'),
 ('20231029151516'),
+('20231010203028'),
+('20231010201451'),
 ('20231010194809'),
 ('20231007141103'),
 ('20230830115220'),
index 28d755b24d4a40e74934d50600258764234d5803..81de3ef587402b35eeff71727e49b85da903da81 100755 (executable)
@@ -33,6 +33,6 @@ mail = Mail.new($stdin.read
 message = Message.from_mail(mail, from, to)
 message.save!
 
-UserMailer.message_notification(message).deliver
+UserMailer.message_notification(message).deliver if message.notify_recipient?
 
 exit 0