card-mod-follow 0.14.2 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89536b57eb136690674e4762d7d4663c82ad42000449ff2986de9d12cdfee49f
4
- data.tar.gz: 04fcbfb3fe979582c5a81598909d7a285065a0496c58ddaa284e08836ec77120
3
+ metadata.gz: b19692968a1b12d79cd8f2359364048ea3f845cd2d0f9f069f2e94447dd3262e
4
+ data.tar.gz: 7848e9787ba3e6e9609bef392a7f60054547948fcdc58bc6bdf4163a22e2ebe9
5
5
  SHA512:
6
- metadata.gz: d8d85edc90e6cd9251612ad3e993a5b8d24feb939fb18d77d3cb4ca1b3d9dd650b070ebd78cea7e8627ce9dc60e10ae3b83908730a5e032d8e5eb7ab246954f7
7
- data.tar.gz: 890213ca1c68626f756d7160945ab3b4754ff52144d7fbf78a6eca9677d8ed38261a9ea9e21804a72d3bd93bba9879c285ca9973726cf15c7c1dfdd7a0b32d44
6
+ metadata.gz: 9b9e543d4da82f42ff82e53f03f02da8fda6b18bacd7822bb1ec8629819b485d2238066903aa729d0552e1b98d71303a6f5daf30f211352e61e93062e06c00ac
7
+ data.tar.gz: 8510f87f53fe882ca415757e6c2d15ca7062e6cf153fdc643f4e3ad8b73df32f1639b1a36a84a454570ecf952e153ba448d0b263697797d775e72583b23f8f9c
data/README.md CHANGED
@@ -32,10 +32,10 @@ Special views include:
32
32
 
33
33
  Each follow preference can point to one or more of the following follow options.
34
34
 
35
- - _always:_ notify user after every create, update, and delete
36
- - _never:_ do not notify user
37
- - _created:_ notify user when a card is created
38
- - _edited:_ notify user when a card is created or updated
35
+ - _always:_ notify user of changes to all cards in set
36
+ - _never:_ do not notify user of changes to card in set
37
+ - _created:_ notify user of changes to cards user has created
38
+ - _edited:_ notify user of changes to cards user has edited
39
39
 
40
40
  ### Follow dashboard
41
41
 
@@ -112,7 +112,7 @@ Useful views added by this mod include:
112
112
  These rules rules are advanced configuration that let sharks determine which of a card’s fields
113
113
  are automatically followed when a user follow that card.
114
114
 
115
- The default value is `:includes`, which means that nested cards are followed by default.
115
+ The default value is `:nests`, which means that nested cards are followed by default.
116
116
 
117
117
  ## Lib
118
118
 
