Permalink
Browse files

Add profile directory (#9427)

Fix #5578
  • Loading branch information...
Gargron committed Dec 6, 2018
1 parent 155cf12 commit 73be8f38c115c279e3d3961b98bd2b82b9706b05
Showing with 578 additions and 7 deletions.
  1. +44 −0 app/controllers/admin/tags_controller.rb
  2. +1 −1 app/controllers/api/v1/accounts/credentials_controller.rb
  3. +48 −0 app/controllers/directories_controller.rb
  4. +1 −1 app/controllers/settings/profiles_controller.rb
  5. +2 −1 app/helpers/admin/filter_helper.rb
  6. +5 −0 app/javascript/styles/mastodon/accounts.scss
  7. +165 −0 app/javascript/styles/mastodon/widgets.scss
  8. +40 −0 app/models/account.rb
  9. +10 −2 app/models/account_stat.rb
  10. +24 −0 app/models/account_tag_stat.rb
  11. +3 −0 app/models/concerns/account_associations.rb
  12. +1 −0 app/models/concerns/account_counters.rb
  13. +26 −0 app/models/tag.rb
  14. +15 −0 app/policies/tag_policy.rb
  15. +5 −0 app/services/update_account_service.rb
  16. +12 −0 app/views/admin/tags/_tag.html.haml
  17. +19 −0 app/views/admin/tags/index.html.haml
  18. +59 −0 app/views/directories/index.html.haml
  19. +4 −0 app/views/layouts/public.html.haml
  20. +3 −1 app/views/settings/profiles/show.html.haml
  21. +19 −0 config/locales/en.yml
  22. +2 −0 config/locales/simple_form.en.yml
  23. +1 −0 config/navigation.rb
  24. +12 −0 config/routes.rb
  25. +8 −0 db/migrate/20181203003808_create_accounts_tags_join_table.rb
  26. +5 −0 db/migrate/20181203021853_add_discoverable_to_accounts.rb
  27. +5 −0 db/migrate/20181204193439_add_last_status_at_to_account_stats.rb
  28. +11 −0 db/migrate/20181204215309_create_account_tag_stats.rb
  29. +20 −1 db/schema.rb
  30. +3 −0 spec/fabricators/account_tag_stat_fabricator.rb
  31. +5 −0 spec/models/account_tag_stat_spec.rb
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Admin
class TagsController < BaseController
before_action :set_tags, only: :index
before_action :set_tag, except: :index
before_action :set_filter_params

def index
authorize :tag, :index?
end

def hide
authorize @tag, :hide?
@tag.account_tag_stat.update!(hidden: true)
redirect_to admin_tags_path(@filter_params)
end

def unhide
authorize @tag, :unhide?
@tag.account_tag_stat.update!(hidden: true)
redirect_to admin_tags_path(@filter_params)
end

private

def set_tags
@tags = Tag.discoverable
@tags.merge!(Tag.hidden) if filter_params[:hidden]
end

def set_tag
@tag = Tag.find(params[:id])
end

def set_filter_params
@filter_params = filter_params.to_hash.symbolize_keys
end

def filter_params
params.permit(:hidden)
end
end
end
@@ -21,7 +21,7 @@ def update
private

def account_params
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
end

def user_settings_params
@@ -0,0 +1,48 @@
# frozen_string_literal: true

class DirectoriesController < ApplicationController
layout 'public'

before_action :set_instance_presenter
before_action :set_tag, only: :show
before_action :set_tags
before_action :set_accounts

def index
render :index
end

def show
render :index
end

private

def set_tag
@tag = Tag.discoverable.find_by!(name: params[:id].downcase)
end

def set_tags
@tags = Tag.discoverable.limit(30)
end

def set_accounts
@accounts = Account.searchable.discoverable.page(params[:page]).per(50).tap do |query|
query.merge!(Account.tagged_with(@tag.id)) if @tag

if popular_requested?
query.merge!(Account.popular)
else
query.merge!(Account.by_recent_status)
end
end
end

def set_instance_presenter
@instance_presenter = InstancePresenter.new
end

def popular_requested?
request.path.ends_with?('/popular')
end
end
@@ -29,7 +29,7 @@ def update
private

def account_params
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
end

def set_account
@@ -5,8 +5,9 @@ module Admin::FilterHelper
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
INVITE_FILTER = %i(available expired).freeze
CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
TAGS_FILTERS = %i(hidden).freeze

FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS
FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS

def filter_link_to(text, link_to_params, link_class_params = link_to_params)
new_url = filtered_url_for(link_to_params)
@@ -189,6 +189,11 @@
&--under-tabs {
border-radius: 0 0 4px 4px;
}

&--flexible {
box-sizing: border-box;
min-height: 100%;
}
}

