message_train 0.7.5 → 1.0.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 (171) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/ci.yml +86 -0
  4. data/.gitignore +64 -0
  5. data/.rubocop.yml +9 -0
  6. data/.rubocop_todo.yml +854 -2
  7. data/.ruby-version +1 -1
  8. data/.simplecov +2 -4
  9. data/CHANGELOG.md +128 -0
  10. data/CLAUDE.md +5 -0
  11. data/Gemfile +30 -32
  12. data/LICENSE.txt +1 -1
  13. data/README.md +106 -35
  14. data/Rakefile +4 -18
  15. data/app/assets/stylesheets/message_train.scss +405 -67
  16. data/app/controllers/concerns/message_train_support.rb +2 -2
  17. data/app/controllers/message_train/boxes_controller.rb +4 -2
  18. data/app/controllers/message_train/conversations_controller.rb +1 -0
  19. data/app/helpers/message_train/application_helper.rb +68 -0
  20. data/app/helpers/message_train/attachments_helper.rb +7 -4
  21. data/app/helpers/message_train/conversations_helper.rb +16 -16
  22. data/app/helpers/message_train/messages_helper.rb +23 -10
  23. data/app/javascript/controllers/message_train/bulk_actions_controller.js +36 -0
  24. data/app/javascript/controllers/message_train/lightbox_controller.js +25 -0
  25. data/app/javascript/controllers/message_train/nested_form_controller.js +36 -0
  26. data/app/javascript/controllers/message_train/recipient_autocomplete_controller.js +31 -0
  27. data/app/mailers/message_train/previews/receipt_mailer_preview.rb +7 -5
  28. data/app/models/message_train/attachment.rb +54 -42
  29. data/app/models/message_train/box.rb +2 -2
  30. data/app/models/message_train/conversation.rb +28 -2
  31. data/app/models/message_train/ignore.rb +2 -2
  32. data/app/models/message_train/message.rb +10 -4
  33. data/app/models/message_train/receipt.rb +27 -4
  34. data/app/models/message_train/unsubscribe.rb +1 -1
  35. data/app/views/application/404.html.haml +1 -3
  36. data/app/views/kaminari/_first_page.html.haml +1 -1
  37. data/app/views/kaminari/_last_page.html.haml +1 -1
  38. data/app/views/kaminari/_next_page.html.haml +1 -1
  39. data/app/views/kaminari/_page.html.haml +3 -3
  40. data/app/views/kaminari/_paginator.html.haml +12 -11
  41. data/app/views/kaminari/_prev_page.html.haml +1 -1
  42. data/app/views/message_train/application/_attachment_fields.html.haml +10 -7
  43. data/app/views/message_train/application/_attachment_link.html.haml +2 -2
  44. data/app/views/message_train/application/_widget.html.haml +2 -4
  45. data/app/views/message_train/boxes/_widget.html.haml +5 -5
  46. data/app/views/message_train/boxes/show.html.haml +28 -45
  47. data/app/views/message_train/collectives/_widget.html.haml +4 -4
  48. data/app/views/message_train/conversations/_conversation.html.haml +11 -11
  49. data/app/views/message_train/conversations/_deleted_toggle.html.haml +1 -1
  50. data/app/views/message_train/conversations/_ignored_toggle.html.haml +2 -2
  51. data/app/views/message_train/conversations/_read_toggle.html.haml +2 -2
  52. data/app/views/message_train/conversations/_toggle.html.haml +6 -4
  53. data/app/views/message_train/conversations/_trashed_toggle.html.haml +2 -2
  54. data/app/views/message_train/conversations/show.html.haml +8 -6
  55. data/app/views/message_train/messages/_deleted_toggle.html.haml +1 -1
  56. data/app/views/message_train/messages/_form.html.haml +22 -30
  57. data/app/views/message_train/messages/_message.html.haml +27 -45
  58. data/app/views/message_train/messages/_read_toggle.html.haml +2 -2
  59. data/app/views/message_train/messages/_toggle.html.haml +5 -1
  60. data/app/views/message_train/messages/_trashed_toggle.html.haml +2 -2
  61. data/app/views/message_train/unsubscribes/index.html.haml +12 -15
  62. data/badges/coverage.json +1 -0
  63. data/config/importmap.rb +2 -0
  64. data/config/locales/en.yml +6 -3
  65. data/db/migrate/20150721145319_create_message_train_conversations.rb +1 -1
  66. data/db/migrate/20150721160322_create_message_train_messages.rb +1 -1
  67. data/db/migrate/20150721161144_create_message_train_attachments.rb +5 -2
  68. data/db/migrate/20150721161940_create_message_train_receipts.rb +1 -1
  69. data/db/migrate/20150721163838_create_message_train_ignores.rb +1 -1
  70. data/db/migrate/20150901183458_add_received_through_to_message_train_receipts.rb +1 -1
  71. data/db/migrate/20151004184347_add_unique_index_to_receipts.rb +1 -1
  72. data/db/migrate/20151124000820_create_message_train_unsubscribes.rb +1 -1
  73. data/db/migrate/20260827095938_remove_paperclip_columns_from_message_train_attachments.rb +8 -0
  74. data/db/migrate/20260827171549_remove_body_from_message_train_messages.rb +5 -0
  75. data/db/migrate/20260827180000_add_marking_indexes.rb +9 -0
  76. data/db/migrate/20260827200000_add_body_back_to_message_train_messages.rb +5 -0
  77. data/docs/screenshots/inbox-desktop.png +0 -0
  78. data/docs/screenshots/inbox-mobile.png +0 -0
  79. data/lib/generators/message_train/install/install_generator.rb +1 -1
  80. data/lib/message_train/engine.rb +11 -0
  81. data/lib/message_train/instance_methods.rb +13 -7
  82. data/lib/message_train/localization.rb +4 -3
  83. data/lib/message_train/version.rb +1 -1
  84. data/lib/message_train.rb +0 -2
  85. data/message_train.gemspec +57 -389
  86. data/spec/controllers/message_train/boxes_controller_spec.rb +13 -8
  87. data/spec/controllers/message_train/concerns_spec.rb +2 -2
  88. data/spec/controllers/message_train/conversations_controller_spec.rb +36 -7
  89. data/spec/controllers/message_train/messages_controller_spec.rb +4 -4
  90. data/spec/dummy/app/assets/stylesheets/application.scss +141 -12
  91. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  92. data/spec/dummy/app/javascript/application.js +4 -0
  93. data/spec/dummy/app/javascript/controllers/application.js +9 -0
  94. data/spec/dummy/app/javascript/controllers/index.js +4 -0
  95. data/spec/dummy/app/models/role.rb +1 -1
  96. data/spec/dummy/app/views/active_storage/blobs/_blob.html.erb +14 -0
  97. data/spec/dummy/app/views/layouts/_top_navigation.html.haml +18 -7
  98. data/spec/dummy/app/views/layouts/application.html.haml +22 -26
  99. data/spec/dummy/bin/importmap +4 -0
  100. data/spec/dummy/config/application.rb +7 -20
  101. data/spec/dummy/config/environments/development.rb +17 -21
  102. data/spec/dummy/config/environments/production.rb +11 -63
  103. data/spec/dummy/config/environments/test.rb +21 -16
  104. data/spec/dummy/config/importmap.rb +7 -0
  105. data/spec/dummy/config/initializers/assets.rb +1 -6
  106. data/spec/dummy/config/routes.rb +1 -1
  107. data/spec/dummy/config/storage.yml +7 -0
  108. data/spec/dummy/db/migrate/20150721140013_devise_create_users.rb +1 -1
  109. data/spec/dummy/db/migrate/20150721141009_rolify_create_roles.rb +1 -1
  110. data/spec/dummy/db/migrate/20150721141128_create_groups.rb +1 -1
  111. data/spec/dummy/db/migrate/20150721150307_add_display_name_column_to_users.rb +1 -1
  112. data/spec/dummy/db/migrate/20160207190402_create_message_train_conversations.message_train.rb +1 -1
  113. data/spec/dummy/db/migrate/20160207190403_create_message_train_messages.message_train.rb +1 -1
  114. data/spec/dummy/db/migrate/20160207190404_create_message_train_attachments.message_train.rb +5 -2
  115. data/spec/dummy/db/migrate/20160207190405_create_message_train_receipts.message_train.rb +1 -1
  116. data/spec/dummy/db/migrate/20160207190406_create_message_train_ignores.message_train.rb +1 -1
  117. data/spec/dummy/db/migrate/20160207190407_add_received_through_to_message_train_receipts.message_train.rb +1 -1
  118. data/spec/dummy/db/migrate/20160207190408_add_unique_index_to_receipts.message_train.rb +1 -1
  119. data/spec/dummy/db/migrate/20160207190409_create_message_train_unsubscribes.message_train.rb +1 -1
  120. data/spec/dummy/db/migrate/20260827095939_remove_paperclip_columns_from_message_train_attachments.message_train.rb +9 -0
  121. data/spec/dummy/db/migrate/20260827161141_create_active_storage_tables.active_storage.rb +57 -0
  122. data/spec/dummy/db/migrate/20260827171550_remove_body_from_message_train_messages.message_train.rb +6 -0
  123. data/spec/dummy/db/migrate/20260827225116_add_marking_indexes.message_train.rb +10 -0
  124. data/spec/dummy/db/migrate/20260828034830_add_body_back_to_message_train_messages.message_train.rb +6 -0
  125. data/spec/dummy/db/migrate/20260828040000_drop_action_text_tables.rb +5 -0
  126. data/spec/dummy/db/schema.rb +106 -72
  127. data/spec/dummy/db/seeds/conversations.seeds.rb +22 -22
  128. data/spec/dummy/db/seeds/development/conversations.seeds.rb +2 -2
  129. data/spec/dummy/db/seeds/groups.seeds.rb +4 -4
  130. data/spec/dummy/db/seeds/test/attachments.seeds.rb +4 -4
  131. data/spec/dummy/db/seeds/test/conversations.seeds.rb +2 -2
  132. data/spec/dummy/db/seeds/users.seeds.rb +8 -8
  133. data/spec/dummy/vendor/javascript/.keep +0 -0
  134. data/spec/factories/attachment.rb +5 -6
  135. data/spec/factories/group.rb +2 -2
  136. data/spec/factories/message.rb +7 -7
  137. data/spec/factories/user.rb +3 -3
  138. data/spec/features/boxes_spec.rb +38 -34
  139. data/spec/features/conversations_spec.rb +13 -6
  140. data/spec/features/messages_spec.rb +20 -20
  141. data/spec/features/unsubscribes_spec.rb +9 -9
  142. data/spec/helpers/message_train/application_helper_spec.rb +16 -18
  143. data/spec/helpers/message_train/attachment_helper_spec.rb +8 -18
  144. data/spec/helpers/message_train/boxes_helper_spec.rb +6 -6
  145. data/spec/helpers/message_train/collectives_helper_spec.rb +10 -10
  146. data/spec/helpers/message_train/conversations_helper_spec.rb +43 -85
  147. data/spec/helpers/message_train/messages_helper_spec.rb +41 -61
  148. data/spec/models/message_train/attachment_spec.rb +26 -36
  149. data/spec/models/message_train/box_spec.rb +2 -2
  150. data/spec/models/message_train/conversation_spec.rb +15 -0
  151. data/spec/models/message_train/message_spec.rb +5 -1
  152. data/spec/models/message_train/receipt_spec.rb +1 -1
  153. data/spec/models/message_train/unsubscribe_spec.rb +1 -1
  154. data/spec/rails_helper.rb +14 -13
  155. data/spec/support/controller_behaviors.rb +13 -13
  156. data/spec/support/factory_bot.rb +3 -0
  157. data/spec/support/feature_behaviors.rb +14 -14
  158. data/spec/support/loaded_site/attachments.rb +6 -2
  159. data/spec/support/utilities.rb +49 -20
  160. metadata +260 -189
  161. data/.travis.yml +0 -12
  162. data/VERSION +0 -1
  163. data/app/assets/javascripts/ckeditor/config.js +0 -49
  164. data/app/assets/javascripts/message_train.js +0 -149
  165. data/spec/dummy/app/assets/javascripts/application.js +0 -17
  166. data/spec/dummy/app/assets/stylesheets/bootstrap-everything.scss +0 -54
  167. data/spec/dummy/config/initializers/bootstrap_leather.rb +0 -7
  168. data/spec/dummy/config/initializers/paperclip.rb +0 -5
  169. data/spec/dummy/config/secrets.yml +0 -22
  170. data/spec/dummy/db/test.sqlite3 +0 -0
  171. data/spec/support/factory_girl.rb +0 -3