@@ -0,0 +1,19 @@
1
+ $(window).ready ->
2
+ $('body').on 'click', '.btn-item', ->
3
+ $(this).find('i').html('hourglass_full')
4
+
5
+ $('body').on 'mouseenter', '.btn-item-delete', ->
6
+ $(this).find('i').html('remove')
7
+ $(this).addClass("btn-danger").removeClass("btn-primary")
8
+
9
+ $('body').on 'mouseleave', '.btn-item-delete', ->
10
+ $(this).find('i').html('check')
11
+ $(this).addClass("btn-primary").removeClass("btn-danger")
12
+
13
+ $('body').on 'submit', '.edit-view.SELF-Xfollow_default .card-form', ->
14
+ confirmer = $(this).find '.confirm_update_all-view'
15
+ if confirmer.is ':hidden'
16
+ $(this).find('.follow-updater').show()
17
+
18
+ confirmer.show 'blind'
19
+ false
@@ -0,0 +1,7 @@
1
+ .TYPE_PLUS_RIGHT-user-Xfollow .tab-pane {
2
+ margin-top: 15px;
3
+ }
4
+
5
+ .follow-updater {
6
+ display: none;
7
+ }
@@ -0,0 +1,2 @@
1
+ // follow.js.coffee
2
+ (function(){$(window).ready(function(){return $("body").on("click",".btn-item",function(){return $(this).find("i").html("hourglass_full")}),$("body").on("mouseenter",".btn-item-delete",function(){return $(this).find("i").html("remove"),$(this).addClass("btn-danger").removeClass("btn-primary")}),$("body").on("mouseleave",".btn-item-delete",function(){return $(this).find("i").html("check"),$(this).addClass("btn-primary").removeClass("btn-danger")}),$("body").on("submit",".edit-view.SELF-Xfollow_default .card-form",function(){var n;if((n=$(this).find(".confirm_update_all-view")).is(":hidden"))return $(this).find(".follow-updater").show(),n.show("blind"),!1})})}).call(this);
data/data/real.yml ADDED
@@ -0,0 +1,99 @@
1
+ ---
2
+ - :name: Notification template
3
+ :type: :cardtype
4
+ :codename: notification_template
5
+
6
+
7
+ - :name: "*follow"
8
+ :type: :setting
9
+ :codename: follow
10
+ :fields:
11
+ :right:
12
+ :fields:
13
+ :input_type: radio
14
+ :help: Get notified about changes.
15
+
16
+ - :name: "*following"
17
+ :type: :list
18
+ :codename: following
19
+ :fields:
20
+ :right:
21
+ :fields:
22
+ :create: _left
23
+ :update: _left
24
+
25
+
26
+ - :name: "*followers"
27
+ :codename: followers
28
+ - :name: "*follow fields"
29
+ :type: :setting
30
+ :codename: follow_fields
31
+ - :name: "*always"
32
+ :codename: always
33
+ - :name: "*never"
34
+ :codename: never
35
+ - :name: "*follow defaults"
36
+ :type: :list
37
+ :codename: follow_defaults
38
+
39
+ - :name: follow suggestions
40
+ :type: :list
41
+ :codename: follow_suggestions
42
+
43
+ - :name:
44
+ - :all
45
+ - :follow_fields
46
+ :content: "*nests"
47
+
48
+ - :name: "*contextual class"
49
+ :codename: contextual_class
50
+ :fields:
51
+ :right:
52
+ :fields:
53
+ :input_type: radio
54
+ :content_options: |-
55
+ success
56
+ info
57
+ warning
58
+ danger
59
+ - :name: "*message"
60
+ :codename: message
61
+ - :name: "*disappear"
62
+ :codename: disappear
63
+
64
+
65
+ - :name: follower notification email
66
+ :type: :email_template
67
+ :codename: follower_notification_email
68
+ :fields:
69
+ :from: Decko Bot
70
+ :subject: '{{_user|name}} {{_|last_action_verb}} "{{_|name}}"'
71
+ :html_message: |-
72
+ <p>
73
+ {{_|link}} was just <a href="{{_|url}}?view=history">{{_|last_action_verb}}</a>
74
+ by {{_user|link}}
75
+ </p>
76
+ <blockquote>{{_|list_of_changes}}</blockquote>
77
+ <p>{{_|subedits}}</p>
78
+
79
+ <p>
80
+ You received this email because you're following <strong>{{_|followed}}</strong>.
81
+ </p>
82
+
83
+ <p>
84
+ <em>
85
+ <a href="{{_|unfollow_url}}">Unfollow</a> to stop receiving these emails.
86
+ </em>
87
+ </p>
88
+ :text_message: |-
89
+ "{{_|name}}" was just {{_|last_action_verb}} by {{_user|name}}.
90
+
91
+ {{_|list_of_changes}}
92
+
93
+ {{_|subedits}}
94
+
95
+ See the card: {{_|url}}
96
+
97
+ You received this email because you're following "{{_|followed}}".
98
+
99
+ Use this link to unfollow {{_|unfollow_url}}
@@ -0,0 +1,125 @@
1
+ ---
2
+ - :name:
3
+ - Google glass
4
+ - :self
5
+ - :follow_fields
6
+ :type: :list
7
+ - :name:
8
+ - Sunglasses
9
+ - :self
10
+ - :follow_fields
11
+ :type: :list
12
+ :content: |-
13
+ *nests
14
+ _self+price
15
+ _self+producer
16
+
17
+
18
+ - :name:
19
+ - John Following
20
+ - :self
21
+ - John
22
+ - :follow
23
+ :type: :list
24
+ :content: "*always"
25
+ - :name:
26
+ - All Eyes On Me
27
+ - :self
28
+ - John
29
+ - :follow
30
+ :type: :list
31
+ :content: "*always"
32
+ - :name:
33
+ - Sara Following
34
+ - :self
35
+ - Sara
36
+ - :follow
37
+ :type: :list
38
+ :content: "*always"
39
+ - :name:
40
+ - All Eyes On Me
41
+ - :self
42
+ - Sara
43
+ - :follow
44
+ :type: :list
45
+ :content: "*always"
46
+ - :name:
47
+ - Optic
48
+ - :type
49
+ - Sara
50
+ - :follow
51
+ :type: :list
52
+ :content: "*always"
53
+ - :name:
54
+ - Google glass
55
+ - :self
56
+ - Sara
57
+ - :follow
58
+ :type: :list
59
+ :content: "*always"
60
+ - :name:
61
+ - All Eyes On Me
62
+ - :self
63
+ - Big Brother
64
+ - :follow
65
+ :type: :list
66
+ :content: "*always"
67
+ - :name:
68
+ - Look At Me
69
+ - :self
70
+ - Big Brother
71
+ - :follow
72
+ :type: :list
73
+ :content: "*always"
74
+ - :name:
75
+ - Optic
76
+ - :type
77
+ - Big Brother
78
+ - :follow
79
+ :type: :list
80
+ :content: "*always"
81
+ - :name:
82
+ - lens
83
+ - :right
84
+ - Big Brother
85
+ - :follow
86
+ :type: :list
87
+ :content: "*always"
88
+ - :name:
89
+ - Optic
90
+ - tint
91
+ - :type_plus_right
92
+ - Big Brother
93
+ - :follow
94
+ :type: :list
95
+ :content: "*always"
96
+ - :name:
97
+ - :all
98
+ - Big Brother
99
+ - :follow
100
+ :type: :list
101
+ :content: |-
102
+ *created
103
+ *edited
104
+ - :name:
105
+ - Optic
106
+ - :type
107
+ - Optic fan
108
+ - :follow
109
+ :type: :list
110
+ :content: "*always"
111
+ - :name:
112
+ - Sunglasses
113
+ - :self
114
+ - Sunglasses fan
115
+ - :follow
116
+ :type: :list
117
+ :content: "*always"
118
+ - :name:
119
+ - :all
120
+ - Narcissist
121
+ - :follow
122
+ :type: :list
123
+ :content: |-
124
+ *created
125
+ *edited
@@ -0,0 +1,53 @@
1
+ ---
2
+ # users use "conflict: :defer" because otherwise Card#ensure detects false
3
+ # changes in the +:password card (because the encrypted version is compared to the
4
+ # the unencrypted one. A deeper fix would address the change detection in the
5
+ # +:password cards
6
+ - :name: John
7
+ :type: :user
8
+ :conflict: :defer
9
+ :fields:
10
+ :account:
11
+ :fields:
12
+ :email: john@user.com
13
+ :password: john_pass
14
+ - :name: Sara
15
+ :type: :user
16
+ :conflict: :defer
17
+ :fields:
18
+ :account:
19
+ :fields:
20
+ :email: sara@user.com
21
+ :password: sara_pass
22
+ - :name: Big Brother
23
+ :type: :user
24
+ :conflict: :defer
25
+ :fields:
26
+ :account:
27
+ :fields:
28
+ :email: big-brother@user.com
29
+ :password: big-brother_pass
30
+ - :name: Optic fan
31
+ :type: :user
32
+ :conflict: :defer
33
+ :fields:
34
+ :account:
35
+ :fields:
36
+ :email: optic-fan@user.com
37
+ :password: optic-fan_pass
38
+ - :name: Sunglasses fan
39
+ :type: :user
40
+ :conflict: :defer
41
+ :fields:
42
+ :account:
43
+ :fields:
44
+ :email: sunglasses-fan@user.com
45
+ :password: sunglasses-fan_pass
46
+ - :name: Narcissist
47
+ :type: :user
48
+ :conflict: :defer
49
+ :fields:
50
+ :account:
51
+ :fields:
52
+ :email: narcissist@user.com
53
+ :password: narcissist_pass
@@ -0,0 +1,56 @@
1
+ ---
2
+ - :name: card with fields
3
+ :content: main content {{+field 1}} {{+field 2}}
4
+ :user: Joe User
5
+ :fields:
6
+ field 1: content of field 1
7
+ field 2: content of field 2
8
+ - :name: card with fields and admin fields
9
+ :content: main content {{+field 1}} {{+admin field 1}}
10
+ :user: Joe User
11
+ :fields:
12
+ field 1: content of field 1
13
+ admin field 1: content of admin field 1
14
+ - :name: admin card with fields and admin fields
15
+ :content: main content {{+field 1}} {{+admin field 1}}
16
+ :user: Joe User
17
+ :fields:
18
+ field 1: content of field 1
19
+ admin field 1: content of admin field 1
20
+ - :name: admin card with admin fields
21
+ :content: main content {{+admin field 1}} {{+admin field 2}}
22
+ :user: Joe User
23
+ :fields:
24
+ admin field 1: content of admin field 1
25
+ admin field 2: content of admin field 2
26
+
27
+ - :name:
28
+ - admin card with fields and admin fields
29
+ - :self
30
+ - :read
31
+ :type: :pointer
32
+ :content: Administrator
33
+ - :name:
34
+ - admin card with admin fields
35
+ - :self
36
+ - :read
37
+ :type: :pointer
38
+ :content: Administrator
39
+ - :name:
40
+ - admin field 1
41
+ - :right
42
+ - :read
43
+ :type: :pointer
44
+ :content: Administrator
45
+ - :name:
46
+ - admin field 2
47
+ - :right
48
+ - :read
49
+ :type: :pointer
50
+ :content: Administrator
51
+ - :name:
52
+ - field 1
53
+ - :right
54
+ - :read
55
+ :type: :pointer
56
+ :content: Anyone
data/data/test.yml ADDED
@@ -0,0 +1,39 @@
1
+ ---
2
+ - users
3
+ - :name: Optic
4
+ :type: :cardtype
5
+ - :name: All Eyes On Me
6
+ - :name: No One Sees Me
7
+ - :name: Look At Me
8
+ - :name: Sara Following
9
+ - :name: John Following
10
+ :content: "{{+her}}"
11
+ - :name:
12
+ - John Following
13
+ - her
14
+ - :name:
15
+ - Magnifier
16
+ - lens
17
+ - :name:
18
+ - Magnifier
19
+ - lens
20
+ :content: zoom in
21
+ :user: Narcissist
22
+ - :name: Sunglasses
23
+ :type: Optic
24
+ :content: "{{+tint}}{{+lens}}"
25
+ :user: Narcissist
26
+ - :name: Google glass
27
+ :type: Optic
28
+ :user: Optic fan
29
+ :content: "{{+price}}"
30
+ - :name:
31
+ - Sunglasses
32
+ - tint
33
+ - :name:
34
+ - Sunglasses
35
+ - price
36
+ - :name: Created card
37
+ :content: new content
38
+ - rules
39
+ - with_fields
@@ -12,8 +12,8 @@ class FollowLink
12
12
  def modal_link icon=false
