thredded 0.16.13 → 0.16.14

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -9
  3. data/app/assets/config/thredded_manifest.js +4 -0
  4. data/app/assets/javascripts/thredded/components/currently_online.es6 +1 -0
  5. data/app/assets/javascripts/thredded/components/post_form.es6 +1 -0
  6. data/app/assets/javascripts/thredded/components/preview_area.es6 +2 -0
  7. data/app/assets/javascripts/thredded/components/quote_post.es6 +1 -0
  8. data/app/assets/javascripts/thredded/components/spoilers.es6 +1 -0
  9. data/app/assets/javascripts/thredded/components/submit_hotkey.es6 +1 -0
  10. data/app/assets/javascripts/thredded/components/time_stamps.es6 +1 -0
  11. data/app/assets/javascripts/thredded/components/topic_form.es6 +1 -0
  12. data/app/assets/javascripts/thredded/components/topics.es6 +1 -0
  13. data/app/assets/javascripts/thredded/components/turboforms.es6 +8 -7
  14. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +1 -0
  15. data/app/assets/javascripts/thredded/components/user_textcomplete.es6 +1 -0
  16. data/app/assets/javascripts/thredded/components/users_select.es6 +1 -0
  17. data/app/assets/javascripts/thredded/dependencies/autosize.js +1 -1
  18. data/app/controllers/thredded/messageboard_groups_controller.rb +8 -0
  19. data/app/models/thredded/messageboard.rb +1 -0
  20. data/app/view_hooks/thredded/all_view_hooks.rb +21 -0
  21. data/app/view_models/thredded/messageboard_group_view.rb +1 -1
  22. data/app/views/thredded/messageboard_groups/show.html.erb +41 -0
  23. data/app/views/thredded/messageboards/index.html.erb +6 -1
  24. data/config/locales/de.yml +2 -0
  25. data/config/locales/en.yml +2 -0
  26. data/config/locales/es.yml +2 -0
  27. data/config/locales/fr.yml +2 -0
  28. data/config/locales/it.yml +2 -0
  29. data/config/locales/pl.yml +2 -0
  30. data/config/locales/pt-BR.yml +2 -0
  31. data/config/locales/ru.yml +2 -0
  32. data/config/locales/zh-CN.yml +2 -0
  33. data/config/routes.rb +1 -0
  34. data/lib/generators/thredded/install/templates/initializer.rb +3 -0
  35. data/lib/thredded.rb +6 -0
  36. data/lib/thredded/engine.rb +1 -3
  37. data/lib/thredded/version.rb +1 -1
  38. data/lib/thredded/webpack_assets.rb +110 -0
  39. data/vendor/assets/javascripts/autosize.js +280 -0
  40. metadata +9 -6
  41. data/vendor/assets/javascripts/autosize.min.js +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34e83f36d097c95e1f03964b88a49e915ec18e82981667803a68668033adda9d
4
- data.tar.gz: 19e36508e79ecf4a9dfa196e022a4c31b96607828843704ad236d55311067d03
3
+ metadata.gz: b96a82c0482d773cd074fdd77a4e67a408ba43e2e079bbbd2a4eefeb55085f37
4
+ data.tar.gz: 6949b12a03a39430340dbd8870cb6e5cc4dcddc583e093de576e2a3621becdb4
5
5
  SHA512:
