Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions app/assets/javascripts/community_order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
document.addEventListener('DOMContentLoaded', () => {
const pref = /** @type {HTMLElement} */(document.querySelector(".community-sortable"));
const el = /** @type {HTMLElement} */(pref.querySelector(".sortable"));
const input = /** @type {HTMLInputElement} */(pref.querySelector(".js-user-pref"));

const sortable = Sortable.create(el, {
store: {
get: () => input.value.split(','),
set: self => {
input.value = self.toArray().join(',');
input.dispatchEvent(new Event("change"));
}
}
});

document.querySelectorAll('.community-sortable-list-item').forEach(el => {
el.querySelector('.sort-top-btn')?.addEventListener('click', _ => {
el.parentElement.insertAdjacentElement('afterbegin', el);
sortable.save();
});
el.querySelector('.sort-up-btn')?.addEventListener('click', _ => {
el.previousElementSibling?.insertAdjacentElement('beforebegin', el);
sortable.save();
});
el.querySelector('.sort-down-btn')?.addEventListener('click', _ => {
el.nextElementSibling?.insertAdjacentElement('afterend', el);
sortable.save();
});
el.querySelector('.sort-bottom-btn')?.addEventListener('click', _ => {
el.parentElement.insertAdjacentElement('beforeend', el);
sortable.save();
});
})
});
32 changes: 32 additions & 0 deletions app/assets/stylesheets/users.scss
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,38 @@ img {
}
}

.user-pref.user-block-pref {
flex-direction: column;
align-items: flex-start;
}

.community-sortable {
.community-sortable-list-item {
display: flex;
justify-content: flex-start;
align-items: center;
cursor: grab;
img {
max-height: 1.5rem;
}
.img {
width: 100px;
padding: 10px;
}
.label {
flex-grow: 1;
}
.sortable-buttons {
display: flex;
flex-direction: column;
}
.community-label {
display: flex;
align-items: center;
}
}
}

$sizes: (16, 32, 40, 48, 64, 128, 256);