13
13
  opts = link_opts.merge(
14
14
  "data-path": link_opts[:path],
15
- "data-toggle": "modal",
16
- "data-target": "#modal-#{card.name.safe_key}",
15
+ "data-bs-toggle": "modal",
16
+ "data-bs-target": "#modal-#{card.name.safe_key}",
17
17
  class: css_classes("follow-link", link_opts[:class])
18
18
  )
19
19
  link_to render_link_text(icon), opts
@@ -50,7 +50,7 @@ def all_follow_rule_options follower_id
50
50
  end
51
51
 
52
52
  def follow_rule_option_applies? follower_id, option
53
- option_code = option.to_name.code
53
+ option_code = option.to_name.codename
54
54
  candidate_ids = follower_candidate_ids_for_option option_code
55
55
  follow_rule_option_applies_to_candidates? follower_id, option_code, candidate_ids
56
56
  end
@@ -88,7 +88,7 @@ format do
88
88
  def edit_info_for field, action
89
89
  return nil unless (value = action.value field)
90
90
 
91
- value = action.previous_value if action.action_type == :delete
91
+ value = action.previous_value(field) if action.action_type == :delete
92
92
  wrap_list_item " #{notification_action_label action} #{field}: #{value}"
93
93
  end
94
94
 
data/set/all/notify.rb CHANGED
@@ -10,8 +10,8 @@ def silence_notifications?
10
10
  !(Card::Env.controller || force_notifications)
