Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
		
						commit
						3922b518f7
					
				@ -7,7 +7,7 @@ class Api::V1::CustomEmojisController < Api::BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  def index
 | 
					  def index
 | 
				
			||||||
    render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
 | 
					    render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
 | 
				
			||||||
      ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer)
 | 
					      ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false).includes(:category), each_serializer: REST::CustomEmojiSerializer)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import Overlay from 'react-overlays/lib/Overlay';
 | 
				
			|||||||
import classNames from 'classnames';
 | 
					import classNames from 'classnames';
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import detectPassiveEvents from 'detect-passive-events';
 | 
					import detectPassiveEvents from 'detect-passive-events';
 | 
				
			||||||
import { buildCustomEmojis } from '../../emoji/emoji';
 | 
					import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
 | 
					  emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
 | 
				
			||||||
@ -31,19 +31,6 @@ let EmojiPicker, Emoji; // load asynchronously
 | 
				
			|||||||
const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`;
 | 
					const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`;
 | 
				
			||||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
 | 
					const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const categoriesSort = [
 | 
					 | 
				
			||||||
  'recent',
 | 
					 | 
				
			||||||
  'custom',
 | 
					 | 
				
			||||||
  'people',
 | 
					 | 
				
			||||||
  'nature',
 | 
					 | 
				
			||||||
  'foods',
 | 
					 | 
				
			||||||
  'activity',
 | 
					 | 
				
			||||||
  'places',
 | 
					 | 
				
			||||||
  'objects',
 | 
					 | 
				
			||||||
  'symbols',
 | 
					 | 
				
			||||||
  'flags',
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ModifierPickerMenu extends React.PureComponent {
 | 
					class ModifierPickerMenu extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
@ -241,8 +228,23 @@ class EmojiPickerMenu extends React.PureComponent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const title = intl.formatMessage(messages.emoji);
 | 
					    const title = intl.formatMessage(messages.emoji);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { modifierOpen } = this.state;
 | 
					    const { modifierOpen } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const categoriesSort = [
 | 
				
			||||||
 | 
					      'recent',
 | 
				
			||||||
 | 
					      'people',
 | 
				
			||||||
 | 
					      'nature',
 | 
				
			||||||
 | 
					      'foods',
 | 
				
			||||||
 | 
					      'activity',
 | 
				
			||||||
 | 
					      'places',
 | 
				
			||||||
 | 
					      'objects',
 | 
				
			||||||
 | 
					      'symbols',
 | 
				
			||||||
 | 
					      'flags',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(custom_emojis)).sort());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
 | 
					      <div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
 | 
				
			||||||
        <EmojiPicker
 | 
					        <EmojiPicker
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestion
 | 
				
			|||||||
const mapStateToProps = state => ({
 | 
					const mapStateToProps = state => ({
 | 
				
			||||||
  results: state.getIn(['search', 'results']),
 | 
					  results: state.getIn(['search', 'results']),
 | 
				
			||||||
  suggestions: state.getIn(['suggestions', 'items']),
 | 
					  suggestions: state.getIn(['suggestions', 'items']),
 | 
				
			||||||
  searchTerm: state.getIn(['search', 'value']),
 | 
					  searchTerm: state.getIn(['search', 'searchTerm']),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapDispatchToProps = dispatch => ({
 | 
					const mapDispatchToProps = dispatch => ({
 | 
				
			||||||
 | 
				
			|||||||
@ -92,8 +92,11 @@ export const buildCustomEmojis = (customEmojis) => {
 | 
				
			|||||||
      keywords: [name],
 | 
					      keywords: [name],
 | 
				
			||||||
      imageUrl: url,
 | 
					      imageUrl: url,
 | 
				
			||||||
      custom: true,
 | 
					      custom: true,
 | 
				
			||||||
 | 
					      customCategory: emoji.get('category'),
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return emojis;
 | 
					  return emojis;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const categoriesFromEmojis = customEmojis => customEmojis.reduce((set, emoji) => set.add(emoji.get('category') ? `custom-${emoji.get('category')}` : 'custom'), new Set());
 | 
				
			||||||
 | 
				
			|||||||
@ -110,6 +110,11 @@ class ColumnsArea extends ImmutablePureComponent {
 | 
				
			|||||||
    // React-router does this for us, but too late, feeling laggy.
 | 
					    // React-router does this for us, but too late, feeling laggy.
 | 
				
			||||||
    document.querySelector(currentLinkSelector).classList.remove('active');
 | 
					    document.querySelector(currentLinkSelector).classList.remove('active');
 | 
				
			||||||
    document.querySelector(nextLinkSelector).classList.add('active');
 | 
					    document.querySelector(nextLinkSelector).classList.add('active');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!this.state.shouldAnimate && typeof this.pendingIndex === 'number') {
 | 
				
			||||||
 | 
					      this.context.router.history.push(getLink(this.pendingIndex));
 | 
				
			||||||
 | 
					      this.pendingIndex = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleAnimationEnd = () => {
 | 
					  handleAnimationEnd = () => {
 | 
				
			||||||
@ -160,7 +165,6 @@ class ColumnsArea extends ImmutablePureComponent {
 | 
				
			|||||||
    const { shouldAnimate } = this.state;
 | 
					    const { shouldAnimate } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const columnIndex = getIndex(this.context.router.history.location.pathname);
 | 
					    const columnIndex = getIndex(this.context.router.history.location.pathname);
 | 
				
			||||||
    this.pendingIndex = null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (singleColumn) {
 | 
					    if (singleColumn) {
 | 
				
			||||||
      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
 | 
					      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@
 | 
				
			|||||||
#  uri                :string
 | 
					#  uri                :string
 | 
				
			||||||
#  image_remote_url   :string
 | 
					#  image_remote_url   :string
 | 
				
			||||||
#  visible_in_picker  :boolean          default(TRUE), not null
 | 
					#  visible_in_picker  :boolean          default(TRUE), not null
 | 
				
			||||||
 | 
					#  category_id        :bigint(8)
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CustomEmoji < ApplicationRecord
 | 
					class CustomEmoji < ApplicationRecord
 | 
				
			||||||
@ -27,6 +28,7 @@ class CustomEmoji < ApplicationRecord
 | 
				
			|||||||
    :(#{SHORTCODE_RE_FRAGMENT}):
 | 
					    :(#{SHORTCODE_RE_FRAGMENT}):
 | 
				
			||||||
    (?=[^[:alnum:]:]|$)/x
 | 
					    (?=[^[:alnum:]:]|$)/x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
 | 
				
			||||||
  has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
 | 
					  has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce -strip' } }
 | 
					  has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce -strip' } }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								app/models/custom_emoji_category.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/models/custom_emoji_category.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# == Schema Information
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Table name: custom_emoji_categories
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  id         :bigint(8)        not null, primary key
 | 
				
			||||||
 | 
					#  name       :string
 | 
				
			||||||
 | 
					#  created_at :datetime         not null
 | 
				
			||||||
 | 
					#  updated_at :datetime         not null
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomEmojiCategory < ApplicationRecord
 | 
				
			||||||
 | 
					  has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@ -5,6 +5,8 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  attributes :shortcode, :url, :static_url, :visible_in_picker
 | 
					  attributes :shortcode, :url, :static_url, :visible_in_picker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  attribute :category, if: :category_loaded?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def url
 | 
					  def url
 | 
				
			||||||
    full_asset_url(object.image.url)
 | 
					    full_asset_url(object.image.url)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@ -12,4 +14,12 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer
 | 
				
			|||||||
  def static_url
 | 
					  def static_url
 | 
				
			||||||
    full_asset_url(object.image.url(:static))
 | 
					    full_asset_url(object.image.url(:static))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def category
 | 
				
			||||||
 | 
					    object.category.name
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def category_loaded?
 | 
				
			||||||
 | 
					    object.association(:category).loaded? && object.category.present?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					class CreateCustomEmojiCategories < ActiveRecord::Migration[5.2]
 | 
				
			||||||
 | 
					  def change
 | 
				
			||||||
 | 
					    create_table :custom_emoji_categories do |t|
 | 
				
			||||||
 | 
					      t.string :name, index: { unique: true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      t.timestamps
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					class AddCategoryIdToCustomEmojis < ActiveRecord::Migration[5.2]
 | 
				
			||||||
 | 
					  def change
 | 
				
			||||||
 | 
					    add_column :custom_emojis, :category_id, :bigint
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										10
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								db/schema.rb
									
									
									
									
									
								
							@ -10,7 +10,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
# It's strongly recommended that you check this file into your version control system.
 | 
					# It's strongly recommended that you check this file into your version control system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ActiveRecord::Schema.define(version: 2019_05_29_143559) do
 | 
					ActiveRecord::Schema.define(version: 2019_06_27_222826) do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # These are extensions that must be enabled in order to support this database
 | 
					  # These are extensions that must be enabled in order to support this database
 | 
				
			||||||
  enable_extension "plpgsql"
 | 
					  enable_extension "plpgsql"
 | 
				
			||||||
@ -218,6 +218,13 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do
 | 
				
			|||||||
    t.index ["uri"], name: "index_conversations_on_uri", unique: true
 | 
					    t.index ["uri"], name: "index_conversations_on_uri", unique: true
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  create_table "custom_emoji_categories", force: :cascade do |t|
 | 
				
			||||||
 | 
					    t.string "name"
 | 
				
			||||||
 | 
					    t.datetime "created_at", null: false
 | 
				
			||||||
 | 
					    t.datetime "updated_at", null: false
 | 
				
			||||||
 | 
					    t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  create_table "custom_emojis", force: :cascade do |t|
 | 
					  create_table "custom_emojis", force: :cascade do |t|
 | 
				
			||||||
    t.string "shortcode", default: "", null: false
 | 
					    t.string "shortcode", default: "", null: false
 | 
				
			||||||
    t.string "domain"
 | 
					    t.string "domain"
 | 
				
			||||||
@ -231,6 +238,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do
 | 
				
			|||||||
    t.string "uri"
 | 
					    t.string "uri"
 | 
				
			||||||
    t.string "image_remote_url"
 | 
					    t.string "image_remote_url"
 | 
				
			||||||
    t.boolean "visible_in_picker", default: true, null: false
 | 
					    t.boolean "visible_in_picker", default: true, null: false
 | 
				
			||||||
 | 
					    t.bigint "category_id"
 | 
				
			||||||
    t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
 | 
					    t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ module Mastodon
 | 
				
			|||||||
    option :suffix
 | 
					    option :suffix
 | 
				
			||||||
    option :overwrite, type: :boolean
 | 
					    option :overwrite, type: :boolean
 | 
				
			||||||
    option :unlisted, type: :boolean
 | 
					    option :unlisted, type: :boolean
 | 
				
			||||||
 | 
					    option :category
 | 
				
			||||||
    desc 'import PATH', 'Import emoji from a TAR GZIP archive at PATH'
 | 
					    desc 'import PATH', 'Import emoji from a TAR GZIP archive at PATH'
 | 
				
			||||||
    long_desc <<-LONG_DESC
 | 
					    long_desc <<-LONG_DESC
 | 
				
			||||||
      Imports custom emoji from a TAR GZIP archive specified by PATH.
 | 
					      Imports custom emoji from a TAR GZIP archive specified by PATH.
 | 
				
			||||||
@ -22,6 +23,9 @@ module Mastodon
 | 
				
			|||||||
      Existing emoji will be skipped unless the --overwrite option
 | 
					      Existing emoji will be skipped unless the --overwrite option
 | 
				
			||||||
      is provided, in which case they will be overwritten.
 | 
					      is provided, in which case they will be overwritten.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You can specifiy a --category under which the emojis will be
 | 
				
			||||||
 | 
					      grouped together.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      With the --prefix option, a prefix can be added to all
 | 
					      With the --prefix option, a prefix can be added to all
 | 
				
			||||||
      generated shortcodes. Likewise, the --suffix option controls
 | 
					      generated shortcodes. Likewise, the --suffix option controls
 | 
				
			||||||
      the suffix of all shortcodes.
 | 
					      the suffix of all shortcodes.
 | 
				
			||||||
@ -33,6 +37,7 @@ module Mastodon
 | 
				
			|||||||
      imported = 0
 | 
					      imported = 0
 | 
				
			||||||
      skipped  = 0
 | 
					      skipped  = 0
 | 
				
			||||||
      failed   = 0
 | 
					      failed   = 0
 | 
				
			||||||
 | 
					      category = options[:category] ? CustomEmojiCategory.find_or_create_by(name: options[:category]) : nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Gem::Package::TarReader.new(Zlib::GzipReader.open(path)) do |tar|
 | 
					      Gem::Package::TarReader.new(Zlib::GzipReader.open(path)) do |tar|
 | 
				
			||||||
        tar.each do |entry|
 | 
					        tar.each do |entry|
 | 
				
			||||||
@ -50,6 +55,7 @@ module Mastodon
 | 
				
			|||||||
          custom_emoji.image = StringIO.new(entry.read)
 | 
					          custom_emoji.image = StringIO.new(entry.read)
 | 
				
			||||||
          custom_emoji.image_file_name = File.basename(entry.full_name)
 | 
					          custom_emoji.image_file_name = File.basename(entry.full_name)
 | 
				
			||||||
          custom_emoji.visible_in_picker = !options[:unlisted]
 | 
					          custom_emoji.visible_in_picker = !options[:unlisted]
 | 
				
			||||||
 | 
					          custom_emoji.category = category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if custom_emoji.save
 | 
					          if custom_emoji.save
 | 
				
			||||||
            imported += 1
 | 
					            imported += 1
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								spec/fabricators/custom_emoji_category_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fabricators/custom_emoji_category_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					Fabricator(:custom_emoji_category) do
 | 
				
			||||||
 | 
					  name "MyString"
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										5
									
								
								spec/models/custom_emoji_category_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/custom_emoji_category_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RSpec.describe CustomEmojiCategory, type: :model do
 | 
				
			||||||
 | 
					  pending "add some examples to (or delete) #{__FILE__}"
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@ -3388,8 +3388,8 @@ elliptic@^6.0.0:
 | 
				
			|||||||
    minimalistic-crypto-utils "^1.0.0"
 | 
					    minimalistic-crypto-utils "^1.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emoji-mart@Gargron/emoji-mart#build:
 | 
					emoji-mart@Gargron/emoji-mart#build:
 | 
				
			||||||
  version "2.6.2"
 | 
					  version "2.6.3"
 | 
				
			||||||
  resolved "https://codeload.github.com/Gargron/emoji-mart/tar.gz/ff00dc470b5b2d9f145a6d6e977a54de5df2b4c9"
 | 
					  resolved "https://codeload.github.com/Gargron/emoji-mart/tar.gz/934f314fd8322276765066e8a2a6be5bac61b1cf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emoji-regex@^7.0.1, emoji-regex@^7.0.2:
 | 
					emoji-regex@^7.0.1, emoji-regex@^7.0.2:
 | 
				
			||||||
  version "7.0.3"
 | 
					  version "7.0.3"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user