.account-role {
@@ -240,3 +240,168 @@
border-radius: 0;
}
}

.page-header {
background: lighten($ui-base-color, 8%);
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
border-radius: 4px;
padding: 60px 15px;
text-align: center;
margin: 10px 0;

h1 {
color: $primary-text-color;
font-size: 36px;
line-height: 1.1;
font-weight: 700;
margin-bottom: 10px;
}

p {
font-size: 15px;
color: $darker-text-color;
}
}

.directory {
background: $ui-base-color;
border-radius: 0 0 4px 4px;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);

&__tag {
box-sizing: border-box;
margin-bottom: 10px;

a {
display: flex;
align-items: center;
justify-content: space-between;
background: $ui-base-color;
border-radius: 4px;
padding: 15px;
text-decoration: none;
color: inherit;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);

&:hover,
&:active,
&:focus {
background: lighten($ui-base-color, 8%);
}
}

&.active a {
background: $ui-highlight-color;
cursor: default;
}

h4 {
flex: 1 1 auto;
font-size: 18px;
font-weight: 700;
color: $primary-text-color;

.fa {
color: $darker-text-color;
}

small {
display: block;
font-weight: 400;
font-size: 15px;
margin-top: 8px;
color: $darker-text-color;
}
}

&.active h4 {
&,
.fa,
small {
color: $primary-text-color;
}
}

.avatar-stack {
flex: 0 0 auto;
width: (36px + 4px) * 3;
}

&.active .avatar-stack .account__avatar {
border-color: $ui-highlight-color;
}
}
}

.avatar-stack {
display: flex;
justify-content: flex-end;

.account__avatar {
flex: 0 0 auto;
width: 36px;
height: 36px;
border-radius: 50%;
position: relative;
margin-left: -10px;
border: 2px solid $ui-base-color;

&:nth-child(1) {
z-index: 1;
}

&:nth-child(2) {
z-index: 2;
}

&:nth-child(3) {
z-index: 3;
}
}
}

.accounts-table {
width: 100%;

.account {
padding: 0;
border: 0;
}

thead th {
text-align: center;
text-transform: uppercase;
color: $darker-text-color;
font-weight: 700;
padding: 10px;

&:first-child {
text-align: left;
}
}

tbody td {
padding: 15px 0;
vertical-align: middle;
border-bottom: 1px solid lighten($ui-base-color, 8%);
}

tbody tr:last-child td {
border-bottom: 0;
}

&__count {
width: 120px;
text-align: center;
font-size: 15px;
font-weight: 500;
color: $primary-text-color;

small {
display: block;
color: $darker-text-color;
font-weight: 400;
font-size: 14px;
}
}
}
@@ -43,11 +43,13 @@
# featured_collection_url :string
# fields :jsonb
# actor_type :string
# discoverable :boolean
#

class Account < ApplicationRecord
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
MIN_FOLLOWERS_DISCOVERY = 10

include AccountAssociations
include AccountAvatar
@@ -89,6 +91,10 @@ class Account < ApplicationRecord
scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
scope :searchable, -> { where(suspended: false).where(moved_to_account_id: nil) }
scope :discoverable, -> { where(silenced: false).where(discoverable: true).joins(:account_stat).where(AccountStat.arel_table[:followers_count].gteq(MIN_FOLLOWERS_DISCOVERY)) }
scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) }
scope :popular, -> { order('account_stats.followers_count desc') }
scope :by_recent_status, -> { order('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc') }

delegate :email,
:unconfirmed_email,
@@ -174,6 +180,40 @@ def keypair
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
end

def tags_as_strings=(tag_names)
tag_names.map! { |name| name.mb_chars.downcase }
tag_names.uniq!(&:to_s)

# Existing hashtags
hashtags_map = Tag.where(name: tag_names).each_with_object({}) { |tag, h| h[tag.name] = tag }

# Initialize not yet existing hashtags
tag_names.each do |name|
next if hashtags_map.key?(name)
hashtags_map[name.downcase] = Tag.new(name: name)
end

# Remove hashtags that are to be deleted
tags.each do |tag|
if hashtags_map.key?(tag.name)
hashtags_map.delete(tag.name)
else
transaction do
tags.delete(tag)
tag.decrement_count!(:accounts_count)
end
end
end

# Add hashtags that were so far missing
hashtags_map.each_value do |tag|
transaction do
tags << tag
tag.increment_count!(:accounts_count)
end
end
end

def fields
(self[:fields] || []).map { |f| Field.new(self, f) }
end
Oops, something went wrong.

0 comments on commit 73be8f3

Please sign in to comment.