11
11
  end
12
12
 
13
- event :notify_followers_after_save,
14
- :integrate_with_delay, on: :save, when: :notable_change? do
13
+ event :notify_followers_after_save, :integrate_with_delay,
14
+ on: :save, priority: 20, when: :notable_change? do
15
15
  notify_followers
16
16
  end
17
17
 
@@ -0,0 +1 @@
1
+ assign_type :list
@@ -0,0 +1 @@
1
+ assign_type :toggle
data/set/right/follow.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  # While the user follow dashboard ([User]+:follow`) is also in this Set, its
3
3
  # customizations are handled in TypePlusRight::User::Follow
4
4
 
5
+ assign_type :list
6
+
5
7
  event :cache_expired_for_new_preference, :integrate, when: :preference? do
6
8
  Card.follow_caches_expired
7
9
  end
@@ -34,6 +36,10 @@ def ok_to_delete
34
36
  permit :delete
35
37
  end
36
38
 
39
+ def raw_help_text
40
+ "Get notified about changes"
41
+ end
42
+
37
43
  def permit action, verb=nil
38
44
  if %i[create delete update].include?(action) && allowed_to_change_follow_status?
39
45
  true
@@ -1,3 +1,5 @@
1
+ assign_type :list
2
+
1
3
  event :follow_fields_changed, :integrate do