6
- metadata.gz: 2859851223d3889f78331303bdeb8eeb78b3eea60316b2dc40249a8e940fd9a79fa34219f264ebc4afe5b8bc947ab40fda25902cfb71e877f06a510bddda6e88
7
- data.tar.gz: a67c187aa11cf3f4a900a2a7aa86da6245b73c221f367790dc111793fbafe531b56f082d105d9e03f07bae4f5818a99da00b69c6aa79889e4526602ee8dc9073
6
+ metadata.gz: 4403418748bbb9b7bdcf5a503908fb526d60ff0f6100d11be98d64f625da8e9479c2be127adb2b88435686371d58cb75c39d1e4c73789e5e8297eabf9d01198d
7
+ data.tar.gz: 154ddbcd92f7e8f705bf4fe27bbc32f03fff0e3af7fdb81aca82143783dfd389eed1cbd516802356288af0c839cd3ddc61087209037c60bc521bee072939efa6
data/README.md CHANGED
@@ -43,7 +43,8 @@ Table of Contents
43
43
  * [Application layout](#application-layout)
44
44
  * [Reference your paths so that Thredded can find them](#reference-your-paths-so-that-thredded-can-find-them)
45
45
  * [Add Thredded styles](#add-thredded-styles)
46
- * [Add Thredded JavaScripts](#add-thredded-javascripts)
46
+ * [Add Thredded JavaScripts (Sprockets)](#add-thredded-javascripts-sprockets)
47
+ * [Add Thredded JavaScripts (Webpack)](#add-thredded-javascripts-webpack)
47
48
  * [User profile page](#user-profile-page)
48
49
  * [Customizing views](#customizing-views)
49
50
  * [View hooks](#view-hooks)
@@ -95,7 +96,7 @@ Then, see the rest of this Readme for more information about using and customizi
95
96
  Add the gem to your Gemfile:
96
97
 
97
98
  ```ruby
98
- gem 'thredded', '~> 0.16.13'
99
+ gem 'thredded', '~> 0.16.14'
99
100
  ```
100
101
 
101
102
  Add the Thredded [initializer] to your parent app by running the install generator.
@@ -210,7 +211,7 @@ from the Thredded one by adding this Sass snippet after `@import "thredded";`:
210
211
 
211
212
  See [below](#styles) for customizing the styles via Sass variables.
212
213
 
213
- #### Add Thredded JavaScripts
214
+ #### Add Thredded JavaScripts (Sprockets)
214
215
 
215
216
  Include thredded JavaScripts in your `application.js`:
216
217
 
@@ -220,6 +221,28 @@ Include thredded JavaScripts in your `application.js`:
220
221
 
221
222
  Thredded is fully compatible with deferred and async script loading.
222
223
 
224
+ #### Add Thredded JavaScripts (Webpack)
225
+
226
+ You can also include Thredded JavaScript into your webpack pack.
227
+
228
+ First, run `bundle exec rails webpacker:install:erb`.
229
+
230
+ Then, add an `app/javascript/thredded_imports.js.erb` file with the following contents:
231
+
232
+ ```erb
233
+ <%= Thredded::WebpackAssets.javascripts %>
234
+ ```
235
+
236
+ Finally, add the following to your `app/javascript/packs/application.js`:
237
+
238
+ ```js
239
+ require('thredded_imports.js');
240
+ ```
241
+
242
+ Note that you must use `require` (not `import`) because Thredded JavaScript must be run after UJS/Turbolink `start()`
243
+ has been called. This is because Webpack places `import` calls before the code in the same file (unlike `require`,
244
+ which are placed in the same order as in the source).
245
+
223
246
  ##### Alternative JavaScript dependencies
224
247
 
225
248
  <details><summary><b>Rails UJS version</b></summary>
@@ -381,15 +404,27 @@ Here are the steps to ensure the best support for your language if it isn't Engl
381
404
 
382
405
  1. Add `rails-i18n` and `kaminari-i18n` to your Gemfile.
383
406
 
384
- 2. Require the translations for timeago.js in your JavaScript. E.g. for Brazilian Portuguese:
407
+ 2. Require the translations for timeago.js in your JavaScript. E.g. if you want to add German and Brazilian Portuguese:
408
+
409
+ Sprockets:
385
410
 
386
411
  ```js
387
412
  //= require thredded/dependencies/timeago
413
+ //= require timeago/locales/de
388
414
  //= require timeago/locales/pt_BR
389
415
  //= require thredded
390
- ```
391
-
392
- Note that it is important that timeago and its locales are required *before* `//= require thredded`.
416
+ ```
417
+
418
+ Webpack:
419
+
420
+ ```erb
421
+ <% timeago_root = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts') %>
422
+ import "<%= File.join(timeago_root, 'timeago.js') %>";
423
+ <%= %w[de pt_BR].map { |locale| %(import "#{File.join(timeago_root, "timeago/locales/#{locale}.js")}";) } * "\n" %>
424
+ <%= Thredded::WebpackAssets.javascripts %>
425
+ ```
426
+
427
+ Note that it is important that timeago and its locales are required *before* Thredded.
393
428
 
394
429
  3. To generate URL slugs for messageboards, categories, and topics with support for more language than English,
395
430
  you can use a gem like [babosa](https://github.com/norman/babosa).
@@ -601,13 +636,26 @@ First, to get started, migrate and seed the database (SQLite by default):
601
636
  ```bash
602
637
  bundle
603
638
  # Create, migrate, and seed the development database with fake forum users, topics, and posts:
604
- rake db:create db:migrate db:seed
639
+ bin/rails db:create db:migrate db:seed
640
+ ```
641
+
642
+ Install NPM dependencies for the dummy app:
643
+
644
+ ```bash
645
+ cd spec/dummy && yarn && cd -
605
646
  ```
606
647
 
607
648
  Then, start the dummy app server:
608
649
 
609
650
  ```bash
610
- rake dev:server
651
+ bin/rails s
652
+ ```
653
+
654
+ By default, the dummy app server uses Webpack for JavaScript.
655
+ To use Sprockets instead, run:
656
+
657
+ ```bash
658
+ THREDDED_TESTAPP_WEBPACK=1 bin/rails s
611
659
  ```
612
660
 
613
661
  ### Testing
@@ -0,0 +1,4 @@
1
+ //= link thredded.js
2
+ //= link thredded.css
3
+ //= link_tree ../images/ .png
4
+ //= link_tree ../images/ .svg
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
 
3
4
  (() => {
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/dependencies/autosize
2
3
  //= require thredded/core/on_page_load
3
4
  //= require thredded/components/mention_autocompletion
@@ -1,3 +1,5 @@
1
+ //= require thredded/core/thredded
2
+ //= require thredded/core/debounce
1
3
  //= require thredded/core/serialize_form
2
4
  //= require thredded/components/spoilers
3
5
 
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
 
3
4
  (function() {
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
 
3
4
  (() => {
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  (function() {
2
3
  const Thredded = window.Thredded;
3
4
  Thredded.isSubmitHotkey = (evt) => {
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  (() => {
2
3
  const COMPONENT_SELECTOR = '#thredded--container [data-time-ago]';
3
4
  const Thredded = window.Thredded;
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/dependencies/autosize
2
3
  //= require thredded/core/on_page_load
3
4
  //= require thredded/components/mention_autocompletion
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
  //= require thredded/core/serialize_form
3
4
 
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
  //= require thredded/core/serialize_form
3
4
 
@@ -6,13 +7,6 @@
6
7
  const Thredded = window.Thredded;
7
8
  const Turbolinks = window.Turbolinks;
8
9
 
9
- Thredded.onPageLoad(() => {
10
- if (!Turbolinks || !Turbolinks.supported) return;
11
- Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-turboform]'), (form) => {
12
- form.addEventListener('submit', handleSubmit);
13
- });
14
- });
15
-
16
10
  const handleSubmit = (evt) => {
17
11
  evt.preventDefault();
18
12
  const form = evt.currentTarget;
@@ -22,4 +16,11 @@
22
16
  // Turbolinks. Hide it:
23
17
  Thredded.hideSoftKeyboard();
24
18
  };
19
+
20
+ Thredded.onPageLoad(() => {
21
+ if (!Turbolinks || !Turbolinks.supported) return;
22
+ Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-turboform]'), (form) => {
23
+ form.addEventListener('submit', handleSubmit);
24
+ });
25
+ });
25
26
  })();
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
 
3
4
  // Reflects the logic of user preference settings by enabling/disabling certain inputs.
@@ -1,5 +1,6 @@
1
1
  //= require thredded/core/thredded
2
2
  //= require thredded/core/escape_html
3
+ //= require thredded/dependencies/textcomplete
3
4
 
4
5
  (() => {
5
6
  const Thredded = window.Thredded;
@@ -1,3 +1,4 @@
1
+ //= require thredded/core/thredded
1
2
  //= require thredded/core/on_page_load
2
3
  //= require thredded/components/user_textcomplete
3
4
  //= require thredded/dependencies/autosize
@@ -1 +1 @@
1
- //= require autosize.min
1
+ //= require autosize
@@ -20,6 +20,14 @@ module Thredded
20
20
  end
21
21
  end
22
22
 
23
+ def show
24
+ @group = Thredded::MessageboardGroup.where(id: params[:id])
25
+ @groups = Thredded::MessageboardGroupView.grouped(
26
+ policy_scope(Thredded::Messageboard.where(group: params[:id])),
27
+ user: thredded_current_user
28
+ )
29
+ end
30
+
23
31
  private
24
32
 
25
33
  def messageboard_group_params
@@ -12,6 +12,7 @@ module Thredded
12
12
  admin
13
13
  autocomplete-users
14
14
  messageboards
15
+ messageboard-groups
15
16
  posts
16
17
  preferences
17
18
  private-posts
@@ -10,6 +10,8 @@ module Thredded
10
10
  attr_reader :post_form
11
11
  # @return [MessageboardsIndex]
12
12
  attr_reader :messageboards_index
13
+ # @return [MessageboardGroupShow]
14
+ attr_reader :messageboard_group_show
13
15
  # @return [ModerationUserPage]
14
16
  attr_reader :moderation_user_page
15
17
  # @return [TopicPage]
@@ -33,6 +35,7 @@ module Thredded
33
35
  @post_form = PostForm.new
34
36
  @moderation_user_page = ModerationUserPage.new
35
37
  @messageboards_index = MessageboardsIndex.new
38
+ @messageboard_group_show = MessageboardGroupShow.new
36
39
  @topic_page = TopicPage.new
37
40
  end
38
41
 
@@ -95,6 +98,24 @@ module Thredded
95
98
  end
96
99
  end
97
100
 
101
+ class MessageboardGroupShow
102
+ # @return [Thredded::AllViewHooks::ViewHook]
103
+ attr_reader :container
104
+ # @return [Thredded::AllViewHooks::ViewHook]
105
+ attr_reader :list
106
+ # @return [Thredded::AllViewHooks::ViewHook]
107
+ attr_reader :group
108
+ # @return [Thredded::AllViewHooks::ViewHook]
109
+ attr_reader :messageboard
110
+
111
+ def initialize
112
+ @container = ViewHook.new
113
+ @list = ViewHook.new
114
+ @group = ViewHook.new
115
+ @messageboard = ViewHook.new
116
+ end
117
+ end
118
+
98
119
  class ModerationUserPage
99
120
  # @return [Thredded::AllViewHooks::ViewHook]
100
121
  attr_reader :user_title
@@ -3,7 +3,7 @@
3
3
  module Thredded
4
4
  # A view model for a page of MessageboardGroupViews.
5
5
  class MessageboardGroupView
6
- delegate :name, to: :@group, allow_nil: true
6
+ delegate :name, :id, to: :@group, allow_nil: true
7
7
  attr_reader :group, :messageboards
8
8
 
9
9
  # @param [ActiveRecord::Relation<Thredded::Messageboard>] messageboards_scope
@@ -0,0 +1,41 @@
1
+ <% content_for :thredded_page_title, t('thredded.messageboard_group.show.page_title', name: @group.name) %>
2
+ <% content_for :thredded_page_id, 'thredded--messageboard-groups--index' %>
3
+ <% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
4
+ <%= thredded_page do %>
5
+ <%= define_svg_icons('thredded/follow.svg', 'thredded/lock.svg') %>
6
+ <%= view_hooks.messageboard_group_show.container.render self, groups: @groups do %>
7
+ <section class="thredded--main-section thredded--messageboards">
8
+ <%= view_hooks.messageboard_group_show.list.render self, groups: @groups do %>
9
+ <% @groups.each do |group| %>
10
+ <% if group.name.present? %>
11
+ <h3 class="thredded--messageboards-group--title">
12
+ <%= link_to group.name,
13
+ show_messageboard_group_path(group.id),
14
+ class: 'thredded--link' %>
15
+ </h3>
16
+ <% end %>
17
+ <div class="thredded--messageboards-group">
18
+ <%= view_hooks.messageboard_group_show.group.render self, group: group do %>
19
+ <%= render partial: 'thredded/messageboards/messageboard',
20
+ collection: group.messageboards %>
21
+ <% end %>
22
+ <%= render partial: 'thredded/messageboards/grid_sizers' %>
23
+ </div>
24
+ <% end %>
25
+ <% end %>
26
+
27
+ <div class="thredded--messageboards--actions">
28
+ <% if policy(Thredded::Messageboard.new).create? %>
29
+ <a class="thredded--button" href="<%= new_messageboard_path %>" rel="nofollow">
30
+ <%= t('thredded.messageboard.create') %>
31
+ </a>
32
+ <% end %>
33
+ <% if policy(Thredded::MessageboardGroup.new).create? %>
34
+ <a class="thredded--button" href="<%= new_messageboard_group_path %>" rel="nofollow">
35
+ <%= t('thredded.messageboard_group.create') %>
36
+ </a>
37
+ <% end %>
38
+ </div>
39
+ </section>
40
+ <% end %>
41
+ <% end %>
@@ -8,7 +8,12 @@
8
8
  <%= view_hooks.messageboards_index.list.render self, groups: @groups do %>
9
9
  <% @groups.each do |group| %>
10
10
  <% if group.name.present? %>
11
- <h3 class="thredded--messageboards-group--title"><%= group.name %></h3>
11
+ <h3 class="thredded--messageboards-group--title">
12
+ <%= link_to_if Thredded.show_messageboard_group_page,
13
+ group.name,
14
+ show_messageboard_group_path(group.id),
15
+ class: 'thredded--link' %>
16
+ </h3>
12
17
  <% end %>
13
18
  <div class="thredded--messageboards-group">
14
19
  <%= view_hooks.messageboards_index.group.render self, group: group do %>
@@ -90,6 +90,8 @@ de:
90
90
  form:
91
91
  create_btn_submitting: :thredded.form.create_btn_submitting
92
92
  saved: Die Gruppe %{name} wurde erstellt
93
+ show:
94
+ page_title: 'Messageboard-Gruppe: %{name}'
93
95
  moderation:
94
96
  approve_btn: Erlauben
95
97
  block_btn: Blockieren
@@ -89,6 +89,8 @@ en:
89
89
  form:
90
90
  create_btn_submitting: :thredded.form.create_btn_submitting
91
91
  saved: Messageboard Group %{name} created
92
+ show:
93
+ page_title: 'Messageboard Group: %{name}'
92
94
  moderation:
93
95
  approve_btn: Approve
94
96
  block_btn: Block
@@ -91,6 +91,8 @@ es:
91
91
  form:
92
92
  create_btn_submitting: :thredded.form.create_btn_submitting
93
93
  saved: Sección %{name} creada
94
+ show:
95
+ page_title: 'Grupo de tablero de mensajes: %{name}'
94
96
  moderation:
95
97
  approve_btn: Aprobar
96
98
  block_btn: Rechazar
@@ -89,6 +89,8 @@ fr:
89
89
  form:
90
90
  create_btn_submitting: :thredded.form.create_btn_submitting
91
91
  saved: Le Groupe %{name} a été créé
92
+ show:
93
+ page_title: 'Groupe de babillard: %{name}'
92
94
  moderation:
93
95
  approve_btn: Approuver
94
96
  block_btn: Bloqué
@@ -89,6 +89,8 @@ it:
89
89
  form:
90
90
  create_btn_submitting: :thredded.form.create_btn_submitting
91
91
  saved: Creato il gruppo di bacheche %{name}
92
+ show:
93
+ page_title: 'Gruppo di messaggi: %{name}'
92
94
  moderation:
93
95
  approve_btn: Approva
94
96
  block_btn: Blocca
@@ -89,6 +89,8 @@ pl:
89
89
  form:
90
90
  create_btn_submitting: :thredded.form.create_btn_submitting
91
91
  saved: Grupa %{name} została utworzona
92
+ show:
93
+ page_title: 'Grupa forów: %{name}'
92
94
  moderation:
93
95
  approve_btn: Zaakceptuj
94
96
  block_btn: Zablokuj
@@ -91,6 +91,8 @@ pt-BR:
91
91
  form:
92
92
  create_btn_submitting: :thredded.form.create_btn_submitting
93
93
  saved: Grupo de mensagens %{name} criado
94
+ show:
95
+ page_title: 'Grupo de Messageboard: %{name}'
94
96
  moderation:
95
97
  approve_btn: Aprovar
96
98
  block_btn: Bloquear
@@ -88,6 +88,8 @@ ru:
88
88
  form:
89
89
  create_btn_submitting: :thredded.form.create_btn_submitting
90
90
  saved: Группа форумов %{name} создана
91
+ show:
92
+ page_title: 'Группа досок объявлений: %{name}'
91
93
  moderation:
92
94
  approve_btn: Одобрить
93
95
  block_btn: Заблокировать
@@ -84,6 +84,8 @@ zh-CN:
84
84
  form:
85
85
  create_btn_submitting: :thredded.form.create_btn_submitting
86
86
  saved: 板块分组 %{name} 已创建
87
+ show:
88
+ page_title: 留言板组:%{name}
87
89
  moderation:
88
90
  approve_btn: 通过
89
91
  block_btn: 锁定
@@ -59,6 +59,7 @@ Thredded::Engine.routes.draw do # rubocop:disable Metrics/BlockLength
59
59
 
60
60
  resource :preferences, only: %i[edit update], as: :global_preferences
61
61
  resource :messageboard, path: 'messageboards', only: [:new]
62
+ get '/messageboard-groups/:id', action: :show, controller: 'messageboard_groups', as: :show_messageboard_group
62
63
  resources :messageboards, only: %i[edit update destroy]
63
64
  resources :messageboards, only: %i[index create], path: '' do
64
65
  resource :preferences, only: %i[edit update]
@@ -58,6 +58,9 @@ Thredded.messageboards_order = :position
58
58
  # Whether admin users see button to delete entire messageboards on the messageboard edit page.
59
59
  Thredded.show_messageboard_delete_button = false
60
60
 
61
+ # Whether MessageboardGroup show page is enabled.
62
+ Thredded.show_messageboard_group_page = true
63
+
61
64
  # Whether users that are following a topic are listed on the topic page.
62
65
  Thredded.show_topic_followers = false
63
66
 
@@ -46,6 +46,8 @@ require 'thredded/base_notifier'
46
46
  require 'thredded/arel_compat'
47
47
  require 'thredded/collection_to_strings_with_cache_renderer'
48
48
 
49
+ require 'thredded/webpack_assets'
50
+
49
51
  if Rails::VERSION::MAJOR < 5
50
52
  begin
51
53
  require 'where-or'
@@ -132,6 +134,9 @@ module Thredded # rubocop:disable Metrics/ModuleLength
132
134
  # @return [Number] Minimum length to trigger username auto-completion for @-mentions and private message recipients.
133
135
  attr_accessor :autocomplete_min_length
134
136
 
137
+ # @return [Boolean] Whether MessageboardGroup show page is enabled.
138
+ attr_accessor :show_messageboard_group_page
139
+
135
140
  # @return [Thredded::AllViewHooks] View hooks configuration.
136
141
  def view_hooks
137
142
  Thredded::AllViewHooks.instance ||
@@ -282,6 +287,7 @@ module Thredded # rubocop:disable Metrics/ModuleLength
282
287
  self.messageboards_order = :position
283
288
  self.topics_per_page = 50
284
289
  self.autocomplete_min_length = 2
290
+ self.show_messageboard_group_page = true
285
291
 
286
292
  self.auto_follow_when_creating_topic = true
287
293
  self.auto_follow_when_posting_in_topic = true
@@ -17,9 +17,7 @@ module Thredded
17
17
 
18
18
  initializer 'thredded.setup_assets' do
19
19
  Thredded::Engine.config.assets.precompile += %w[
20
- thredded.js
21
- thredded.css
22
- thredded/*.svg
20
+ thredded_manifest.js
23
21
  ]
24
22
  end
25
23
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- VERSION = '0.16.13'
4
+ VERSION = '0.16.14'
5
5
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'set'
5
+
6
+ module Thredded
7
+ # Lets you include Thredded JavaScripts into your Webpack "pack".
8
+ #
9
+ # To use this, first run `bundle exec rails webpacker:install:erb`.
10
+ # Then, rename `app/javascript/packs/application.js` to `app/javascript/packs/application.js.erb`
11
+ # Finally, add this line to `app/javascript/packs/application.js.erb`:
12
+ #
13
+ # <%= Thredded::WebpackAssets.javascripts %>
14
+ #
15
+ # To include additional timeago locales, add this *before* `Thredded::WebpackAssets.javascripts`:
16
+ #
17
+ # <% timeago_root = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts') %>
18
+ # import "<%= File.join(timeago_root, 'timeago.js') %>";
19
+ # <%= %w[de pt_BR].map { |locale| %(import "#{File.join(timeago_root, "timeago/locales/#{locale}.js")}";) } * "\n" %>
20
+ module WebpackAssets
21
+ JAVASCRIPT_EXTS = %w[.es6 .js].freeze
22
+
23
+ def self.javascripts
24
+ @javascripts ||= JavaScriptsResolver.new.resolve('thredded.es6')
25
+ end
26
+
27
+ class JavaScriptsResolver
28
+ def resolve(entry_point)
29
+ resolve_full_paths(entry_point).map do |dep|
30
+ %(import "#{dep}";)
31
+ end.join("\n")
32
+ end
33
+
34
+ private
35
+
36
+ def resolve_full_paths(entry_point)
37
+ deps = []
38
+ seen = Set.new
39
+ queue = [[:start, dep_to_path(entry_point)]]
40
+ until queue.empty?
41
+ state, path = queue.pop
42
+ if state == :visiting
43
+ deps << path
44
+ next
45
+ end
46
+ next unless seen.add?(path)
47
+ queue << [:visiting, path]
48
+ next if path.to_s.start_with?('@')
49
+ src = File.read(path)
50
+ src.scan(%r{//= require (\S+)}).each do |m|
51
+ queue << [:start, dep_to_path(m[0])]
52
+ end
53
+ tree_root = Pathname.new(File.dirname(path))
54
+ src.scan(%r{//= require_tree (\S+)}).each do |m|
55
+ tree_root.join(m[0]).each_child do |pn|
56
+ next unless JAVASCRIPT_EXTS.include?(pn.extname)
57
+ queue << [:start, pn.cleanpath.to_s]
58
+ end
59
+ end
60
+ end
61
+ deps
62
+ end
63
+
64
+ def engine_js_root
65
+ @engine_js_root ||= Pathname.new(engine_js_prefix)
66
+ end
67
+
68
+ def engine_js_prefix
69
+ @engine_js_prefix ||=
70
+ File.expand_path('app/assets/javascripts', File.join(__dir__, '../..'))
71
+ end
72
+
73
+ def engine_vendor_js_prefix
74
+ @engine_vendor_js_prefix ||=
75
+ File.expand_path('vendor/assets/javascripts', File.join(__dir__, '../..'))
76
+ end
77
+
78
+ def engine_dep?(dep)
79
+ dep.start_with?('thredded/')
80
+ end
81
+
82
+ def engine_dep_to_path(dep)
83
+ find_dep_in_path(engine_js_prefix, dep) ||
84
+ find_dep_in_path(engine_vendor_js_prefix, dep) ||
85
+ fail("Failed to find #{dep}")
86
+ end
87
+
88
+ def find_dep_in_path(dir, dep)
89
+ path = File.join(dir, dep)
90
+ return path if File.file?(path)
91
+ JAVASCRIPT_EXTS.each do |ext|
92
+ path_with_ext = "#{path}#{ext}"
93
+ return path_with_ext if File.file?(path_with_ext)
94
+ end
95
+ nil
96
+ end
97
+
98
+ def dep_to_path(dep)
99
+ if dep == 'timeago'
100
+ path = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts', "#{dep}.js")
101
+ fail "Failed to find #{dep}" unless File.file?(path)
102
+ return path
103
+ elsif dep == 'rails-ujs'
104
+ return '@rails/ujs'
105
+ end
106
+ engine_dep_to_path(dep)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,280 @@
1
+ /** https://unpkg.com/autosize@4.0.2/dist/autosize.js **/
2
+ // Modified to always define the autosize global.
3
+ // This allows Thredded code to be compatible with both Webpack and Sprockets.
4
+ (function (global, factory) {
5
+ var mod = {
6
+ exports: {}
7
+ };
8
+ factory(mod, mod.exports);
9
+ global.autosize = mod.exports;
10
+ })(window, function (module, exports) {
11
+ 'use strict';
12
+
13
+ var map = typeof Map === "function" ? new Map() : function () {
14
+ var keys = [];
15
+ var values = [];
16
+
17
+ return {
18
+ has: function has(key) {
19
+ return keys.indexOf(key) > -1;
20
+ },
21
+ get: function get(key) {
22
+ return values[keys.indexOf(key)];
23
+ },
24
+ set: function set(key, value) {
25
+ if (keys.indexOf(key) === -1) {
26
+ keys.push(key);
27
+ values.push(value);
28
+ }
29
+ },
30
+ delete: function _delete(key) {
31
+ var index = keys.indexOf(key);
32
+ if (index > -1) {
33
+ keys.splice(index, 1);
34
+ values.splice(index, 1);
35
+ }
36
+ }
37
+ };
38
+ }();
39
+
40
+ var createEvent = function createEvent(name) {
41
+ return new Event(name, { bubbles: true });
42
+ };
43
+ try {
44
+ new Event('test');
45
+ } catch (e) {
46
+ // IE does not support `new Event()`
47
+ createEvent = function createEvent(name) {
48
+ var evt = document.createEvent('Event');
49
+ evt.initEvent(name, true, false);
50
+ return evt;
51
+ };
52
+ }
53
+
54
+ function assign(ta) {
55
+ if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return;
56
+
57
+ var heightOffset = null;
58
+ var clientWidth = null;
59
+ var cachedHeight = null;
60
+
61
+ function init() {
62
+ var style = window.getComputedStyle(ta, null);
63
+
64
+ if (style.resize === 'vertical') {
65
+ ta.style.resize = 'none';
66
+ } else if (style.resize === 'both') {
67
+ ta.style.resize = 'horizontal';
68
+ }
69
+
70
+ if (style.boxSizing === 'content-box') {
71
+ heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
72
+ } else {
73
+ heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
74
+ }
75
+ // Fix when a textarea is not on document body and heightOffset is Not a Number
76
+ if (isNaN(heightOffset)) {
77
+ heightOffset = 0;
78
+ }
79
+
80
+ update();
81
+ }
82
+
83
+ function changeOverflow(value) {
84
+ {
85
+ // Chrome/Safari-specific fix:
86
+ // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
87
+ // made available by removing the scrollbar. The following forces the necessary text reflow.
88
+ var width = ta.style.width;
89
+ ta.style.width = '0px';
90
+ // Force reflow:
91
+ /* jshint ignore:start */
92
+ ta.offsetWidth;
93
+ /* jshint ignore:end */
94
+ ta.style.width = width;
95
+ }
96
+
97
+ ta.style.overflowY = value;
98
+ }
99
+
100
+ function getParentOverflows(el) {
101
+ var arr = [];
102
+
103
+ while (el && el.parentNode && el.parentNode instanceof Element) {
104
+ if (el.parentNode.scrollTop) {
105
+ arr.push({
106
+ node: el.parentNode,
107
+ scrollTop: el.parentNode.scrollTop
108
+ });
109
+ }
110
+ el = el.parentNode;
111
+ }
112
+
113
+ return arr;
114
+ }
115
+
116
+ function resize() {
117
+ if (ta.scrollHeight === 0) {
118
+ // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
119
+ return;
120
+ }
121
+
122
+ var overflows = getParentOverflows(ta);
123
+ var docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240)
124
+
125
+ ta.style.height = '';
126
+ ta.style.height = ta.scrollHeight + heightOffset + 'px';
127
+
128
+ // used to check if an update is actually necessary on window.resize
129
+ clientWidth = ta.clientWidth;
130
+
131
+ // prevents scroll-position jumping
132
+ overflows.forEach(function (el) {
133
+ el.node.scrollTop = el.scrollTop;
134
+ });
135
+
136
+ if (docTop) {
137
+ document.documentElement.scrollTop = docTop;
138
+ }
139
+ }
140
+
141
+ function update() {
142
+ resize();
143
+
144
+ var styleHeight = Math.round(parseFloat(ta.style.height));
145
+ var computed = window.getComputedStyle(ta, null);
146
+
147
+ // Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box
148
+ var actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(computed.height)) : ta.offsetHeight;
149
+
150
+ // The actual height not matching the style height (set via the resize method) indicates that
151
+ // the max-height has been exceeded, in which case the overflow should be allowed.
152
+ if (actualHeight < styleHeight) {
153
+ if (computed.overflowY === 'hidden') {
154
+ changeOverflow('scroll');
155
+ resize();
156
+ actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
157
+ }
158
+ } else {
159
+ // Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands.
160
+ if (computed.overflowY !== 'hidden') {
161
+ changeOverflow('hidden');
162
+ resize();
163
+ actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
164
+ }
165
+ }
166
+
167
+ if (cachedHeight !== actualHeight) {
168
+ cachedHeight = actualHeight;
169
+ var evt = createEvent('autosize:resized');
170
+ try {
171
+ ta.dispatchEvent(evt);
172
+ } catch (err) {
173
+ // Firefox will throw an error on dispatchEvent for a detached element
174
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=889376
175
+ }
176
+ }
177
+ }
178
+
179
+ var pageResize = function pageResize() {
180
+ if (ta.clientWidth !== clientWidth) {
181
+ update();
182
+ }
183
+ };
184
+
185
+ var destroy = function (style) {
186
+ window.removeEventListener('resize', pageResize, false);
187
+ ta.removeEventListener('input', update, false);
188
+ ta.removeEventListener('keyup', update, false);
189
+ ta.removeEventListener('autosize:destroy', destroy, false);
190
+ ta.removeEventListener('autosize:update', update, false);
191
+
192
+ Object.keys(style).forEach(function (key) {
193
+ ta.style[key] = style[key];
194
+ });
195
+
196
+ map.delete(ta);
197
+ }.bind(ta, {
198
+ height: ta.style.height,
199
+ resize: ta.style.resize,
200
+ overflowY: ta.style.overflowY,
201
+ overflowX: ta.style.overflowX,
202
+ wordWrap: ta.style.wordWrap
203
+ });
204
+
205
+ ta.addEventListener('autosize:destroy', destroy, false);
206
+
207
+ // IE9 does not fire onpropertychange or oninput for deletions,
208
+ // so binding to onkeyup to catch most of those events.
209
+ // There is no way that I know of to detect something like 'cut' in IE9.
210
+ if ('onpropertychange' in ta && 'oninput' in ta) {
211
+ ta.addEventListener('keyup', update, false);
212
+ }
213
+
214
+ window.addEventListener('resize', pageResize, false);
215
+ ta.addEventListener('input', update, false);
216
+ ta.addEventListener('autosize:update', update, false);
217
+ ta.style.overflowX = 'hidden';
218
+ ta.style.wordWrap = 'break-word';
219
+
220
+ map.set(ta, {
221
+ destroy: destroy,
222
+ update: update
223
+ });
224
+
225
+ init();
226
+ }
227
+
228
+ function destroy(ta) {
229
+ var methods = map.get(ta);
230
+ if (methods) {
231
+ methods.destroy();
232
+ }
233
+ }
234
+
235
+ function update(ta) {
236
+ var methods = map.get(ta);
237
+ if (methods) {
238
+ methods.update();
239
+ }
240
+ }
241
+
242
+ var autosize = null;
243
+
244
+ // Do nothing in Node.js environment and IE8 (or lower)
245
+ if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
246
+ autosize = function autosize(el) {
247
+ return el;
248
+ };
249
+ autosize.destroy = function (el) {
250
+ return el;
251
+ };
252
+ autosize.update = function (el) {
253
+ return el;
254
+ };
255
+ } else {
256
+ autosize = function autosize(el, options) {
257
+ if (el) {
258
+ Array.prototype.forEach.call(el.length ? el : [el], function (x) {
259
+ return assign(x, options);
260
+ });
261
+ }
262
+ return el;
263
+ };
264
+ autosize.destroy = function (el) {
265
+ if (el) {
266
+ Array.prototype.forEach.call(el.length ? el : [el], destroy);
267
+ }
268
+ return el;
269
+ };
270
+ autosize.update = function (el) {
271
+ if (el) {
272
+ Array.prototype.forEach.call(el.length ? el : [el], update);
273
+ }
274
+ return el;
275
+ };
276
+ }
277
+
278
+ exports.default = autosize;
279
+ module.exports = exports['default'];
280
+ });
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thredded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.13
4
+ version: 0.16.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Oliveira
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-08-17 00:00:00.000000000 Z
12
+ date: 2019-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: active_record_union
@@ -295,14 +295,14 @@ dependencies:
295
295
  requirements:
296
296
  - - ">="
297
297
  - !ruby/object:Gem::Version
298
- version: '0'
298
+ version: 3.0.2.2
299
299
  type: :runtime
300
300
  prerelease: false
301
301
  version_requirements: !ruby/object:Gem::Requirement
302
302
  requirements:
303
303
  - - ">="
304
304
  - !ruby/object:Gem::Version
305
- version: '0'
305
+ version: 3.0.2.2
306
306
  - !ruby/object:Gem::Dependency
307
307
  name: capybara
308
308
  requirement: !ruby/object:Gem::Requirement
@@ -623,6 +623,7 @@ extra_rdoc_files: []
623
623
  files:
624
624
  - MIT-LICENSE
625
625
  - README.md
626
+ - app/assets/config/thredded_manifest.js
626
627
  - app/assets/images/favicons/README.md
627
628
  - app/assets/images/favicons/amazon.png
628
629
  - app/assets/images/favicons/github.png
@@ -845,6 +846,7 @@ files:
845
846
  - app/views/thredded/kaminari/_paginator.html.erb
846
847
  - app/views/thredded/kaminari/_prev_page.html.erb
847
848
  - app/views/thredded/messageboard_groups/new.html.erb
849
+ - app/views/thredded/messageboard_groups/show.html.erb
848
850
  - app/views/thredded/messageboards/_form.html.erb
849
851
  - app/views/thredded/messageboards/_grid_sizers.html.erb
850
852
  - app/views/thredded/messageboards/_messageboard.html.erb
@@ -1013,7 +1015,8 @@ files:
1013
1015
  - lib/thredded/version.rb
1014
1016
  - lib/thredded/view_hooks/config.rb
1015
1017
  - lib/thredded/view_hooks/renderer.rb
1016
- - vendor/assets/javascripts/autosize.min.js
1018
+ - lib/thredded/webpack_assets.rb
1019
+ - vendor/assets/javascripts/autosize.js
1017
1020
  - vendor/assets/javascripts/textcomplete.min.js
1018
1021
  homepage: https://thredded.org
1019
1022
  licenses:
@@ -1034,7 +1037,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1034
1037
  - !ruby/object:Gem::Version
1035
1038
  version: '0'
1036
1039
  requirements: []
1037
- rubygems_version: 3.0.3
1040
+ rubygems_version: 3.0.6
1038
1041
  signing_key:
1039
1042
  specification_version: 4
1040
1043
  summary: The best Rails forums engine ever.
@@ -1,2 +0,0 @@
1
- /** https://unpkg.com/autosize@4.0.2/dist/autosize.min.js **/
2
- !function(e,t){if("function"==typeof define&&define.amd)define(["module","exports"],t);else if("undefined"!=typeof exports)t(module,exports);else{var n={exports:{}};t(n,n.exports),e.autosize=n.exports}}(this,function(e,t){"use strict";var n,o,p="function"==typeof Map?new Map:(n=[],o=[],{has:function(e){return-1<n.indexOf(e)},get:function(e){return o[n.indexOf(e)]},set:function(e,t){-1===n.indexOf(e)&&(n.push(e),o.push(t))},delete:function(e){var t=n.indexOf(e);-1<t&&(n.splice(t,1),o.splice(t,1))}}),c=function(e){return new Event(e,{bubbles:!0})};try{new Event("test")}catch(e){c=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}function r(r){if(r&&r.nodeName&&"TEXTAREA"===r.nodeName&&!p.has(r)){var e,n=null,o=null,i=null,d=function(){r.clientWidth!==o&&a()},l=function(t){window.removeEventListener("resize",d,!1),r.removeEventListener("input",a,!1),r.removeEventListener("keyup",a,!1),r.removeEventListener("autosize:destroy",l,!1),r.removeEventListener("autosize:update",a,!1),Object.keys(t).forEach(function(e){r.style[e]=t[e]}),p.delete(r)}.bind(r,{height:r.style.height,resize:r.style.resize,overflowY:r.style.overflowY,overflowX:r.style.overflowX,wordWrap:r.style.wordWrap});r.addEventListener("autosize:destroy",l,!1),"onpropertychange"in r&&"oninput"in r&&r.addEventListener("keyup",a,!1),window.addEventListener("resize",d,!1),r.addEventListener("input",a,!1),r.addEventListener("autosize:update",a,!1),r.style.overflowX="hidden",r.style.wordWrap="break-word",p.set(r,{destroy:l,update:a}),"vertical"===(e=window.getComputedStyle(r,null)).resize?r.style.resize="none":"both"===e.resize&&(r.style.resize="horizontal"),n="content-box"===e.boxSizing?-(parseFloat(e.paddingTop)+parseFloat(e.paddingBottom)):parseFloat(e.borderTopWidth)+parseFloat(e.borderBottomWidth),isNaN(n)&&(n=0),a()}function s(e){var t=r.style.width;r.style.width="0px",r.offsetWidth,r.style.width=t,r.style.overflowY=e}function u(){if(0!==r.scrollHeight){var e=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push({node:e.parentNode,scrollTop:e.parentNode.scrollTop}),e=e.parentNode;return t}(r),t=document.documentElement&&document.documentElement.scrollTop;r.style.height="",r.style.height=r.scrollHeight+n+"px",o=r.clientWidth,e.forEach(function(e){e.node.scrollTop=e.scrollTop}),t&&(document.documentElement.scrollTop=t)}}function a(){u();var e=Math.round(parseFloat(r.style.height)),t=window.getComputedStyle(r,null),n="content-box"===t.boxSizing?Math.round(parseFloat(t.height)):r.offsetHeight;if(n<e?"hidden"===t.overflowY&&(s("scroll"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight):"hidden"!==t.overflowY&&(s("hidden"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight),i!==n){i=n;var o=c("autosize:resized");try{r.dispatchEvent(o)}catch(e){}}}}function i(e){var t=p.get(e);t&&t.destroy()}function d(e){var t=p.get(e);t&&t.update()}var l=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?((l=function(e){return e}).destroy=function(e){return e},l.update=function(e){return e}):((l=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return r(e)}),e}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],i),e},l.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],d),e}),t.default=l,e.exports=t.default});