@@ -71,15 +71,19 @@ describe MessageTrain::BoxesController do
71
71
  )
72
72
  end
73
73
  it_should_behave_like(
74
- 'a page with an error message matching',
75
- /Access to Conversation \d+ denied/
74
+ 'a redirect matching',
75
+ %r{^http://test\.host/messages/box/in}
76
76
  )
77
+ context 'sets error' do
78
+ subject { flash[:error] }
79
+ it { is_expected.to match(/Access to Conversation \d+ denied/) }
80
+ end
77
81
  end
78
82
  describe 'without params' do
79
83
  before do
80
84
  put :update, params: { division: 'in' }
81
85
  end
82
- it_should_behave_like 'a page with an alert message', 'Nothing to do'
86
+ it_should_behave_like 'a redirect with alert', '/messages/box/in', 'Nothing to do'
83
87
  end
84
88
  describe 'with valid params' do
85
89
  before do
@@ -92,8 +96,7 @@ describe MessageTrain::BoxesController do
92
96
  }
93
97
  )
94
98
  end
95
- it_should_behave_like 'a page with a notice message', 'Update successful'
96
- it_should_behave_like 'a response without error'
99
+ it_should_behave_like 'a redirect with notice', '/messages/box/in', 'Update successful'
97
100
  end
98
101
  end
