From 82d2069c75071138f582503c39b0996cc4cdff11 Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Tue, 6 Aug 2019 22:33:03 +0900 Subject: [PATCH 1/9] Bump faker from 1.9.6 to 2.1.0 and update faker api (#11489) * Bump faker from 1.9.6 to 2.1.0 Bumps [faker](https://github.com/stympy/faker) from 1.9.6 to 2.1.0. - [Release notes](https://github.com/stympy/faker/releases) - [Changelog](https://github.com/stympy/faker/blob/master/CHANGELOG.md) - [Commits](https://github.com/stympy/faker/compare/1.9.6...v2.1.0) Signed-off-by: dependabot-preview[bot] * Use faker api v2 https://github.com/stympy/faker/releases/tag/2.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- .../identity_proofs_controller_spec.rb | 4 ++-- spec/fabricators/account_fabricator.rb | 2 +- .../account_identity_proof_fabricator.rb | 2 +- spec/models/account_spec.rb | 18 +++++++++--------- spec/models/report_spec.rb | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Gemfile b/Gemfile index b0759d9e1..263be0ac3 100644 --- a/Gemfile +++ b/Gemfile @@ -112,7 +112,7 @@ end group :test do gem 'capybara', '~> 3.28' gem 'climate_control', '~> 0.2' - gem 'faker', '~> 1.9' + gem 'faker', '~> 2.1' gem 'microformats', '~> 4.1' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index a353304e2..f185f3fa5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -229,7 +229,7 @@ GEM tzinfo excon (0.62.0) fabrication (2.20.2) - faker (1.9.6) + faker (2.1.0) i18n (>= 0.7) faraday (0.15.0) multipart-post (>= 1.2, < 3) @@ -695,7 +695,7 @@ DEPENDENCIES doorkeeper (~> 5.1) dotenv-rails (~> 2.7) fabrication (~> 2.20) - faker (~> 1.9) + faker (~> 2.1) fast_blank (~> 1.0) fastimage fog-core (<= 2.1.0) diff --git a/spec/controllers/settings/identity_proofs_controller_spec.rb b/spec/controllers/settings/identity_proofs_controller_spec.rb index 2a0f91088..261e980d4 100644 --- a/spec/controllers/settings/identity_proofs_controller_spec.rb +++ b/spec/controllers/settings/identity_proofs_controller_spec.rb @@ -8,8 +8,8 @@ describe Settings::IdentityProofsController do let(:valid_token) { '1'*66 } let(:kbname) { 'kbuser' } let(:provider) { 'keybase' } - let(:findable_id) { Faker::Number.number(5) } - let(:unfindable_id) { Faker::Number.number(5) } + let(:findable_id) { Faker::Number.number(digits: 5) } + let(:unfindable_id) { Faker::Number.number(digits: 5) } let(:new_proof_params) do { provider: provider, provider_username: kbname, token: valid_token, username: user.account.username } end diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index f12464ef3..ab900c5fa 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -4,7 +4,7 @@ private_key = keypair.to_pem Fabricator(:account) do transient :suspended, :silenced - username { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } } + username { sequence(:username) { |i| "#{Faker::Internet.user_name(separators: %w(_))}#{i}" } } last_webfingered_at { Time.now.utc } public_key { public_key } private_key { private_key } diff --git a/spec/fabricators/account_identity_proof_fabricator.rb b/spec/fabricators/account_identity_proof_fabricator.rb index 94f40dfd6..7b932fa96 100644 --- a/spec/fabricators/account_identity_proof_fabricator.rb +++ b/spec/fabricators/account_identity_proof_fabricator.rb @@ -1,7 +1,7 @@ Fabricator(:account_identity_proof) do account provider 'keybase' - provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(15)}" } } + provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(number: 15)}" } } token { sequence(:token) { |i| "#{i}#{Faker::Crypto.sha1()*2}"[0..65] } } verified false live false diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 3a17d540a..3eec464bd 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -607,19 +607,19 @@ RSpec.describe Account, type: :model do end it 'is invalid if the username is longer then 30 characters' do - account = Fabricate.build(:account, username: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31)) account.valid? expect(account).to model_have_error_on_field(:username) end it 'is invalid if the display name is longer than 30 characters' do - account = Fabricate.build(:account, display_name: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31)) account.valid? expect(account).to model_have_error_on_field(:display_name) end it 'is invalid if the note is longer than 500 characters' do - account = Fabricate.build(:account, note: Faker::Lorem.characters(501)) + account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501)) account.valid? expect(account).to model_have_error_on_field(:note) end @@ -653,19 +653,19 @@ RSpec.describe Account, type: :model do end it 'is valid even if the username is longer then 30 characters' do - account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31)) account.valid? expect(account).not_to model_have_error_on_field(:username) end it 'is valid even if the display name is longer than 30 characters' do - account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31)) account.valid? expect(account).not_to model_have_error_on_field(:display_name) end it 'is valid even if the note is longer than 500 characters' do - account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(501)) + account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501)) account.valid? expect(account).not_to model_have_error_on_field(:note) end @@ -804,7 +804,7 @@ RSpec.describe Account, type: :model do context 'when is local' do # Test disabled because test environment omits autogenerating keys for performance xit 'generates keys' do - account = Account.create!(domain: nil, username: Faker::Internet.user_name(nil, ['_'])) + account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_'])) expect(account.keypair.private?).to eq true end end @@ -812,12 +812,12 @@ RSpec.describe Account, type: :model do context 'when is remote' do it 'does not generate keys' do key = OpenSSL::PKey::RSA.new(1024).public_key - account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(nil, ['_']), public_key: key.to_pem) + account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem) expect(account.keypair.params).to eq key.params end it 'normalizes domain' do - account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(nil, ['_'])) + account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_'])) expect(account.domain).to eq 'xn--r9j5b5b' end end diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index a0cd0800d..312954c9d 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -125,7 +125,7 @@ describe Report do end it 'is invalid if comment is longer than 1000 characters' do - report = Fabricate.build(:report, comment: Faker::Lorem.characters(1001)) + report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001)) report.valid? expect(report).to model_have_error_on_field(:comment) end From 9072fe5ab6464cc9c7a871d388464c7afcf41cd0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Aug 2019 17:57:52 +0200 Subject: [PATCH 2/9] Add trends UI with admin and user settings (#11502) --- .../settings/preferences_controller.rb | 1 + app/javascript/mastodon/actions/trends.js | 32 ++++++++++++++ .../getting_started/components/trends.js | 43 +++++++++++++++++++ .../containers/trends_container.js | 13 ++++++ .../features/getting_started/index.js | 5 ++- .../ui/components/navigation_panel.js | 6 ++- app/javascript/mastodon/initial_state.js | 1 + app/javascript/mastodon/reducers/index.js | 2 + app/javascript/mastodon/reducers/settings.js | 4 ++ app/javascript/mastodon/reducers/trends.js | 23 ++++++++++ .../styles/mastodon/components.scss | 38 ++++++++++++---- app/lib/user_settings_decorator.rb | 5 +++ app/models/form/admin_settings.rb | 2 + app/models/trending_tags.rb | 4 ++ app/models/user.rb | 3 +- app/serializers/initial_state_serializer.rb | 2 + app/views/admin/settings/edit.html.haml | 3 ++ .../preferences/appearance/show.html.haml | 5 +++ config/locales/en.yml | 8 +++- config/locales/simple_form.en.yml | 1 + config/settings.yml | 1 + 21 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 app/javascript/mastodon/actions/trends.js create mode 100644 app/javascript/mastodon/features/getting_started/components/trends.js create mode 100644 app/javascript/mastodon/features/getting_started/containers/trends_container.js create mode 100644 app/javascript/mastodon/reducers/trends.js diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index d548072a8..edf29947b 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -56,6 +56,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_advanced_layout, :setting_use_blurhash, :setting_use_pending_items, + :setting_trends, notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag), interactions: %i(must_be_follower must_be_following must_be_following_dm) ) diff --git a/app/javascript/mastodon/actions/trends.js b/app/javascript/mastodon/actions/trends.js new file mode 100644 index 000000000..853e4f60a --- /dev/null +++ b/app/javascript/mastodon/actions/trends.js @@ -0,0 +1,32 @@ +import api from '../api'; + +export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST'; +export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS'; +export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL'; + +export const fetchTrends = () => (dispatch, getState) => { + dispatch(fetchTrendsRequest()); + + api(getState) + .get('/api/v1/trends') + .then(({ data }) => dispatch(fetchTrendsSuccess(data))) + .catch(err => dispatch(fetchTrendsFail(err))); +}; + +export const fetchTrendsRequest = () => ({ + type: TRENDS_FETCH_REQUEST, + skipLoading: true, +}); + +export const fetchTrendsSuccess = trends => ({ + type: TRENDS_FETCH_SUCCESS, + trends, + skipLoading: true, +}); + +export const fetchTrendsFail = error => ({ + type: TRENDS_FETCH_FAIL, + error, + skipLoading: true, + skipAlert: true, +}); diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.js new file mode 100644 index 000000000..1dcacc8b3 --- /dev/null +++ b/app/javascript/mastodon/features/getting_started/components/trends.js @@ -0,0 +1,43 @@ +import React from 'react'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Hashtag from 'mastodon/components/hashtag'; + +export default class Trends extends ImmutablePureComponent { + + static defaultProps = { + loading: false, + }; + + static propTypes = { + trends: ImmutablePropTypes.list, + fetchTrends: PropTypes.func.isRequired, + }; + + componentDidMount () { + this.props.fetchTrends(); + this.refreshInterval = setInterval(() => this.props.fetchTrends(), 36000); + } + + componentWillUnmount () { + if (this.refreshInterval) { + clearInterval(this.refreshInterval); + } + } + + render () { + const { trends } = this.props; + + if (!trends || trends.isEmpty()) { + return null; + } + + return ( +
+ {trends.take(3).map(hashtag => )} +
+ ); + } + +} diff --git a/app/javascript/mastodon/features/getting_started/containers/trends_container.js b/app/javascript/mastodon/features/getting_started/containers/trends_container.js new file mode 100644 index 000000000..1df3fb4fe --- /dev/null +++ b/app/javascript/mastodon/features/getting_started/containers/trends_container.js @@ -0,0 +1,13 @@ +import { connect } from 'react-redux'; +import { fetchTrends } from '../../../actions/trends'; +import Trends from '../components/trends'; + +const mapStateToProps = state => ({ + trends: state.getIn(['trends', 'items']), +}); + +const mapDispatchToProps = dispatch => ({ + fetchTrends: () => dispatch(fetchTrends()), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Trends); diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 791f22d47..6a122a750 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -7,12 +7,13 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { me, profile_directory } from '../../initial_state'; +import { me, profile_directory, showTrends } from '../../initial_state'; import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { List as ImmutableList } from 'immutable'; import NavigationBar from '../compose/components/navigation_bar'; import Icon from 'mastodon/components/icon'; import LinkFooter from 'mastodon/features/ui/components/link_footer'; +import TrendsContainer from './containers/trends_container'; const messages = defineMessages({ home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, @@ -168,6 +169,8 @@ class GettingStarted extends ImmutablePureComponent { + + {multiColumn && showTrends && } ); } diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js index ef3ad2e09..64a40a9da 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.js +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js @@ -2,10 +2,11 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import Icon from 'mastodon/components/icon'; -import { profile_directory } from 'mastodon/initial_state'; +import { profile_directory, showTrends } from 'mastodon/initial_state'; import NotificationsCounterIcon from './notifications_counter_icon'; import FollowRequestsNavLink from './follow_requests_nav_link'; import ListPanel from './list_panel'; +import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container'; const NavigationPanel = () => (
@@ -25,6 +26,9 @@ const NavigationPanel = () => ( {!!profile_directory && } + + {showTrends &&
} + {showTrends && }
); diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index cb2ccc7c4..38e7b0595 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -22,5 +22,6 @@ export const isStaff = getMeta('is_staff'); export const forceSingleColumn = !getMeta('advanced_layout'); export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); +export const showTrends = getMeta('trends'); export default initialState; diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index 981ad8e64..3b60878eb 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -31,6 +31,7 @@ import conversations from './conversations'; import suggestions from './suggestions'; import polls from './polls'; import identity_proofs from './identity_proofs'; +import trends from './trends'; const reducers = { dropdown_menu, @@ -65,6 +66,7 @@ const reducers = { conversations, suggestions, polls, + trends, }; export default combineReducers(reducers); diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index 033bfc999..793a99f8f 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -12,6 +12,10 @@ const initialState = ImmutableMap({ skinTone: 1, + trends: ImmutableMap({ + show: true, + }), + home: ImmutableMap({ shows: ImmutableMap({ reblog: true, diff --git a/app/javascript/mastodon/reducers/trends.js b/app/javascript/mastodon/reducers/trends.js new file mode 100644 index 000000000..5cecc8fca --- /dev/null +++ b/app/javascript/mastodon/reducers/trends.js @@ -0,0 +1,23 @@ +import { TRENDS_FETCH_REQUEST, TRENDS_FETCH_SUCCESS, TRENDS_FETCH_FAIL } from '../actions/trends'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; + +const initialState = ImmutableMap({ + items: ImmutableList(), + isLoading: false, +}); + +export default function trendsReducer(state = initialState, action) { + switch(action.type) { + case TRENDS_FETCH_REQUEST: + return state.set('isLoading', true); + case TRENDS_FETCH_SUCCESS: + return state.withMutations(map => { + map.set('items', fromJS(action.trends)); + map.set('isLoading', false); + }); + case TRENDS_FETCH_FAIL: + return state.set('isLoading', false); + default: + return state; + } +}; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f02458ded..8de72d72e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2212,7 +2212,6 @@ a.account__display-name { } .getting-started__wrapper, - .getting-started__trends, .search { margin-bottom: 10px; } @@ -2319,13 +2318,24 @@ a.account__display-name { margin-bottom: 10px; height: calc(100% - 20px); overflow-y: auto; + display: flex; + flex-direction: column; + + & > a { + flex: 0 0 auto; + } hr { + flex: 0 0 auto; border: 0; background: transparent; border-top: 1px solid lighten($ui-base-color, 4%); margin: 10px 0; } + + .flex-spacer { + background: transparent; + } } .drawer__pager { @@ -2717,8 +2727,10 @@ a.account__display-name { } &__trends { - background: $ui-base-color; flex: 0 1 auto; + opacity: 1; + animation: fade 150ms linear; + margin-top: 10px; @media screen and (max-height: 810px) { .trends__item:nth-child(3) { @@ -2735,11 +2747,15 @@ a.account__display-name { @media screen and (max-height: 670px) { display: none; } - } - &__scrollable { - max-height: 100%; - overflow-y: auto; + .trends__item { + border-bottom: 0; + padding: 10px; + + &__current { + color: $darker-text-color; + } + } } } @@ -5968,7 +5984,8 @@ noscript { font-size: 24px; line-height: 36px; font-weight: 500; - text-align: center; + text-align: right; + padding-right: 15px; color: $secondary-text-color; } @@ -5976,7 +5993,12 @@ noscript { flex: 0 0 auto; width: 50px; - path { + path:first-child { + fill: rgba($highlight-text-color, 0.25) !important; + fill-opacity: 1 !important; + } + + path:last-child { stroke: lighten($highlight-text-color, 6%) !important; } } diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 9ae9986c2..3568a3e11 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -36,6 +36,7 @@ class UserSettingsDecorator user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout') user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash') user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items') + user.settings['trends'] = trends_preference if change?('setting_trends') end def merged_notification_emails @@ -122,6 +123,10 @@ class UserSettingsDecorator boolean_cast_setting 'setting_use_pending_items' end + def trends_preference + boolean_cast_setting 'setting_trends' + end + def boolean_cast_setting(key) ActiveModel::Type::Boolean.new.cast(settings[key]) end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 2c03c88a8..051268375 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -29,6 +29,7 @@ class Form::AdminSettings hero mascot spam_check_enabled + trends ).freeze BOOLEAN_KEYS = %i( @@ -41,6 +42,7 @@ class Form::AdminSettings preview_sensitive_media profile_directory spam_check_enabled + trends ).freeze UPLOAD_KEYS = %i( diff --git a/app/models/trending_tags.rb b/app/models/trending_tags.rb index e9b9b25e3..0a7e2feac 100644 --- a/app/models/trending_tags.rb +++ b/app/models/trending_tags.rb @@ -66,6 +66,10 @@ class TrendingTags end def request_review!(tag) + return unless Setting.trends + + tag.touch(:requested_review_at) + User.staff.includes(:account).find_each { |u| AdminMailer.new_trending_tag(u.account, tag).deliver_later! if u.allows_trending_tag_emails? } end end diff --git a/app/models/user.rb b/app/models/user.rb index b83e26af3..a4a20d975 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -107,7 +107,8 @@ class User < ApplicationRecord delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network, :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, - :advanced_layout, :use_blurhash, :use_pending_items, to: :settings, prefix: :setting, allow_nil: false + :advanced_layout, :use_blurhash, :use_pending_items, :trends, + to: :settings, prefix: :setting, allow_nil: false attr_reader :invite_code attr_writer :external diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 7e5d3eda9..c92c5e606 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -20,6 +20,7 @@ class InitialStateSerializer < ActiveModel::Serializer invites_enabled: Setting.min_invite_role == 'user', mascot: instance_presenter.mascot&.file&.url, profile_directory: Setting.profile_directory, + trends: Setting.trends, } if object.current_account @@ -35,6 +36,7 @@ class InitialStateSerializer < ActiveModel::Serializer store[:use_blurhash] = object.current_account.user.setting_use_blurhash store[:use_pending_items] = object.current_account.user.setting_use_pending_items store[:is_staff] = object.current_account.user.staff? + store[:trends] = Setting.trends && object.current_account.user.setting_trends end store diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index 1e2ed3f77..28c0ece15 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -68,6 +68,9 @@ .fields-group = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html') + .fields-group + = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html') + .fields-group = f.input :spam_check_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.spam_check_enabled.title'), hint: t('admin.settings.spam_check_enabled.desc_html') diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index e279a61c4..d6ee1933f 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -25,6 +25,11 @@ = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label + %h4= t 'appearance.discovery' + + .fields-group + = f.input :setting_trends, as: :boolean, wrapper: :with_label + %h4= t 'appearance.confirmation_dialogs' .fields-group diff --git a/config/locales/en.yml b/config/locales/en.yml index 9b62aac3a..67c392662 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -460,8 +460,8 @@ en: title: Custom terms of service site_title: Server name spam_check_enabled: - desc_html: Mastodon can auto-silence and auto-report accounts based on measures such as detecting accounts who send repeated unsolicited messages. There may be false positives. - title: Anti-spam + desc_html: Mastodon can auto-silence and auto-report accounts that send repeated unsolicited messages. There may be false positives. + title: Anti-spam automation thumbnail: desc_html: Used for previews via OpenGraph and API. 1200x630px recommended title: Server thumbnail @@ -469,6 +469,9 @@ en: desc_html: Display public timeline on landing page title: Timeline preview title: Site settings + trends: + desc_html: Publicly display previously reviewed hashtags that are currently trending + title: Trending hashtags statuses: back_to_account: Back to account page batch: @@ -514,6 +517,7 @@ en: advanced_web_interface_hint: 'If you want to make use of your entire screen width, the advanced web interface allows you to configure many different columns to see as much information at the same time as you want: Home, notifications, federated timeline, any number of lists and hashtags.' animations_and_accessibility: Animations and accessibility confirmation_dialogs: Confirmation dialogs + discovery: Discovery sensitive_content: Sensitive content application_mailer: notification_preferences: Change e-mail preferences diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 6fdfc9d7b..e15d5904f 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -114,6 +114,7 @@ en: setting_show_application: Disclose application used to send toots setting_system_font_ui: Use system's default font setting_theme: Site theme + setting_trends: Show today's trends setting_unfollow_modal: Show confirmation dialog before unfollowing someone setting_use_blurhash: Show colorful gradients for hidden media setting_use_pending_items: Slow mode diff --git a/config/settings.yml b/config/settings.yml index 10180201f..4e5eefb59 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -34,6 +34,7 @@ defaults: &defaults advanced_layout: false use_blurhash: true use_pending_items: false + trends: true notification_emails: follow: false reblog: false From dd38c280a50a8feb70ad341c3561fe2f87c8cf3d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Aug 2019 19:40:06 +0200 Subject: [PATCH 3/9] Fix admin dashboard missing latest features (#11505) Fix redis-namespace deprecation warning about administrative commands --- app/controllers/admin/dashboard_controller.rb | 11 ++++++++++- app/views/admin/dashboard/index.html.haml | 6 ++++++ config/locales/en.yml | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 70afdedd7..ab56065e0 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -28,9 +28,12 @@ module Admin @pam_enabled = ENV['PAM_ENABLED'] == 'true' @hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' @trending_hashtags = TrendingTags.get(10, filtered: false) + @authorized_fetch = authorized_fetch_mode? + @whitelist_enabled = whitelist_mode? @profile_directory = Setting.profile_directory @timeline_preview = Setting.timeline_preview @spam_check_enabled = Setting.spam_check_enabled + @trends_enabled = Setting.trends end private @@ -40,7 +43,13 @@ module Admin end def redis_info - @redis_info ||= Redis.current.info + @redis_info ||= begin + if Redis.current.is_a?(Redis::Namespace) + Redis.current.redis.info + else + Redis.current.info + end + end end end end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 910896075..f567b81e8 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -49,6 +49,8 @@ = feature_hint(link_to(t('admin.dashboard.feature_profile_directory'), edit_admin_settings_path), @profile_directory) %li = feature_hint(link_to(t('admin.dashboard.feature_timeline_preview'), edit_admin_settings_path), @timeline_preview) + %li + = feature_hint(link_to(t('admin.dashboard.trends'), edit_admin_settings_path), @trends_enabled) %li = feature_hint(link_to(t('admin.dashboard.feature_relay'), admin_relays_path), @relay_enabled) %li @@ -90,6 +92,10 @@ = feature_hint(t('admin.dashboard.search'), @search_enabled) %li = feature_hint(t('admin.dashboard.single_user_mode'), @single_user_mode) + %li + = feature_hint(t('admin.dashboard.authorized_fetch_mode'), @authorized_fetch) + %li + = feature_hint(t('admin.dashboard.whitelist_mode'), @whitelist_mode) %li = feature_hint('LDAP', @ldap_enabled) %li diff --git a/config/locales/en.yml b/config/locales/en.yml index 67c392662..333d4f172 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -247,6 +247,7 @@ en: updated_msg: Emoji successfully updated! upload: Upload dashboard: + authorized_fetch_mode: Authorized fetch mode backlog: backlogged jobs config: Configuration feature_deletions: Account deletions @@ -270,6 +271,7 @@ en: week_interactions: interactions this week week_users_active: active this week week_users_new: users this week + whitelist_mode: Whitelist mode domain_allows: add_new: Whitelist domain created_msg: Domain has been successfully whitelisted @@ -565,6 +567,7 @@ en: status: account_status: Account status confirming: Waiting for e-mail confirmation to be completed. + functional: Your account is fully operational. pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. trouble_logging_in: Trouble logging in? authorize_follow: From a8958d07e222f2aa6867e8a5bf93cce4535fd3ce Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 7 Aug 2019 10:00:58 +0200 Subject: [PATCH 4/9] Fix featured tags not finding the right tag on save (#11504) Regression from f371b32 --- app/javascript/styles/mastodon/widgets.scss | 7 ++++++- app/models/featured_tag.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss index acaf5b024..8c30bc57c 100644 --- a/app/javascript/styles/mastodon/widgets.scss +++ b/app/javascript/styles/mastodon/widgets.scss @@ -324,7 +324,8 @@ &.active h4 { &, .fa, - small { + small, + .trends__item__current { color: $primary-text-color; } } @@ -337,6 +338,10 @@ &.active .avatar-stack .account__avatar { border-color: $ui-highlight-color; } + + .trends__item__current { + padding-right: 0; + } } } diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb index d06ae26a8..e02ae0705 100644 --- a/app/models/featured_tag.rb +++ b/app/models/featured_tag.rb @@ -23,7 +23,7 @@ class FeaturedTag < ApplicationRecord validate :validate_featured_tags_limit, on: :create def name=(str) - self.tag = Tag.find_or_initialize_by(name: str.strip.delete('#').mb_chars.downcase.to_s) + self.tag = Tag.find_or_create_by_names(str.strip)&.first end def increment(timestamp) From 5e35aa82802b09a63d4625fa9c1837ad75178553 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 7 Aug 2019 10:01:19 +0200 Subject: [PATCH 5/9] Fix non-lowercase hashtags not being picked up by the streaming API (#11508) Regression from f371b32 Fix hashtag links always being lowercase --- app/javascript/mastodon/components/status_content.js | 2 +- app/lib/formatter.rb | 2 +- app/services/batched_remove_status_service.rb | 4 ++-- app/services/fan_out_on_write_service.rb | 4 ++-- app/services/remove_status_service.rb | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 549de95fc..76117f1d9 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -112,7 +112,7 @@ export default class StatusContent extends React.PureComponent { } onHashtagClick = (hashtag, e) => { - hashtag = hashtag.replace(/^#/, '').toLowerCase(); + hashtag = hashtag.replace(/^#/, ''); if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 65059efa0..b5f42305f 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -294,7 +294,7 @@ class Formatter end def hashtag_html(tag) - "##{encode(tag)}" + "##{encode(tag)}" end def mention_html(account) diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index 27dc460a6..6df8d4769 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -80,8 +80,8 @@ class BatchedRemoveStatusService < BaseService end @tags[status.id].each do |hashtag| - redis.publish("timeline:hashtag:#{hashtag}", payload) - redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local? + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", payload) + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", payload) if status.local? end end end diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index f3e9c855d..95336ef45 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -72,8 +72,8 @@ class FanOutOnWriteService < BaseService Rails.logger.debug "Delivering status #{status.id} to hashtags" status.tags.pluck(:name).each do |hashtag| - Redis.current.publish("timeline:hashtag:#{hashtag}", @payload) - Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local? + Redis.current.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload) + Redis.current.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if status.local? end end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index a69fce8b8..91c934181 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -124,8 +124,8 @@ class RemoveStatusService < BaseService return unless @status.public_visibility? @tags.each do |hashtag| - redis.publish("timeline:hashtag:#{hashtag}", @payload) - redis.publish("timeline:hashtag:#{hashtag}:local", @payload) if @status.local? + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload) + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if @status.local? end end From ac33f1aedd9a6c72c6c176afb1f5d62a1ce5d44d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 7 Aug 2019 10:01:55 +0200 Subject: [PATCH 6/9] Fix account tags not being saved correctly (#11507) * Fix account tags not being saved correctly Regression from f371b32 Fix Tag#discoverable not returning tags where listable is nil instead of true Add notice when saving hashtags in admin UI Change public hashtag and directory pages to return 404 for forbidden tags * Remove unused locale string --- app/controllers/admin/tags_controller.rb | 2 +- app/controllers/directories_controller.rb | 2 +- app/controllers/tags_controller.rb | 2 +- app/models/account.rb | 12 +----------- app/models/tag.rb | 3 ++- config/locales/en.yml | 1 + 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index 0e9dda302..ed271aedc 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -17,7 +17,7 @@ module Admin authorize @tag, :update? if @tag.update(tag_params.merge(reviewed_at: Time.now.utc)) - redirect_to admin_tag_path(@tag.id) + redirect_to admin_tag_path(@tag.id), notice: I18n.t('admin.tags.updated_msg') else render :show end diff --git a/app/controllers/directories_controller.rb b/app/controllers/directories_controller.rb index d2ef76f06..a5c47b515 100644 --- a/app/controllers/directories_controller.rb +++ b/app/controllers/directories_controller.rb @@ -25,7 +25,7 @@ class DirectoriesController < ApplicationController end def set_tag - @tag = Tag.discoverable.find_by!(name: params[:id].downcase) + @tag = Tag.discoverable.find_normalized!(params[:id]) end def set_tags diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 3cd2d9e20..5a6fcc8fd 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -47,7 +47,7 @@ class TagsController < ApplicationController private def set_tag - @tag = Tag.find_normalized!(params[:id]) + @tag = Tag.usable.find_normalized!(params[:id]) end def set_body_classes diff --git a/app/models/account.rb b/app/models/account.rb index ccd116d6e..b205c8c9e 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -227,17 +227,7 @@ class Account < ApplicationRecord end def tags_as_strings=(tag_names) - tag_names.map! { |name| name.mb_chars.downcase.to_s } - tag_names.uniq! - - # 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] = Tag.new(name: name) - end + hashtags_map = Tag.find_or_create_by_names(tag_names).each_with_object({}) { |tag, h| h[tag.name] = tag } # Remove hashtags that are to be deleted tags.each do |tag| diff --git a/app/models/tag.rb b/app/models/tag.rb index 6a02581fa..e2fe91da1 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -31,7 +31,8 @@ class Tag < ApplicationRecord scope :reviewed, -> { where.not(reviewed_at: nil) } scope :pending_review, -> { where(reviewed_at: nil).where.not(requested_review_at: nil) } - scope :discoverable, -> { where.not(listable: false).joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) } + scope :usable, -> { where(usable: [true, nil]) } + scope :discoverable, -> { where(listable: [true, nil]).joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) } scope :most_used, ->(account) { joins(:statuses).where(statuses: { account: account }).group(:id).order(Arel.sql('count(*) desc')) } delegate :accounts_count, diff --git a/config/locales/en.yml b/config/locales/en.yml index 333d4f172..20baf634e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -496,6 +496,7 @@ en: title: Hashtags trending_right_now: Trending right now unique_uses_today: "%{count} posting today" + updated_msg: Hashtag settings updated successfully title: Administration warning_presets: add_new: Add new From 396b8cdd0f071fab85b09855f882b19c07cccd44 Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 7 Aug 2019 13:58:53 +0200 Subject: [PATCH 7/9] Improve focus handling with dropdown menus (#11511) - Focus first item when activated via keyboard - When the dropdown menu closes, give back the focus to the actual element which was focused prior to opening the menu --- .../mastodon/components/dropdown_menu.js | 42 +++++++++++++++---- .../mastodon/components/icon_button.js | 9 ++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index 9937d0f88..d423378c1 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -45,7 +45,6 @@ class DropdownMenu extends React.PureComponent { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - this.activeElement = document.activeElement; if (this.focusedItem && this.props.openedViaKeyboard) { this.focusedItem.focus(); } @@ -56,9 +55,6 @@ class DropdownMenu extends React.PureComponent { document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('keydown', this.handleKeyDown, false); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - if (this.activeElement) { - this.activeElement.focus(); - } } setRef = c => { @@ -117,7 +113,7 @@ class DropdownMenu extends React.PureComponent { } } - handleItemKeyUp = e => { + handleItemKeyPress = e => { if (e.key === 'Enter' || e.key === ' ') { this.handleClick(e); } @@ -147,7 +143,7 @@ class DropdownMenu extends React.PureComponent { return (
  • - + {text}
  • @@ -214,15 +210,44 @@ export default class Dropdown extends React.PureComponent { } else { const { top } = target.getBoundingClientRect(); const placement = top * 2 < innerHeight ? 'bottom' : 'top'; - this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click'); } } handleClose = () => { + if (this.activeElement) { + this.activeElement.focus(); + this.activeElement = null; + } this.props.onClose(this.state.id); } + handleMouseDown = () => { + if (!this.state.open) { + this.activeElement = document.activeElement; + } + } + + handleButtonKeyDown = (e) => { + switch(e.key) { + case ' ': + case 'Enter': + this.handleMouseDown(); + break; + } + } + + handleKeyPress = (e) => { + switch(e.key) { + case ' ': + case 'Enter': + this.handleClick(e); + e.stopPropagation(); + e.preventDefault(); + break; + } + } + handleItemClick = e => { const i = Number(e.currentTarget.getAttribute('data-index')); const { action, to } = this.props.items[i]; @@ -266,6 +291,9 @@ export default class Dropdown extends React.PureComponent { size={size} ref={this.setTargetRef} onClick={this.handleClick} + onMouseDown={this.handleMouseDown} + onKeyDown={this.handleButtonKeyDown} + onKeyPress={this.handleKeyPress} /> diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index a727359e9..401675052 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -14,6 +14,7 @@ export default class IconButton extends React.PureComponent { onClick: PropTypes.func, onMouseDown: PropTypes.func, onKeyDown: PropTypes.func, + onKeyPress: PropTypes.func, size: PropTypes.number, active: PropTypes.bool, pressed: PropTypes.bool, @@ -44,6 +45,12 @@ export default class IconButton extends React.PureComponent { } } + handleKeyPress = (e) => { + if (this.props.onKeyPress && !this.props.disabled) { + this.props.onKeyPress(e); + } + } + handleMouseDown = (e) => { if (!this.props.disabled && this.props.onMouseDown) { this.props.onMouseDown(e); @@ -100,6 +107,7 @@ export default class IconButton extends React.PureComponent { onClick={this.handleClick} onMouseDown={this.handleMouseDown} onKeyDown={this.handleKeyDown} + onKeyPress={this.handleKeyPress} style={style} tabIndex={tabIndex} disabled={disabled} @@ -121,6 +129,7 @@ export default class IconButton extends React.PureComponent { onClick={this.handleClick} onMouseDown={this.handleMouseDown} onKeyDown={this.handleKeyDown} + onKeyPress={this.handleKeyPress} style={style} tabIndex={tabIndex} disabled={disabled} From e8e980cdac9d1c3a81d9f30412f6de4cd021c225 Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 7 Aug 2019 13:58:53 +0200 Subject: [PATCH 8/9] [Glitch] Improve focus handling with dropdown menus Port 396b8cdd0f071fab85b09855f882b19c07cccd44 to glitch-soc --- .../glitch/components/dropdown_menu.js | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js index f29b824d5..39d7ba50c 100644 --- a/app/javascript/flavours/glitch/components/dropdown_menu.js +++ b/app/javascript/flavours/glitch/components/dropdown_menu.js @@ -45,7 +45,6 @@ class DropdownMenu extends React.PureComponent { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - this.activeElement = document.activeElement; if (this.focusedItem && this.props.openedViaKeyboard) { this.focusedItem.focus(); } @@ -56,9 +55,6 @@ class DropdownMenu extends React.PureComponent { document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('keydown', this.handleKeyDown, false); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - if (this.activeElement) { - this.activeElement.focus(); - } } setRef = c => { @@ -117,7 +113,7 @@ class DropdownMenu extends React.PureComponent { } } - handleItemKeyUp = e => { + handleItemKeyPress = e => { if (e.key === 'Enter' || e.key === ' ') { this.handleClick(e); } @@ -147,7 +143,7 @@ class DropdownMenu extends React.PureComponent { return (
  • - + {text}
  • @@ -214,15 +210,44 @@ export default class Dropdown extends React.PureComponent { } else { const { top } = target.getBoundingClientRect(); const placement = top * 2 < innerHeight ? 'bottom' : 'top'; - this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click'); } } handleClose = () => { + if (this.activeElement) { + this.activeElement.focus(); + this.activeElement = null; + } this.props.onClose(this.state.id); } + handleMouseDown = () => { + if (!this.state.open) { + this.activeElement = document.activeElement; + } + } + + handleButtonKeyDown = (e) => { + switch(e.key) { + case ' ': + case 'Enter': + this.handleMouseDown(); + break; + } + } + + handleKeyPress = (e) => { + switch(e.key) { + case ' ': + case 'Enter': + this.handleClick(e); + e.stopPropagation(); + e.preventDefault(); + break; + } + } + handleItemClick = (i, e) => { const { action, to } = this.props.items[i]; @@ -265,6 +290,9 @@ export default class Dropdown extends React.PureComponent { size={size} ref={this.setTargetRef} onClick={this.handleClick} + onMouseDown={this.handleMouseDown} + onKeyDown={this.handleButtonKeyDown} + onKeyPress={this.handleKeyPress} /> From e8ad0a800616a1f95fd71ab8457869e716212ca0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 7 Aug 2019 10:01:19 +0200 Subject: [PATCH 9/9] [Glitch] Fix hashtag links always being lowercase Port 5e35aa82802b09a63d4625fa9c1837ad75178553 to glitch-soc Signed-off-by: Thibaut Girka --- app/javascript/flavours/glitch/components/status_content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index f8b101dc4..95a4fe3fa 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -172,7 +172,7 @@ export default class StatusContent extends React.PureComponent { } onHashtagClick = (hashtag, e) => { - hashtag = hashtag.replace(/^#/, '').toLowerCase(); + hashtag = hashtag.replace(/^#/, ''); if (this.props.parseClick) { this.props.parseClick(e, `/timelines/tag/${hashtag}`);