2
4
  Card.follow_caches_expired
3
5
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # X+*followers provides a list of all users following X.
4
4
 
5
- include_set Abstract::Pointer
5
+ include_set Abstract::List
6
6
 
7
7
  format :html do
8
8
  view :core, cache: :never do
@@ -1,3 +1,5 @@
1
+ assign_type :list
2
+
1
3
  def virtual?
2
4
  new?
3
5
  end
@@ -0,0 +1 @@
1
+ assign_type :phrase
@@ -14,7 +14,7 @@
14
14
  # - if you truly want to override existing follow rules, that may be monkey territory?
15
15
  # - we will delete "*follow defaults" after the above are completed
16
16
 
17
- event :update_follow_rules, :finalize, on: :save, when: :update_all_users do
17
+ event :update_follow_rules, :finalize, on: :save, trigger: :required do
18
18
  Auth.as_bot do
19
19
  Card.search(type: "user").each do |user|
20
20
  follow_defaults.each do |set_card, option|
@@ -46,24 +46,16 @@ def follow_option item
46
46
  end
47
47
 
48
48
  format :html do
49
- view :edit, perms: :update, unknown: true do
50
- frame_and_form :update, hidden: { card: { update_all_users: false } } do
51
- [
52
- _render_content_formgroups,
53
- _render_confirm_update_all,
54
- _render_edit_buttons
55
- ]
56
- end
57
- end
58
-
59
49
  view :edit_buttons do
60
- button_formgroup do
61
- [submit_and_update_button, simple_submit_button, cancel_to_edit_button]
62
- end
50
+ render_confirm_update_all +
51
+ button_formgroup do
52
+ [submit_and_update_button, simple_submit_button, cancel_to_edit_button]
53
+ end
63
54
  end