99
102
 
@@ -115,7 +118,7 @@ describe MessageTrain::BoxesController do
115
118
  before do
116
119
  delete :destroy, params: { division: 'in' }
117
120
  end
118
- it_should_behave_like 'a page with an alert message', 'Nothing to do'
121
+ it_should_behave_like 'a redirect with alert', '/messages/box/in', 'Nothing to do'
119
122
  end
120
123
  describe 'with valid params' do
121
124
  context 'ignoring' do
@@ -130,7 +133,8 @@ describe MessageTrain::BoxesController do
130
133
  )
131
134
  end
132
135
  it_should_behave_like(
133
- 'a page with a notice message',
136
+ 'a redirect with notice',
137
+ '/messages/box/in',
134
138
  'Update successful'
135
139
  )
136
140
  end
@@ -146,7 +150,8 @@ describe MessageTrain::BoxesController do
146
150
  )
147
151
  end
148
152
  it_should_behave_like(
149
- 'a page with a notice message',
153
+ 'a redirect with notice',
154
+ '/messages/box/in',
150
155
  'Update successful'
151
156
  )
152
157
  end
@@ -37,7 +37,7 @@ describe MessageTrain::BoxesController do
37
37
  end
38
38
  it_should_behave_like(
39
39
  'a redirect with error',
40
- '/collectives/groups:first-group/box/sent',
40
+ '/messages/collectives/groups:first-group/box/sent',
41
41
  'Access to that box denied'
42
42
  )
