thredded 0.16.13 → 0.16.14

Sign up to get free protection for your applications and to get access to all the features.
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});