rails_notice 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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +165 -0
  3. data/README.md +76 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/rails_notice_manifest.js +3 -0
  6. data/app/assets/javascripts/controllers/notifications/index.js +0 -0
  7. data/app/assets/javascripts/controllers/rails_notice_admin/notification_settings/index.js +17 -0
  8. data/app/assets/javascripts/controllers/rails_notice_admin/notifications/index.js +15 -0
  9. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/edit.js +3 -0
  10. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/index.js +17 -0
  11. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/new.js +18 -0
  12. data/app/assets/javascripts/rails_notice/cable.js +19 -0
  13. data/app/assets/javascripts/rails_notice/channels/notices.js +39 -0
  14. data/app/assets/javascripts/rails_notice/messenger-theme-future.js +33 -0
  15. data/app/assets/javascripts/rails_notice/messenger.min.js +2 -0
  16. data/app/assets/stylesheets/the_notify/cable.css +9 -0
  17. data/app/assets/stylesheets/the_notify/messenger-theme-air.css +357 -0
  18. data/app/assets/stylesheets/the_notify/messenger.css +105 -0
  19. data/app/channels/notices_channel.rb +7 -0
  20. data/app/channels/the_notify_connection.rb +30 -0
  21. data/app/controllers/concerns/rails_notice_controller.rb +18 -0
  22. data/app/controllers/rails_notice_admin/base_controller.rb +6 -0
  23. data/app/controllers/rails_notice_admin/notification_settings_controller.rb +55 -0
  24. data/app/controllers/rails_notice_admin/notifications_controller.rb +71 -0
  25. data/app/controllers/rails_notice_my/base_controller.rb +5 -0
  26. data/app/controllers/rails_notice_my/notification_settings_controller.rb +28 -0
  27. data/app/controllers/rails_notice_my/notifications_controller.rb +78 -0
  28. data/app/controllers/receivers_controller.rb +12 -0
  29. data/app/jobs/notification_job.rb +17 -0
  30. data/app/mailers/rails_notice_mailer.rb +21 -0
  31. data/app/models/annunciation.rb +4 -0
  32. data/app/models/concerns/i18n_helper.rb +14 -0
  33. data/app/models/concerns/the_notifiable.rb +33 -0
  34. data/app/models/concerns/the_publishable.rb +9 -0
  35. data/app/models/concerns/the_receivable.rb +17 -0
  36. data/app/models/notification.rb +156 -0
  37. data/app/models/notification_setting.rb +8 -0
  38. data/app/views/rails_notice/_link.html.erb +6 -0
  39. data/app/views/rails_notice_admin/base/_nav.html.erb +16 -0
  40. data/app/views/rails_notice_admin/notification_settings/_form.html.erb +7 -0
  41. data/app/views/rails_notice_admin/notification_settings/_search_form.html.erb +11 -0
  42. data/app/views/rails_notice_admin/notification_settings/edit.html.erb +14 -0
  43. data/app/views/rails_notice_admin/notification_settings/index.html.erb +52 -0
  44. data/app/views/rails_notice_admin/notification_settings/new.html.erb +13 -0
  45. data/app/views/rails_notice_admin/notification_settings/show.html.erb +0 -0
  46. data/app/views/rails_notice_admin/notifications/_form.html.erb +6 -0
  47. data/app/views/rails_notice_admin/notifications/_menu.html.erb +6 -0
  48. data/app/views/rails_notice_admin/notifications/_search_form.html.erb +11 -0
  49. data/app/views/rails_notice_admin/notifications/edit.html.erb +14 -0
  50. data/app/views/rails_notice_admin/notifications/index.html.erb +44 -0
  51. data/app/views/rails_notice_admin/notifications/new.html.erb +15 -0
  52. data/app/views/rails_notice_mailer/notify.html.erb +35 -0
  53. data/app/views/rails_notice_my/base/_nav_my.html.erb +4 -0
  54. data/app/views/rails_notice_my/notification_settings/_form.html.erb +5 -0
  55. data/app/views/rails_notice_my/notification_settings/show.html.erb +5 -0
  56. data/app/views/rails_notice_my/notifications/_navbar.html.erb +13 -0
  57. data/app/views/rails_notice_my/notifications/_notifications.html.erb +17 -0
  58. data/app/views/rails_notice_my/notifications/edit.html.erb +2 -0
  59. data/app/views/rails_notice_my/notifications/index.html.erb +28 -0
  60. data/app/views/rails_notice_my/notifications/index.js.erb +13 -0
  61. data/app/views/rails_notice_my/notifications/new.html.erb +1 -0
  62. data/app/views/rails_notice_my/notifications/read.js.erb +2 -0
  63. data/app/views/rails_notice_my/notifications/read_all.js.erb +2 -0
  64. data/app/views/rails_notice_my/notifications/show.html.erb +7 -0
  65. data/config/locales/en.yml +7 -0
  66. data/config/locales/zh.yml +7 -0
  67. data/config/routes.rb +29 -0
  68. data/db/migrate/20170110025557_create_notification.rb +21 -0
  69. data/db/migrate/20170209025946_create_notification_settings.rb +23 -0
  70. data/lib/rails_notice.rb +13 -0
  71. data/lib/rails_notice/active_record.rb +18 -0
  72. data/lib/rails_notice/config.rb +13 -0
  73. data/lib/rails_notice/engine.rb +9 -0
  74. data/lib/rails_notice/version.rb +3 -0
  75. data/lib/tasks/the_notify_tasks.rake +4 -0
  76. metadata +132 -0