43
43
  end
@@ -55,7 +55,7 @@ describe MessageTrain::BoxesController do
55
55
  end
56
56
  it_should_behave_like(
57
57
  'a redirect with error',
58
- '/collectives/groups:membered-group/box/in',
58
+ '/messages/collectives/groups:membered-group/box/in',
59
59
  'Access to that box denied'
60
60
  )
61
61
  end
@@ -57,7 +57,14 @@ describe MessageTrain::ConversationsController do
57
57
  before do
58
58
  put :update, params: { box_division: 'in', id: unread_conversation.id }
59
59
  end
60
- it_should_behave_like 'a page with an alert message', 'Nothing to do'
60
+ it_should_behave_like(
61
+ 'a redirect matching',
62
+ %r{^http://test\.host/messages/box/in/conversations/\d+$}
63
+ )
64
+ context 'sets alert' do
65
+ subject { flash[:alert] }
66
+ it { is_expected.to eq 'Nothing to do' }
67
+ end
61
68
  end
62
69
  describe 'with valid params' do
63
70
  before do
@@ -71,7 +78,14 @@ describe MessageTrain::ConversationsController do
71
78
  }
72
79
  )
73
80
  end
74
- it_should_behave_like 'a page with a notice message', 'Update successful'
81
+ it_should_behave_like(
82
+ 'a redirect matching',
83
+ %r{^http://test\.host/messages/box/in/conversations/\d+$}
84
+ )
85
+ context 'sets notice' do
86
+ subject { flash[:notice] }
87
+ it { is_expected.to eq 'Update successful' }
88
+ end
75
89
  end
76
90
  end
77
91
 
@@ -101,7 +115,14 @@ describe MessageTrain::ConversationsController do
101
115
  }
102
116
  )
103
117
  end
104
- it_should_behave_like 'a page with an alert message', 'Nothing to do'
118
+ it_should_behave_like(
119
+ 'a redirect matching',
120
+ %r{^http://test\.host/messages/box/in/conversations/\d+$}
121
+ )
122
+ context 'sets alert' do
123
+ subject { flash[:alert] }
124
+ it { is_expected.to eq 'Nothing to do' }
125
+ end
105
126
  end
106
127
  describe 'with valid params' do
107
128
  context 'ignoring' do
@@ -116,9 +137,13 @@ describe MessageTrain::ConversationsController do
116
137
  )
117
138
  end
