Skip to content

Commit cc149a5

Browse files
authored
Merge pull request #1698 from codidact/art/1348/duplicate-links
Link to duplicate posts
2 parents 434fcca + 32c2051 commit cc149a5

5 files changed

Lines changed: 131 additions & 30 deletions

File tree

app/assets/javascripts/sidebar.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
document.addEventListener('DOMContentLoaded', () => {
2+
QPixel.DOM.addDelegatedListener('click', '.js-widget-hide, .js-widget-hide *', ev => {
3+
let tgt = /** @type {HTMLElement} */ (ev.target);
4+
if (!tgt.classList.contains('js-widget-hide')) {
5+
tgt = tgt.closest('.js-widget-hide');
6+
}
7+
const icon = tgt.querySelector('i');
8+
const widget = /** @type {HTMLElement} */ (tgt.closest('.widget'));
9+
const isHidden = widget.dataset.collapsed === 'true';
10+
if (isHidden) {
11+
widget.querySelectorAll('.widget--body').forEach(el => {
12+
el.classList.remove('hiding', 'hidden');
13+
});
14+
icon.classList.remove('fa-chevron-down');
15+
icon.classList.add('fa-chevron-up');
16+
widget.dataset.collapsed = 'false';
17+
}
18+
else {
19+
widget.querySelectorAll('.widget--body').forEach(el => {
20+
el.classList.add('hiding');
21+
setTimeout(() => el.classList.add('hidden'), 200);
22+
});
23+
icon.classList.remove('fa-chevron-up');
24+
icon.classList.add('fa-chevron-down');
25+
widget.dataset.collapsed = 'true';
26+
}
27+
});
28+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.widget--body {
2+
transition: all 0.2s ease;
3+
}
4+
5+
.widget--body.hiding {
6+
scale: 0;
7+
height: 0;
8+
margin: 0;
9+
padding: 0;
10+
}
11+
12+
.widget--body.hidden {
13+
display: none;
14+
}

app/models/post.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class Post < ApplicationRecord
2323
has_many :flags, as: :post, dependent: :destroy
2424
has_many :children, class_name: 'Post', foreign_key: 'parent_id', dependent: :destroy
2525
has_many :suggested_edits, dependent: :destroy
26-
has_many :reactions
26+
has_many :reactions, dependent: :destroy
27+
has_many :inbound_duplicates, class_name: 'Post', foreign_key: 'duplicate_post_id', dependent: :nullify
2728

2829
counter_culture :parent, column_name: proc { |model| model.deleted? ? nil : 'answer_count' }
2930
counter_culture [:user, :community_user], column_name: proc { |model| model.deleted? ? nil : 'post_count' }
@@ -53,6 +54,7 @@ class Post < ApplicationRecord
5354
includes(:user, :tags, :post_type, :category, :last_activity_by,
5455
user: :avatar_attachment)
5556
}
57+
scope :has_duplicates, -> { joins(:inbound_duplicates) } # uses INNER JOIN by default so no where required
5658

5759
before_validation :update_tag_associations, if: -> { post_type&.has_tags }
5860
after_create :create_initial_revision

app/views/layouts/_sidebar.html.erb

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,81 @@
1414

1515

1616
<% unless @community.is_fake %>
17-
<% if Rails.env.development? || @hot_questions.to_a.size > 0 || @pinned_links.to_a.size > 0 %>
18-
<div class="widget has-margin-4 is-tertiary">
19-
<% if Rails.env.development? || @pinned_links.to_a.size > 0 %>
20-
<div class="widget--header">Featured</div>
21-
<% @pinned_links.each do |pl| %>
22-
<div class="widget--body">
23-
<% pl_link = pl.post.nil? ? pl.link : generic_share_link(pl.post) %>
24-
<% pl_label = pl.post.nil? ? pl.label : (pl.post.parent.nil? ? pl.post.title : pl.post.parent.title) %>
25-
<%= link_to pl_link, class: 'h-fw-bold' do %>
26-
<%= pl_label %>
27-
<% end %>
28-
<% unless pl.shown_before.nil? %>
29-
<div>
30-
&mdash;
31-
<% if !pl.shown_after.nil? %>
32-
<% if pl.shown_after < DateTime.now %>
33-
ends in <%= time_ago_in_words(pl.shown_before) %>
17+
<%# Featured widget %>
18+
<% if Rails.env.development? || @pinned_links.to_a.size > 0 %>
19+
<% cache @pinned_links do %>
20+
<div class="widget has-margin-4 is-teal">
21+
<% if Rails.env.development? || @pinned_links.to_a.size > 0 %>
22+
<div class="widget--header">Featured</div>
23+
<% @pinned_links.each do |pl| %>
24+
<div class="widget--body">
25+
<% pl_link = pl.post.nil? ? pl.link : generic_share_link(pl.post) %>
26+
<% pl_label = pl.post.nil? ? pl.label : (pl.post.parent.nil? ? pl.post.title : pl.post.parent.title) %>
27+
<%= link_to pl_link, class: 'h-fw-bold' do %>
28+
<%= pl_label %>
29+
<% end %>
30+
<% unless pl.shown_before.nil? %>
31+
<div>
32+
&mdash;
33+
<% if !pl.shown_after.nil? %>
34+
<% if pl.shown_after < DateTime.now %>
35+
ends in <%= time_ago_in_words(pl.shown_before) %>
36+
<% else %>
37+
starts in <%= time_ago_in_words(pl.shown_after) %>
38+
<% end %>
3439
<% else %>
35-
starts in <%= time_ago_in_words(pl.shown_after) %>
40+
in <%= time_ago_in_words(pl.shown_before) %>
3641
<% end %>
37-
<% else %>
38-
in <%= time_ago_in_words(pl.shown_before) %>
39-
<% end %>
40-
</div>
42+
</div>
43+
<% end %>
44+
</div>
45+
<% end %>
46+
<% end %>
47+
</div>
48+
<% end %>
49+
<% end %>
50+
51+
<%# Related Posts widget %>
52+
<% if defined?(@post) && @post.inbound_duplicates.any? %>
53+
<% collapse_related = user_preference('collapse_related_posts') == 'true' %>
54+
<% cache [@post, @post.inbound_duplicates] do %>
55+
<div class="widget has-margin-4 is-green" data-collapsed="<%= user_preference('collapse_related_posts') %>">
56+
<div class="widget--header">
57+
Related Posts
58+
<button type="button" class="widget--header-link button is-icon-only-button js-widget-hide"
59+
aria-label="Collapse Related Posts">
60+
<i class="fas <%= collapse_related ? 'fa-chevron-down' : 'fa-chevron-up' %>"></i>
61+
</button>
62+
</div>
63+
<% @post.inbound_duplicates.each do |dp| %>
64+
<div class="widget--body <%= 'hidden' if collapse_related %>">
65+
<% unless dp.category.nil? %>
66+
<%= dp.category.name %>
67+
&mdash;
68+
<% end %>
69+
<%= link_to generic_share_link(dp) do %>
70+
<%= dp.title %>
4171
<% end %>
4272
</div>
4373
<% end %>
44-
<% end %>
45-
<% if Rails.env.development? || @hot_questions.to_a.size > 0 %>
46-
<div class="widget--header">Hot Posts</div>
74+
</div>
75+
<% end %>
76+
<% end %>
77+
78+
<%# Hot Posts widget %>
79+
<% if Rails.env.development? || @hot_questions.to_a.size > 0 %>
80+
<% collapse_hot = user_preference('collapse_hot_posts') == 'true' %>
81+
<% cache @hot_questions do %>
82+
<div class="widget has-margin-4 is-tertiary" data-collapsed="<%= user_preference('collapse_hot_posts') %>">
83+
<div class="widget--header">
84+
Hot Posts
85+
<button type="button" class="widget--header-link button is-icon-only-button js-widget-hide"
86+
aria-label="Collapse Hot Posts">
87+
<i class="fas <%= collapse_hot ? 'fa-chevron-down' : 'fa-chevron-up' %>"></i>
88+
</button>
89+
</div>
4790
<% @hot_questions.each do |hq| %>
48-
<div class="widget--body">
91+
<div class="widget--body <%= 'hidden' if collapse_hot %>">
4992
<% unless hq.category.nil? %>
5093
<%= hq.category.name %>
5194
&mdash;
@@ -55,8 +98,8 @@
5598
<% end %>
5699
</div>
57100
<% end %>
58-
<% end %>
59-
</div>
101+
</div>
102+
<% end %>
60103
<% end %>
61104
<% end %>
62105

config/config/preferences.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,18 @@ sticky_header:
8383
default_filter_name:
8484
type: ~
8585
default: none
86-
category: true
86+
category: true
87+
88+
collapse_hot_posts:
89+
type: boolean
90+
description: >
91+
Collapse the Hot Posts widget in the sidebar by default.
92+
default: 'false'
93+
global: true
94+
95+
collapse_related_posts:
96+
type: boolean
97+
description: >
98+
Collapse the Related Posts widget in the sidebar by default.
99+
default: 'false'
100+
global: true

0 commit comments

Comments
 (0)