@@ -0,0 +1,156 @@
1
+ class Notification < ApplicationRecord
2
+ serialize :cc_emails, Array
3
+
4
+ attribute :code, :string, default: 'default'
5
+ belongs_to :receiver, polymorphic: true
6
+ belongs_to :sender, polymorphic: true, optional: true
7
+ belongs_to :notifiable, polymorphic: true, optional: true
8
+ has_one :notification_setting, ->(o) { where(receiver_type: o.receiver_type) }, primary_key: :receiver_id, foreign_key: :receiver_id
9
+
10
+ default_scope -> { order(id: :desc) }
11
+ scope :unread, -> { where(read_at: nil) }
12
+ scope :have_read, -> { where.not(read_at: nil) }
13
+
14
+ after_create_commit :process_job,
15
+ :send_email,
16
+ :update_unread_count
17
+
18
+ def process_job
19
+ make_as_unread
20
+ if sending_at
21
+ NotificationJob.set(wait_until: sending_at).perform_later id
22
+ else
23
+ NotificationJob.perform_later(self.id)
24
+ end
25
+ end
26
+
27
+ def send_email
28
+ return unless email_enable?
29
+
30
+ if notify_setting[:mailer_class]
31
+ notify_method = notify_setting[:mailer_method] || 'notify'
32
+ if sending_at
33
+ notify_setting[:mailer_class].public_send(notify_method, self.notifiable_id).deliver_later(wait_until: sending_at)
34
+ else
35
+ notify_setting[:mailer_class].public_send(notify_method, self.notifiable_id).deliver_later
36
+ end
37
+ else
38
+ if sending_at
39
+ RailsNoticeMailer.notify(self.id).deliver_later(wait_until: sending_at)
40
+ else
41
+ RailsNoticeMailer.notify(self.id).deliver_later
42
+ end
43
+ end
44
+ end
45
+
46
+ def email_enable?
47
+ if receiver.notification_setting&.accept_email
48
+ return true
49
+ end
50
+
51
+ if receiver.notification_setting&.accept_email.is_a?(FalseClass)
52
+ return false
53
+ end
54
+
55
+ RailsNotice.config.default_send_email
56
+ end
57
+
58
+ def notifiable_detail
59
+ r = self.notifiable.as_json(**notify_setting.slice(:only, :except, :include, :methods))
60
+ Hash(r).with_indifferent_access
61
+ end
62
+
63
+ def notifiable_attributes
64
+ if verbose
65
+ r = notifiable_detail
66
+ r.transform_values! do |i|
67
+ next i unless i.acts_like?(:time)
68
+ if self.receiver.respond_to?(:timezone)
69
+ _zone = self.receiver.timezone
70
+ else
71
+ _zone = Time.zone.name
72
+ end
73
+ i.in_time_zone(_zone).strftime '%Y-%m-%d %H:%M:%S'
74
+ end
75
+ r
76
+ else
77
+ {}.with_indifferent_access
78
+ end
79
+ end
80
+
81
+ def notify_setting
82
+ notifiable_type.constantize.notifies[self.code] || {}
83
+ end
84
+
85
+ def tr_key(column)
86
+ "#{self.class.i18n_scope}.notify.#{notifiable.class.base_class.model_name.i18n_key}.#{self.code}.#{column}"
87
+ end
88
+
89
+ def code
90
+ super ? super.to_sym : :default
91
+ end
92
+
93
+ def title
94
+ return super unless notifiable
95
+ tr_values = notifiable_detail.slice *I18nHelper.interpolate_key(I18n.t(tr_key(:title)))
96
+ tr_values.merge! notify_setting.fetch(:tr_values, {})
97
+
98
+ if super.blank?
99
+ I18n.t tr_key(:title), tr_values
100
+ else
101
+ super
102
+ end
103
+ end
104
+
105
+ def body
106
+ return super unless notifiable
107
+ tr_values = notifiable_detail.slice *I18nHelper.interpolate_key(I18n.t(tr_key(:body)))
108
+ tr_values.merge! notify_setting.fetch(:tr_values, {})
109
+
110
+ if super.blank?
111
+ I18n.t tr_key(:body), tr_values
112
+ else
113
+ super
114
+ end
115
+ end
116
+
117
+ def cc_emails
118
+ r = notify_setting.fetch(:cc_emails, []).map do |i|
119
+ next i unless i.respond_to?(:call)
120
+ i.call(notifiable)
121
+ end
122
+ r.flatten.concat super
123
+ end
124
+
125
+ def unread_count
126
+ Rails.cache.read("#{self.receiver_type}_#{self.receiver_id}_unread") || 0
127
+ end
128
+
129
+ def make_as_unread
130
+ if read_at.present?
131
+ self.update(read_at: nil)
132
+ Rails.cache.increment "#{self.receiver_type}_#{self.receiver_id}_unread"
133
+ end
134
+ end
135
+
136
+ def make_as_read
137
+ if read_at.blank?
138
+ update(read_at: Time.now)
139
+ Rails.cache.decrement "#{self.receiver_type}_#{self.receiver_id}_unread"
140
+ end
141
+ end
142
+
143
+ def update_unread_count
144
+ Rails.cache.write "#{self.receiver_type}_#{self.receiver_id}_unread", Notification.where(receiver_id: self.receiver_id, receiver_type: self.receiver_type, read_at: nil).count, raw: true
145
+ end
146
+
147
+ def self.update_unread_count(receiver)
148
+ if Rails.cache.write "#{receiver.class.name}_#{receiver.id}_unread", Notification.where(receiver_id: receiver.id, receiver_type: receiver.class.name, read_at: nil).count, raw: true
149
+ Rails.cache.read "#{receiver.class.name}_#{receiver.id}_unread"
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ # notifiable_type:
156
+ # notifiable_id:
@@ -0,0 +1,8 @@
1
+ class NotificationSetting < ApplicationRecord
2
+ belongs_to :receiver, polymorphic: true
3
+ serialize :notifiable_types, Array
4
+
5
+ def notifiable_types
6
+ super + RailsNotice.config.default_notifiable_types
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ <% if current_receiver %>
2
+ <%= link_to notifications_path, class: 'item', remote: true, id: 'notify_show' do %>
3
+ <i class="bell icon"></i>
4
+ <span id="notice_count" style="padding-left: 5px"><%= current_receiver.unread_count %></span>
5
+ <% end %>
6
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <div class="ui vertical fluid icon menu">
2
+ <div class="header center link item" id="close_side">
3
+ <i class="content icon"></i>
4
+ </div>
5
+ <div class="item ui dropdown">
6
+ <a class="<%= active_helper(controllers: ['notifications', 'notification_settings', 'notify_settings'], active_class: 'title active', item_class: 'title') %>">
7
+ <i class="users icon"></i>
8
+ <span>Notification</span>
9
+ </a>
10
+ <div class="<%= active_helper(controllers: ['notifications', 'notification_settings', 'notify_settings'], active_class: 'menu content active', item_class: 'menu content') %>">
11
+ <%= link_to 'Notification', admin_notifications_path, class: active_helper(controllers: 'notifications') %>
12
+ <%= link_to 'Receiver Settings', admin_notification_settings_path, class: active_helper(controllers: ['notification_settings']) %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+
@@ -0,0 +1,7 @@
1
+ <%= form_with model: [:admin, @notification_setting], local: true do |f| %>
2
+ <%= render 'shared/error_messages', target: @notification_setting %>
3
+ <%= f.number_field :showtime %>
4
+ <%= f.check_box :accept_email %>
5
+ <%= f.collection_check_boxes :notifiable_types, RailsNotice.notifiable_types, :to_s, :to_s %>
6
+ <%= f.submit %>
7
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= search_form_with do |f| %>
2
+ <div class="fields">
3
+ <div class="ui left action input">
4
+ <%= f.select :receiver_type, options_for_select(Notification.options_i18n(:receiver_type)), { on: { wrapper_input: false } }, { class: 'ui selection dropdown', id: 'q_receiver_type' } %>
5
+ <%= f.select :receiver_id, options_for_select([]), { on: { wrapper_input: false } }, { id: 'q_receiver_id', class: 'ui search dropdown' } %>
6
+ </div>
7
+ <%= f.submit 'Search' %>
8
+ </div>
9
+ <% end %>
10
+
11
+
@@ -0,0 +1,14 @@
1
+ <div>
2
+
3
+ <div class="ui segment breadcrumb">
4
+ <%= link_to 'Back', admin_notification_settings_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">Edit</div>
7
+ </div>
8
+
9
+ <div class="ui segment">
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
14
+
@@ -0,0 +1,52 @@
1
+ <div class="ui top attached menu borderless">
2
+ <div class="header item">Notification Settings</div>
3
+ <div class="right menu">
4
+ <div class="item"><%= link_to 'New Notification Setting', new_admin_notification_setting_path, class: 'ui teal button' %></div>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="ui attached segment">
9
+ <%= render 'search_form' %>
10
+ </div>
11
+
12
+ <table class="ui bottom attached table">
13
+ <thead>
14
+ <tr>
15
+ <th><%= NotificationSetting.human_attribute_name(:id) %></th>
16
+ <th><%= NotificationSetting.human_attribute_name(:receiver_type) %></th>
17
+ <th><%= NotificationSetting.human_attribute_name(:receiver_id) %></th>
18
+ <th><%= NotificationSetting.human_attribute_name(:showtime) %></th>
19
+ <th><%= NotificationSetting.human_attribute_name(:accept_email) %></th>
20
+ <th><%= NotificationSetting.human_attribute_name(:notifiable_types) %></th>
21
+ <th></th>
22
+ </tr>
23
+ </thead>
24
+
25
+ <tbody>
26
+ <% @notification_settings.each do |notification_setting| %>
27
+ <tr>
28
+ <td><%= notification_setting.id %></td>
29
+ <td><%= notification_setting.receiver_type %></td>
30
+ <td><%= notification_setting.receiver_id %></td>
31
+ <td><%= notification_setting.showtime %></td>
32
+ <td>
33
+ <%= form_with model: notification_setting, url: admin_notification_setting_path(notification_setting.id), method: :patch, remote: true do |f| %>
34
+ <%= f.check_box :accept_email, onChange: "Rails.fire(this.form, 'submit')", label: false, on: {wrapper_all: false, offset: false} %>
35
+ <% end %>
36
+ </td>
37
+ <td class="ui labels">
38
+ <% notification_setting.notifiable_types.each do |notifiable_type| %>
39
+ <span class="ui label"><%= notifiable_type %></span>
40
+ <% end %>
41
+ </td>
42
+ <td class="ui labels">
43
+ <%= link_to 'Show', admin_notification_setting_path(notification_setting), class: 'ui blue label' %>
44
+ <%= link_to 'Edit', edit_admin_notification_setting_path(notification_setting), class: 'ui pink label' %>
45
+ <%= link_to 'Destroy', admin_notification_setting_path(notification_setting), method: :delete, data: { confirm: 'Are you sure?' }, class: 'ui red label' %>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+
52
+ <%= paginate @notification_settings %>
@@ -0,0 +1,13 @@
1
+ <div>
2
+
3
+ <div class="ui segment breadcrumb">
4
+ <%= link_to 'Back', admin_notification_settings_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">Add</div>
7
+ </div>
8
+
9
+ <div class="ui segment">
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
@@ -0,0 +1,6 @@
1
+ <%= default_form_with model: [:admin, @user, @notification] do |f| %>
2
+ <%= f.text_field :title, label: '标题:' %>
3
+ <%= f.text_area :body, label: '内容:' %>
4
+ <%= f.text_field :link, label: 'Link:' %>
5
+ <%= f.submit %>
6
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <div class="ui top attached borderless menu">
2
+ <div class="header item">Notification List</div>
3
+ <div class="right menu">
4
+ <div class="item"><%= link_to 'New', new_admin_notification_path, class: 'ui teal button' %></div>
5
+ </div>
6
+ </div>
@@ -0,0 +1,11 @@
1
+ <%= search_form_with scope: :q do |f| %>
2
+ <div class="fields">
3
+ <div class="ui left action input">
4
+ <%= f.select :receiver_type, options_for_select(Notification.options_i18n(:receiver_type)), { on: { wrapper_input: false } }, { class: 'ui selection dropdown', id: 'q_receiver_type' } %>
5
+ <%= f.select :receiver_id, options_for_select([]), { on: { wrapper_input: false } }, { id: 'q_receiver_id', class: 'ui search dropdown' } %>
6
+ </div>
7
+ <%= f.text_field :'body-like' %>
8
+ <%= f.submit 'Search' %>
9
+ </div>
10
+ <% end %>
11
+
@@ -0,0 +1,14 @@
1
+ <div class="sixteen wide column">
2
+
3
+ <div class="ui breadcrumb">
4
+ <%= link_to '返回', admin_user_notifications_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">编辑</div>
7
+ </div>
8
+
9
+ <div>
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
14
+
@@ -0,0 +1,44 @@
1
+ <%= render 'menu' %>
2
+
3
+ <div class="ui attached segment">
4
+ <%= render 'search_form' %>
5
+ </div>
6
+
7
+ <table class="ui table">
8
+ <thead>
9
+ <tr>
10
+ <th>ID</th>
11
+ <th>Receiver Type</th>
12
+ <th>Receiver ID</th>
13
+ <th>Title</th>
14
+ <th>Body</th>
15
+ <th>Link</th>
16
+ <th>Read At</th>
17
+ <th>Sent Result</th>
18
+ <th>Operation</th>
19
+ </tr>
20
+ </thead>
21
+
22
+ <tbody>
23
+ <% @notifications.each do |notification| %>
24
+ <tr>
25
+ <td><%= notification.id %></td>
26
+ <td><%= notification.receiver_type %></td>
27
+ <td><%= notification.receiver_id %></td>
28
+ <td><%= notification.title %></td>
29
+ <td><%= notification.body %></td>
30
+ <td><%= notification.link %></td>
31
+ <td><%= notification.read_at %></td>
32
+ <td>
33
+ <code><%= notification.sent_result %></code>
34
+ </td>
35
+ <td class="ui labels">
36
+ <%= link_to 'Push', push_admin_notification_path(notification), method: :patch, remote: true, data: { confirm: 'Are you sure?' }, class: 'ui mini green label' %>
37
+ <%= link_to 'Email', email_admin_notification_path(notification), method: :patch, remote: true, data: { confirm: 'Are you sure?' }, class: 'ui mini green label' %>
38
+ </td>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
43
+
44
+ <%= paginate @notifications %>
@@ -0,0 +1,15 @@
1
+ <div class="three wide column">
2
+ <%= render 'admin/users/nav' %>
3
+ </div>
4
+
5
+ <div class="thirteen wide column">
6
+ <div class="ui breadcrumb">
7
+ <%= link_to 'Back', admin_user_notifications_path(@user), class: 'section' %>
8
+ <div class="divider"> / </div>
9
+ <div class="active section">New</div>
10
+ </div>
11
+
12
+ <div>
13
+ <%= render 'form' %>
14
+ </div>
15
+ </div>
@@ -0,0 +1,35 @@
1
+ <p>Dear <%= @notification.receiver.endearing_name %>,</p>
2
+
3
+ <p><%= @notification.body %></p>
4
+
5
+ <% if @notification.notifiable && @notification.verbose %>
6
+
7
+ <table class="ui very basic table">
8
+ <tbody>
9
+ <% @notification.notifiable_attributes.each do |key, value| %>
10
+ <tr>
11
+ <td class="right aligned">
12
+ <%= @notification.notifiable.class.human_attribute_name(key) %>
13
+ </td>
14
+ <td>
15
+ <% if value.is_a?(Hash) %>
16
+ <%= simple_format_hash(value) %>
17
+ <% elsif value.is_a?(String) && @notification.notifiable.column_for_attribute(key).limit.to_i > 255 %>
18
+ <%= simple_format(value) %>
19
+ <% else %>
20
+ <%= value %>
21
+ <% end %>
22
+ </td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+
28
+ <% end %>
29
+
30
+ <p><%= t('rails_notice.detail') %>: <%= link_to @notification.link, @notification.link %></p>
31
+
32
+ <% if @notification.sender %>
33
+ <p>Best,<br>
34
+ <%= @notification.sender.endearing_name %></p>
35
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <div class="ui menu vertical fluid">
2
+ <%= link_to t('rails_notice.unread'), notifications_path(receiver: params[:receiver]), class: active_params(scope: '', active_class: 'item active', item_class: 'item') %>
3
+ <%= link_to t('rails_notice.readed'), notifications_path(receiver: params[:receiver], scope: 'have_read'), class: active_params(scope: 'have_read', active_class: 'item active', item_class: 'item') %>
4
+ </div>