118
139
  it_should_behave_like(
119
- 'a page with a notice message',
120
- 'Update successful'
140
+ 'a redirect matching',
141
+ %r{^http://test\.host/messages/box/in/conversations/\d+$}
121
142
  )
143
+ context 'sets notice' do
144
+ subject { flash[:notice] }
145
+ it { is_expected.to eq 'Update successful' }
146
+ end
122
147
  end
123
148
  context 'unignoring' do
124
149
  before do
@@ -132,9 +157,13 @@ describe MessageTrain::ConversationsController do
132
157
  )
133
158
  end
134
159
  it_should_behave_like(
135
- 'a page with a notice message',
136
- 'Update successful'
160
+ 'a redirect matching',
161
+ %r{^http://test\.host/messages/box/in/conversations/\d+$}
137
162
  )
163
+ context 'sets notice' do
164
+ subject { flash[:notice] }
165
+ it { is_expected.to eq 'Update successful' }
166
+ end
138
167
  end
139
168
  end
140
169
  end
@@ -132,7 +132,7 @@ describe MessageTrain::MessagesController do
132
132
  }
133
133
  )
134
134
  end
135
- it_should_behave_like 'a redirect to', '/box/drafts'
135
+ it_should_behave_like 'a redirect to', '/messages/box/drafts'
136
136
  it_should_behave_like 'a response without error'
137
137
 
138
138
  context "sets the flash with an alert of the messages's draft status" do
@@ -150,7 +150,7 @@ describe MessageTrain::MessagesController do
150
150
  }
151
151
  )
152
152
  end
153
- it_should_behave_like 'a redirect to', '/box/sent'
153
+ it_should_behave_like 'a redirect to', '/messages/box/sent'
154
154
  it_should_behave_like 'a response without error'
155
155
 
156
156
  context "sets the flash with a notice of the messages's creation" do
@@ -307,7 +307,7 @@ describe MessageTrain::MessagesController do
307
307
  }
308
308
  )
309
309
  end
310
- it_should_behave_like 'a redirect to', '/box/sent'
310
+ it_should_behave_like 'a redirect to', '/messages/box/sent'
311
311
  it_should_behave_like 'a response without error'
312
312
 
313
313
  context 'updates @message' do
@@ -345,7 +345,7 @@ describe MessageTrain::MessagesController do
345
345
  end
346
346
  it_should_behave_like(
347
347
  'a redirect matching',
348
- %r{/box/in/conversations/\d+}
348
+ %r{/messages/box/in/conversations/\d+}
349
349
  )
350
350
  it_should_behave_like 'a response without error'
351
351
 
@@ -1,20 +1,149 @@
1
- // 'bootstrap-sprockets' must be imported before 'bootstrap' and 'bootstrap/variables'
2
- @import 'bootstrap-sprockets';
3
- @import 'bootstrap-everything';
4
- @import 'bootstrap/theme';
5
1
  @import 'message_train';
6
2
 