64
55
 
65
56
  def submit_and_update_button
66
57
  submit_button text: "Submit and update all users",
58
+ name: "card[trigger]", value: "update_follow_rules",
67
59
  disable_with: "Updating", class: "follow-updater"
68
60
  end
69
61
 
@@ -29,5 +29,5 @@ def disappear?
29
29
  end
30
30
 
31
31
  def alert_class
32
- contextual_class.present? ? contextual_class_card.item_name : :success
32
+ contextual_class.present? ? contextual_class_card.first_name : :success
33
33
  end
data/set/type/set.rb CHANGED
@@ -37,8 +37,8 @@ def all_members_followed_by? user_id=nil
37
37
  end
38
38
 
39
39
  def broader_set_followed_by? user_id
40
- broader_sets.find do |set_name|
41
- Card.fetch(set_name)&.directly_followed_by? user_id
40
+ broader_sets.find do |set_card|
41
+ set_card&.directly_followed_by? user_id
42
42
  end
43
43
  end
44
44
 
@@ -4,7 +4,7 @@ FOLLOW_TABS = { "Follow" => { view: "follow_tab" },
4
4
  # a virtual pointer to the sets that a user is following.
5
5
  # (data is stored in preferences: `[Set]+[User]+:follow`)
6
6
 
7
- include_set Abstract::Pointer
7
+ include_set Abstract::List
8
8
  def virtual?
9
9
  new?
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: card-mod-follow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan McCutchen
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-01-08 00:00:00.000000000 Z
13
+ date: 2023-03-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: card
@@ -18,28 +18,28 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.104.2
21
+ version: 1.105.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 1.104.2
28
+ version: 1.105.1
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: card-mod-carrierwave
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - '='
34
34
  - !ruby/object:Gem::Version
35
- version: 0.14.2
35
+ version: 0.15.1
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - '='
41
41
  - !ruby/object:Gem::Version
42
- version: 0.14.2
42
+ version: 0.15.1
43
43
  description: ''
44
44
  email:
45
45
  - info@decko.org
@@ -48,6 +48,14 @@ extensions: []
48
48
  extra_rdoc_files: []
49
49
  files:
50
50
  - README.md
51
+ - assets/script/follow.js.coffee
52
+ - assets/style/follow.scss
53
+ - data/files/mod_follow_script_asset_output/file.js
54
+ - data/real.yml
55
+ - data/test.yml
56
+ - data/test/rules.yml
57
+ - data/test/users.yml
58
+ - data/test/with_fields.yml
51
59
  - lib/card/follow_option.rb
52
60
  - lib/card/follower_stash.rb
53
61
  - set/abstract/follow_option.rb
@@ -62,11 +70,14 @@ files:
62
70
  - set/all/notify/base_views.rb
63
71
  - set/all/notify/html_views.rb
64
72
  - set/right/account.rb
73
+ - set/right/contextual_class.rb
74
+ - set/right/disappear.rb
65
75
  - set/right/follow.rb
66
76
  - set/right/follow/follow_status.haml
67
77
  - set/right/follow_fields.rb
68
78
  - set/right/followers.rb
69
79
  - set/right/following.rb
80
+ - set/right/message.rb
70
81
  - set/self/always.rb
71
82
  - set/self/created.rb
72
83
  - set/self/edited.rb
@@ -91,6 +102,7 @@ metadata:
91
102
  wiki_uri: https://decko.org
92
103
  documentation_url: http://docs.decko.org/
93
104
  card-mod: follow
105
+ card-mod-group: gem-defaults
94
106
  post_install_message:
95
107
  rdoc_options: []
96
108
  require_paths:
@@ -106,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
118
  - !ruby/object:Gem::Version
107
119
  version: '0'
108
120
  requirements: []
109
- rubygems_version: 3.2.15
121
+ rubygems_version: 3.3.11
110
122
  signing_key:
111
123
  specification_version: 4
112
124
  summary: follower notifications