@each $size in $sizes {
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def upload
end

def dashboard
@communities = Community.all
@communities = Community.all.user_preferred_order(current_user)
@edits = Post.unscoped do
SuggestedEdit.unscoped.joins(:post).where(active: true).group(Arel.sql('posts.category_id')).count
end
Expand Down
19 changes: 19 additions & 0 deletions app/models/community.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,23 @@ class Community < ApplicationRecord
default_scope { where(is_fake: false) }

validates :host, uniqueness: { case_sensitive: false }

scope :user_preferred_order, lambda { |user|
if user.nil?
all
else
user_order = user.preference('community_order', community: false)
.split(',')
.map(&:to_i)

# For some reason, even though we pass in filter: false,
# if user_order is empty, in_order_of still returns an empty array.
# Therefore we have to special case this
if user_order.empty?
all
else
in_order_of(:id, user_order, filter: false)
end
end
}
end
4 changes: 4 additions & 0 deletions app/views/application/dashboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<p>
All communities, with their categories. You can also see the communities from the dropdown arrow at the top right of
each page.
<% if user_signed_in? %>
You may change the order in which these communities appear in
<%= link_to 'your preferences', user_preferences_path %>.
<% end %>
</p>
<div class="grid community-list">
<% @communities.each do |c| %>
Expand Down
1 change: 1 addition & 0 deletions app/views/layouts/_head.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<%= stylesheet_link_tag 'application', media: 'all' %>

<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/jquery@2.2.2/dist/jquery.min.js" %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js" %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/moment@2.13.0/min/moment.min.js" %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/select2@4.0.12/dist/js/select2.min.js" %>
<% if SiteSetting['DonationsEnabled'] && (SiteSetting['LoadStripeEverywhere'] || controller_name == 'donations') %>
Expand Down
5 changes: 4 additions & 1 deletion app/views/layouts/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@

<div class="header-slide" id="communities-slide">
<h3 class="h-m-t-1">Communities</h3>
<% if user_signed_in? %>
<%= link_to 'Edit order', user_preferences_path, class: 'button is-muted is-small' %>
<% end %>
<div class="community-header-list">
<% logo_paths = SiteSetting.all_communities('SiteLogoPath') %>
<% Community.where(hidden: false).each do |c| %>
<% Community.where(hidden: false).user_preferred_order(current_user).each do |c| %>
<div class="community-header-list-entry">
<% logo_setting = logo_paths[c.id] %>
<%= link_to "//#{c.host}" do %>
Expand Down
89 changes: 65 additions & 24 deletions app/views/users/_prefs.html.erb
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
<% prefs.each do |name, value| %>
<% pref_config = AppConfig.preferences[name] %>
<label class="user-pref">
<div class="user-pref--meta">
<strong><%= name.humanize %></strong><br/>
<span class="desc"><%= pref_config['description'] %></span>
</div>
<div class="user-pref--value">
<% case pref_config['type'] %>
<% when 'boolean' %>
<input class="js-user-pref" type="checkbox" data-pref="<%= name %>" <%= 'checked' if value == 'true' %>
data-community="<%= community %>" />
<% when 'choice' %>
<%= select_tag nil,
options_for_select(preference_choice(pref_config), selected: value),
class: 'js-user-pref form-element',
data: { pref: name, community: community } %>
<% when 'integer' %>
<input class="js-user-pref form-element" type="number" data-pref="<%= name %>" value="<%= value %>"
data-community="<%= community %>" />
<% when 'string' %>
<input class="js-user-pref form-element" type="text" data-pref="<%= name %>" value="<%= value %>"
data-community="<%= community %>" />
<% end %>
</div>
</label>
<% case pref_config['type'] %>
<% when 'boolean' %>
<%= render 'prefs_inline', name: name, pref_config: pref_config do %>
<input class="js-user-pref" type="checkbox" data-pref="<%= name %>" <%= 'checked' if value == 'true' %>
data-community="<%= community %>" />
<% end %>
<% when 'choice' %>
<%= render 'prefs_inline', name: name, pref_config: pref_config do %>
<%= select_tag nil,
options_for_select(preference_choice(pref_config), selected: value),
class: 'js-user-pref form-element',
data: { pref: name, community: community } %>
<% end %>
<% when 'integer' %>
<%= render 'prefs_inline', name: name, pref_config: pref_config do %>
<input class="js-user-pref form-element" type="number" data-pref="<%= name %>" value="<%= value %>"
data-community="<%= community %>" />
<% end %>
<% when 'string' %>
<%= render 'prefs_inline', name: name, pref_config: pref_config do %>
<input class="js-user-pref form-element" type="text" data-pref="<%= name %>" value="<%= value %>"
data-community="<%= community %>" />
<% end %>
<% when 'community_order' %>
<%= render 'prefs_block', name: name, pref_config: pref_config do %>
<div class="user-pref--value community-sortable">
<details>
<summary>Reorder</summary>
<% logo_paths = SiteSetting.all_communities('SiteLogoPath') %>
<ol class="sortable">
<% Community.where(hidden: false).user_preferred_order(current_user).each do |c| %>
<li class="community-sortable-list-item grid" data-id="<%= c.id %>">
<i class="fas fa-grip-vertical"></i>
<div class="sortable-buttons grid--cell">
<button class="button is-icon-only-button is-extremely-large sort-top-btn"><i class="fas fa-angle-double-up"></i></button>
<button class="button is-icon-only-button is-extremely-large sort-bottom-btn"><i class="fas fa-angle-double-down"></i></button>
</div>
<div class="sortable-buttons grid--cell">
<button class="button is-icon-only-button is-extremely-large sort-up-btn"><i class="fas fa-arrow-up"></i></button>
<button class="button is-icon-only-button is-extremely-large sort-down-btn"><i class="fas fa-arrow-down"></i></button>
</div>
<div class="community-label grid--cell">
<% logo_setting = logo_paths[c.id] %>
<% if logo_setting.present? %>
<div class="img">
<img loading="lazy" src="<%= logo_setting %>" alt="<%= c.name %>" class="community-header-list-image">
</div>
<% end %>
<div class="label">
<%= c.name %>
</div>
</div>
</li>
<% end %>
</ol>
<input type="hidden"
class="js-user-pref form-element"
type="text"
data-pref="<%= name %>"
value="<%= value %>"
data-community="false" />
</details>
</div>
<% end %>
<% end %>
<% end %>
9 changes: 9 additions & 0 deletions app/views/users/_prefs_block.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="user-pref user-block-pref">
<div class="user-pref--meta">
<strong><%= name.humanize %></strong><br/>
<span class="desc"><%= pref_config['description'] %></span>
</div>
<div class="user-pref--value">
<%= yield %>
</div>
</div>
9 changes: 9 additions & 0 deletions app/views/users/_prefs_inline.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<label class="user-pref">
<div class="user-pref--meta">
<strong><%= name.humanize %></strong><br/>
<span class="desc"><%= pref_config['description'] %></span>
</div>
<div class="user-pref--value">
<%= yield %>
</div>
</label>
9 changes: 8 additions & 1 deletion config/config/preferences.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Schema:
#
# preference_name:
# type: boolean | string | integer | choice
# type: boolean | string | integer | choice | community_order
# description: Free-form description
# choice:
# - only
Expand Down Expand Up @@ -100,3 +100,10 @@ collapse_related_posts:
Collapse the Related Posts widget in the sidebar by default.
default: 'false'
global: true

community_order:
type: community_order
description: >
Order of communities in the dashboard and dropdown
default: ''
global: true
2 changes: 2 additions & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
declare var Sortable: typeof import("sortablejs");

interface ElementOffset {
top: number;
left: number;
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@types/jquery": "^3.5.32",
"@types/prettier": "2.7.3",
"@types/select2": "^4.0.63",
"@types/sortablejs": "1.15.9",
"dompurify": "3.4.13",
"moment": "2.13.0",
"prettier-plugin-brace-style": "0.8.1",
Expand Down
Loading