7
- .navbar-header {
8
- overflow: hidden;
3
+ // Without this, the browser's default body margin (8px) keeps .nav-items'
4
+ // full-bleed background from actually reaching the viewport edge.
5
+ body {
6
+ margin: 0;
9
7
  }
10
8
 
11
- .navbar-brand {
12
- display: block;
13
- width: 300px;
9
+ .top-navigation {
10
+ a {
11
+ text-decoration: none;
12
+ }
13
+ }
14
+
15
+ // Logo row: plain background, left-aligned (not width-capped/centered
16
+ // like the nav-links bar below it).
17
+ .top-navigation-inner {
18
+ display: flex;
19
+ align-items: center;
20
+ padding: var(--message-train-space-md);
21
+ }
22
+
23
+ // The nav-links bar sits below the logo row and is full-bleed (edge to
24
+ // edge) rather than width-capped like the rest of the page, so the blue
25
+ // reads as a distinct band across the whole viewport. Its own content
26
+ // (.nav-items-inner) is still width-capped/centered to line up with the
27
+ // logo row and page content above/below it.
28
+ .nav-items {
29
+ background: var(--message-train-color-accent);
30
+ color: var(--message-train-color-accent-contrast);
31
+
32
+ // Every link in here - top-level (Groups/Inbox dropdown triggers,
33
+ // Manage Notifications, Sign Out) and nested (the dropdown popover's
34
+ // own box/collective links) - should read as white against the blue
35
+ // bar, not just the top level.
36
+ a {
37
+ color: inherit;
38
+ }
39
+
40
+ // The dropdown popover (.message-train-dropdown-nav-item ul, in
41
+ // message_train.scss) normally gets its own light "elevated surface"
42
+ // background so it stands out as a floating menu over page content -
43
+ // here it sits over the blue bar instead, so it keeps the same blue
44
+ // (rather than falling back to that light surface) to stay readable
45
+ // with the white text above.
46
+ .message-train-dropdown-nav-item ul {
47
+ background: var(--message-train-color-accent);
48
+ border-color: rgba(255, 255, 255, 0.3);
49
+ }
50
+ }
51
+
52
+ .nav-items-inner {
53
+ box-sizing: border-box;
54
+ max-width: 75rem;
55
+ margin: 0 auto;
56
+ padding: var(--message-train-space-sm) var(--message-train-space-md);
57
+
58
+ // One list: each collective/box <details> dropdown and each plain
59
+ // account link (Manage Notifications, Sign Out) is its own <li>, so
60
+ // this is valid list markup throughout rather than a details element
61
+ // sitting loose alongside a <ul>. Mobile-first: stacked vertically by
62
+ // default (gap alone separates items, no divider lines), one row only
63
+ // once there's room (same 48rem breakpoint the dropdowns themselves
64
+ // switch at) - subtle left borders between adjacent items only make
65
+ // sense once they're actually side by side.
66
+ //
67
+ // Direct child only (> li) - a dropdown's own popover <ul>/<li> (in
68
+ // message_train.scss) is nested two levels deeper (li > details > ul >
69
+ // li), so it's naturally excluded here without extra scoping.
70
+ > ul {
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: flex-start;
74
+ gap: var(--message-train-space-md);
75
+ list-style: none;
76
+ margin: 0;
77
+ padding: 0;
78
+
79
+ @media (min-width: 48rem) {
80
+ flex-direction: row;
81
+ align-items: center;
82
+ flex-wrap: wrap;
83
+ // The list is usually narrower than the 75rem-capped bar, so
84
+ // without this it hugs the left edge of that (invisible) cap
85
+ // instead of actually centering in the bar.
86
+ justify-content: center;
87
+
88
+ > li + li {
89
+ border-top: none;
90
+ border-left: 1px solid rgba(255, 255, 255, 0.3);
91
+ padding-top: 0;
92
+ padding-left: var(--message-train-space-md);
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ // Heading hierarchy: h1 (site-brand, in the persistent top nav) is the
99
+ // largest/heaviest per TYPOGRAPHY.md's Hierarchy rule, h2 (page title)
100
+ // and h3 (subtitle) step down from there. Left unsized, the browser's UA
101
+ // default made h2 render bigger than h1 - exactly backwards.
102
+ h1.site-brand {
103
+ margin: 0;
104
+ font-size: var(--message-train-fs-2xl);
105
+ font-weight: 700;
106
+
107
+ a {
108
+ display: inline-flex;
109
+ align-items: center;
110
+ gap: var(--message-train-space-sm);
111
+ text-decoration: none;
112
+ color: inherit;
113
+ }
114
+ }
115
+
116
+ main.content {
117
+ padding: var(--message-train-space-md);
118
+
119
+ > h2 {
120
+ margin: 0 0 var(--message-train-space-xs);
121
+ font-size: var(--message-train-fs-xl);
122
+ font-weight: 600;
123
+ }
124
+
125
+ > h3 {
126
+ margin: 0 0 var(--message-train-space-md);
127
+ font-size: var(--message-train-fs-base);
128
+ font-weight: 400;
129
+ }
130
+ }
131
+
132
+ // Mobile-first (RESPONSIVE.md): single column with the sidebar widget
133
+ // stacking below the main content by default; a real side-by-side
134
+ // sidebar only once there's room for both without cramming either.
135
+ .page-grid {
136
+ display: grid;
137
+ grid-template-columns: 1fr;
138
+ gap: var(--message-train-space-xl);
139
+
140
+ @media (min-width: 48rem) {
141
+ grid-template-columns: 1fr 18rem;
142
+ align-items: start;
143
+ }
14
144
  }
15
145
 
16
146
  .logo {
17
- float: left;
18
- height: 2em;
19
- margin: -.5em 1em -.5em 0;
147
+ height: 1.5em;
148
+ width: auto;
20
149
  }
@@ -1,5 +1,5 @@
1
1
  class ApplicationController < ActionController::Base
2
- include MessageTrain::MessageTrainSupport
2
+ include MessageTrainSupport
3
3
 
4
4
  # Prevent CSRF attacks by raising an exception.
5
5
  # For APIs, you may want to use :null_session instead.
@@ -0,0 +1,4 @@
1
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2
+ import "@hotwired/turbo-rails"
3
+
4
+ import "controllers"
@@ -0,0 +1,9 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+
3
+ const application = Application.start()
4
+
5
+ // Configure Stimulus development experience
6
+ application.debug = false
7
+ window.Stimulus = application
8
+
9
+ export { application }
@@ -0,0 +1,4 @@
1
+ // Import and register all your controllers from the importmap via controllers/**/*_controller
2
+ import { application } from "controllers/application"
3
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
4
+ eagerLoadControllersFrom("controllers", application)
@@ -1,6 +1,6 @@
1
1
  class Role < ActiveRecord::Base
2
2
  has_and_belongs_to_many :users, join_table: :users_roles
3
- belongs_to :resource, polymorphic: true
3
+ belongs_to :resource, polymorphic: true, optional: true
4
4
 
5
5
  validates(
6
6
  :resource_type,
@@ -0,0 +1,14 @@
1
+ <figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
2
+ <% if blob.representable? %>
3
+ <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
4
+ <% end %>
5
+
6
+ <figcaption class="attachment__caption">
7
+ <% if caption = blob.try(:caption) %>
8
+ <%= caption %>
9
+ <% else %>
10
+ <span class="attachment__name"><%= blob.filename %></span>
11
+ <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
12
+ <% end %>
13
+ </figcaption>
14
+ </figure>
@@ -1,7 +1,18 @@
1
- = navbar container_mode: :inside, class: 'navbar-static-top' do
2
- = nav_list class: 'navbar-nav navbar-right' do
3
- - if user_signed_in?
4
- = collective_boxes_dropdown_list(current_user)
5
- = boxes_dropdown_list(current_user)
6
- = nav_item 'Manage Notifications', '/unsubscribes'
7
- = nav_item 'Sign Out', '/users/sign_out', method: :delete
1
+ %nav.top-navigation
2
+ .top-navigation-inner
3
+ %h1.site-brand
4
+ %a{ href: '/' }
5
+ %img.logo{ src: image_path('logo.svg'), alt: '' }
6
+ = MessageTrain.configuration.site_name
7
+ - if user_signed_in?
8
+ .nav-items
9
+ .nav-items-inner
10
+ %ul
11
+ - collective_boxes_html = collective_boxes_dropdown_list(current_user)
12
+ - if collective_boxes_html.present?
13
+ %li= collective_boxes_html
14
+ - boxes_html = boxes_dropdown_list(current_user)
15
+ - if boxes_html.present?
16
+ %li= boxes_html
17
+ %li= link_to 'Manage Notifications', message_train.unsubscribes_path
18
+ %li= link_to 'Sign Out', '/users/sign_out', method: :delete
@@ -1,29 +1,25 @@
1
- %head
2
- = render_title # For the title tag, including SEO content
3
- = render_keywords # Defaults to what you put in the config file
4
- = render_description # Can be hooked up within a mountable rails engine, too.
5
- = responsive_meta_tag # If you're using the responsive features, you need this in your head
1
+ !!!
2
+ %html
3
+ %head
4
+ %title= content_for?(:title) ? content_for(:title) : MessageTrain.configuration.site_name
5
+ %meta{name: 'viewport', content: 'width=device-width, initial-scale=1'}
6
+ = stylesheet_link_tag 'application'
7
+ = javascript_importmap_tags
8
+ = csrf_meta_tags
6
9
 
7
- = stylesheet_link_tag 'application'
10
+ %body
11
+ = render 'layouts/top_navigation'
8
12
 
9
- = render_head_css
10
- = javascript_include_tag 'application'
11
- = csrf_meta_tags
13
+ %main.content
14
+ - if content_for?(:title)
15
+ %h2= content_for(:title)
16
+ - if content_for?(:subtitle)
17
+ %h3= content_for(:subtitle)
18
+ #alert_area
19
+ - flash.each do |type, message|
20
+ .alert{class: flash_alert_class(type)}= message
12
21
 
13
- %body
14
- = render 'layouts/top_navigation'
15
-
16
- .container
17
- = render_hero_unit
18
- .row
19
- - if user_signed_in?
20
- = message_train_widget
21
- = render_widgets 'md', 3
22
- .col-md-9.content
23
- = render_h1 # For the h1 tag containing the title alone
24
- #alert_area
25
- = alert_flash_messages
26
- = yield
27
-
28
- %footer
29
- = render_footer_javascript
22
+ .page-grid
23
+ .page-main= yield
24
+ - if user_signed_in?
25
+ %aside.page-sidebar= message_train_widget
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../config/application"
4
+ require "importmap/commands"
@@ -1,11 +1,13 @@
1
- require File.expand_path('../boot', __FILE__)
1
+ require_relative 'boot'
2
2
 
3
+ require 'rails'
3
4
  # Pick the frameworks you want:
4
5
  require 'active_record/railtie'
6
+ require 'active_storage/engine'
5
7
  require 'action_controller/railtie'
6
8
  require 'action_mailer/railtie'
9
+ require 'action_mailbox/engine'
7
10
  require 'action_view/railtie'
8
- require 'sprockets/railtie'
9
11
  # require 'rails/test_unit/railtie'
10
12
 
11
13
  Bundler.require(*Rails.groups)
@@ -13,22 +15,7 @@ require 'message_train'
13
15
 
14
16
  module Dummy
15
17
  class Application < Rails::Application
16
- # Settings in config/environments/* take precedence over those specified
17
- # here. Application configuration should go into files in
18
- # config/initializers -- all .rb files in that directory are automatically
19
- # loaded.
20
-
21
- # Set Time.zone default to the specified zone and make Active Record
22
- # auto-convert to this zone. Run 'rake -D time' for a list of tasks for
23
- # finding time zone names. Default is UTC.
24
- # config.time_zone = 'Central Time (US & Canada)'
25
-
26
- # The default locale is :en and all translations from
27
- # config/locales/*.rb,yml are auto loaded.
28
- # config.i18n.load_path += Dir[
29
- # Rails.root.join('my', 'locales', '*.{rb,yml}').to_s
30
- # ]
31
- # config.i18n.default_locale = :de
18
+ config.load_defaults 8.1
32
19
 
33
20
  config.generators do |g|
34
21
  g.orm :active_record
@@ -40,10 +27,10 @@ module Dummy
40
27
 
41
28
  config.time_zone = 'America/Denver'
42
29
  config.active_record.default_timezone = :local
30
+ config.active_storage.variant_processor = :mini_magick
43
31
  end
44
32
  end
45
33
 
46
- FactoryGirl.definition_file_paths << MessageTrain::Engine.root.join(
34
+ FactoryBot.definition_file_paths << MessageTrain::Engine.root.join(
47
35
  'spec/factories'
48
36
  )
49
- FactoryGirl.reload
@@ -2,18 +2,24 @@ Rails.application.configure do
2
2
  # Settings specified here will take precedence over those in
3
3
  # config/application.rb.
4
4
 
5
- # In the development environment your application's code is reloaded on
6
- # every request. This slows down response time but is perfect for development
7
- # since you don't have to restart the web server when you make code changes.
8
- config.cache_classes = false
5
+ # Make code changes take effect immediately without server restart.
6
+ config.enable_reloading = true
9
7
 
10
8
  # Do not eager load code on boot.
11
9
  config.eager_load = false
12
10
 
13
- # Show full error reports and disable caching.
14
- config.consider_all_requests_local = true
11
+ # Show full error reports.
12
+ config.consider_all_requests_local = true
13
+
14
+ # Enable/disable Action Controller caching. By default Action Controller caching is disabled.
15
15
  config.action_controller.perform_caching = false
16
16
 
17
+ # Change to :null_store to avoid any caching.
18
+ config.cache_store = :memory_store
19
+
20
+ # Store uploaded files on the local file system (see config/storage.yml for options).
21
+ config.active_storage.service = :local
22
+
17
23
  # Don't care if the mailer can't send.
18
24
  config.action_mailer.raise_delivery_errors = false
19
25
  config.action_mailer.default_url_options = { host: 'localhost:3000' }
@@ -24,20 +30,10 @@ Rails.application.configure do
24
30
  # Raise an error on page load if there are pending migrations.
25
31
  config.active_record.migration_error = :page_load
26
32
 
27
- # Debug mode disables concatenation and preprocessing of assets.
28
- # This option may cause significant delays in view rendering with a large
29
- # number of complex assets.
30
- config.assets.debug = true
31
-
32
- # Asset digests allow you to set far-future HTTP expiration dates on all
33
- # assets, yet still be able to expire them through the digest params.
34
- config.assets.digest = true
35
-
36
- # Adds additional error checking when serving assets at runtime.
37
- # Checks for improperly declared sprockets dependencies.
38
- # Raises helpful error messages.
39
- config.assets.raise_runtime_errors = true
40
-
41
33
  # Raises error for missing translations
42
- # config.action_view.raise_on_missing_translations = true
34
+ # config.i18n.raise_on_missing_translations = true
35
+
36
+ config.secret_key_base = 'ac4907450d833ae9cdd320e25bdeedbf3ecb2617dd15e6' \
37
+ '28bd9704882a26eb298f6cc0d5a11543d287de47f39c60' \
38
+ 'c47e81290600189fe72205f34e84143260c9'
43
39
  end