From 83aa41f3b364ecb05d278639fa23f7e9d5f48c5c Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 26 Oct 2017 22:44:57 -0700 Subject: [PATCH 1/8] Add whatinput dependency for more responsive focus styling --- app/javascript/packs/application.js | 2 ++ package.json | 3 ++- yarn.lock | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index c65ebed74..cee0d85bc 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -8,3 +8,5 @@ loadPolyfills().then(() => { }).catch(e => { console.error(e); }); + +require('what-input'); diff --git a/package.json b/package.json index 382e0b8d3..96203c248 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,8 @@ "webpack-bundle-analyzer": "^3.3.2", "webpack-cli": "^3.3.7", "webpack-merge": "^4.2.1", - "websocket.js": "^0.1.12" + "websocket.js": "^0.1.12", + "what-input": "^4.3.1" }, "devDependencies": { "babel-eslint": "^10.0.3", diff --git a/yarn.lock b/yarn.lock index f8314e4d9..c4f163f8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10693,6 +10693,10 @@ websocket.js@^0.1.12: dependencies: backoff "^2.4.1" +what-input@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/what-input/-/what-input-4.3.1.tgz#b8ea7554ba1d9171887c4c6addf28185fec3d31d" + whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" From 3a4aed8217197ff707a6a7640d2d8435c1889637 Mon Sep 17 00:00:00 2001 From: chr Date: Sun, 23 Jun 2019 13:58:33 -0700 Subject: [PATCH 2/8] Feature: 512-character posts in server and client --- .../mastodon/features/compose/components/compose_form.js | 6 +++--- app/serializers/rest/instance_serializer.rb | 4 ++++ app/validators/status_length_validator.rb | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index 47e189251..1011501ae 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -88,7 +88,7 @@ class ComposeForm extends ImmutablePureComponent { const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props; const fulltext = [this.props.spoilerText, countableText(this.props.text)].join(''); - if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) { + if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 512 || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) { return; } @@ -181,7 +181,7 @@ class ComposeForm extends ImmutablePureComponent { const { intl, onPaste, showSearch, anyMedia } = this.props; const disabled = this.props.isSubmitting; const text = [this.props.spoilerText, countableText(this.props.text)].join(''); - const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia); + const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 512 || (text.length !== 0 && text.trim().length === 0 && !anyMedia); let publishText = ''; if (this.props.privacy === 'private' || this.props.privacy === 'direct') { @@ -243,7 +243,7 @@ class ComposeForm extends ImmutablePureComponent { -
+
diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 1bd71683c..05ba27d60 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -39,6 +39,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer instance_presenter.thumbnail ? full_asset_url(instance_presenter.thumbnail.file.url) : full_pack_url('media/images/preview.jpg') end + def max_toot_chars + StatusLengthValidator::MAX_CHARS + end + def stats { user_count: instance_presenter.user_count, diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb index 93bae2fa8..3246b5ffb 100644 --- a/app/validators/status_length_validator.rb +++ b/app/validators/status_length_validator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class StatusLengthValidator < ActiveModel::Validator - MAX_CHARS = 500 + MAX_CHARS = 512 def validate(status) return unless status.local? && !status.reblog? From 88da29ce4230870909dfbbfd4e43e008bad1d427 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 14 Sep 2018 15:13:02 -0700 Subject: [PATCH 3/8] Remove reply counts on non-expanded view --- app/javascript/mastodon/components/status_action_bar.js | 2 +- app/views/statuses/_simple_status.html.haml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index 0bfbd8879..8e9db4b3c 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -249,7 +249,7 @@ class StatusActionBar extends ImmutablePureComponent { return (
-
{obfuscatedCount(status.get('replies_count'))}
+
{shareButton} diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index ca3c8fdd5..cd2194231 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -52,7 +52,6 @@ = fa_icon 'reply fw' - else = fa_icon 'reply-all fw' - .status__action-bar__counter__label= obscured_counter status.replies_count = link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do - if status.distributable? = fa_icon 'retweet fw' From 807ec09883da0423c6c10339edf647c07db7b285 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 17 Dec 2017 22:33:03 -0800 Subject: [PATCH 4/8] Hotlink twitter mentions Differentiate twitter mentions from normal mentions --- app/lib/formatter.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 990b9f63e..1c8c0abfb 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -262,8 +262,9 @@ class Formatter def link_to_mention(entity, linkable_accounts) acct = entity[:screen_name] + username, domain = acct.split('@') - return link_to_account(acct) unless linkable_accounts + return link_to_account(acct) unless linkable_accounts and domain != "twitter.com" account = linkable_accounts.find { |item| TagManager.instance.same_acct?(item.acct, acct) } account ? mention_html(account) : "@#{encode(acct)}" @@ -272,6 +273,10 @@ class Formatter def link_to_account(acct) username, domain = acct.split('@') + if domain == "twitter.com" + return mention_twitter_html(username) + end + domain = nil if TagManager.instance.local_domain?(domain) account = EntityCache.instance.mention(username, domain) @@ -299,4 +304,8 @@ class Formatter def mention_html(account) "@#{encode(account.username)}" end + + def mention_twitter_html(username) + "@#{username}@twitter.com" + end end From 6f15035cad8cf3ce26263751c1aa20ed679adf99 Mon Sep 17 00:00:00 2001 From: chr Date: Mon, 12 Nov 2018 20:30:47 -0800 Subject: [PATCH 5/8] Bio length -> 500 characters Increase the cybre.space profile bio text length limit to 500 characters. Also modifies the account settings page to automatically resize the textbox to the size of the contained text, so that it's easier to type longer bios. --- app/javascript/packs/public.js | 41 ++++++++++++++++++++++++++++++++++ app/models/account.rb | 11 +++++---- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index ed713f335..56adb7a04 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -18,6 +18,12 @@ window.addEventListener('message', e => { id: data.id, height: document.getElementsByTagName('html')[0].scrollHeight, }, '*'); + + if (document.fonts && document.fonts.ready) { + document.fonts.ready.then(sizeBioText); + } else { + sizeBioText(); + } }); }); @@ -117,6 +123,25 @@ function main() { delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); + + if (document.body.classList.contains('with-modals')) { + const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; + const scrollbarWidthStyle = document.createElement('style'); + scrollbarWidthStyle.id = 'scrollbar-width'; + document.head.appendChild(scrollbarWidthStyle); + scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0); + } + + [].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(, content); + }); + + if (document.fonts && document.fonts.ready) { + document.fonts.ready.then(sizeBioText); + } else { + sizeBioText(); + } }); delegate(document, '.webapp-btn', 'click', ({ target, button }) => { @@ -257,6 +282,22 @@ function main() { target.style.display = 'block'; } }); + + delegate(document, '#account_note', 'input', sizeBioText); + + function sizeBioText() { + const noteCounter = document.querySelector('.note-counter'); + const bioTextArea = document.querySelector('#account_note'); + + if (noteCounter) { + noteCounter.textContent = 413 - length(bioTextArea.value); + } + + if (bioTextArea) { + bioTextArea.style.height = 'auto'; + bioTextArea.style.height = (bioTextArea.scrollHeight+3) + 'px'; + } + } } loadPolyfills().then(main).catch(error => { diff --git a/app/models/account.rb b/app/models/account.rb index 55fe53fae..44366e232 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -81,6 +81,7 @@ class Account < ApplicationRecord validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? } + validate :note_has_eight_newlines?, if: -> { local? && will_save_change_to_note? } validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? } scope :remote, -> { where.not(domain: nil) } @@ -310,10 +311,8 @@ class Account < ApplicationRecord def save_with_optional_media! save! rescue ActiveRecord::RecordInvalid - self.avatar = nil - self.header = nil - self[:avatar_remote_url] = '' - self[:header_remote_url] = '' + self.avatar = nil if errors[:avatar].present? + self.header = nil if errors[:header].present? save! end @@ -337,6 +336,10 @@ class Account < ApplicationRecord shared_inbox_url.presence || inbox_url end + def note_has_eight_newlines? + errors.add(:note, 'Bio can\'t have more then 8 newlines') unless note.count("\n") <= 8 + end + class Field < ActiveModelSerializers::Model attributes :name, :value, :verified_at, :account, :errors From 84bf5ca175613fb75dcd2d25cd0a562fc15cf151 Mon Sep 17 00:00:00 2001 From: nightpool Date: Sat, 15 Jun 2019 10:47:09 -0400 Subject: [PATCH 6/8] Use git commit hash as part of the cache key avoids issues with model caching being incompatible across versions --- lib/mastodon/redis_config.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/redis_config.rb b/lib/mastodon/redis_config.rb index f11d94a45..088f2271b 100644 --- a/lib/mastodon/redis_config.rb +++ b/lib/mastodon/redis_config.rb @@ -22,7 +22,7 @@ setup_redis_env_url setup_redis_env_url(:cache, false) namespace = ENV.fetch('REDIS_NAMESPACE') { nil } -cache_namespace = namespace ? namespace + '_cache' : 'cache' +cache_namespace = [namespace, 'cache', `git rev-parse --short HEAD`.strip].compact.join('_') REDIS_CACHE_PARAMS = { expires_in: 10.minutes, From 13fc93fa6374c93494c3e4b12343f1e5096a0f9f Mon Sep 17 00:00:00 2001 From: nightpool Date: Sat, 15 Jun 2019 10:50:04 -0400 Subject: [PATCH 7/8] show warning message for locked account --- app/javascript/styles/mastodon/containers.scss | 6 ++++++ app/views/accounts/show.html.haml | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 319f8c94d..3d985b600 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -298,6 +298,12 @@ min-height: 1px; } + h2 { + font-size: 1rem; + align-items: center; + display: flex; + } + .nav-left { display: flex; align-items: stretch; diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 9c26dbabc..53dad2967 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -21,6 +21,11 @@ = render 'header', account: @account, with_bio: true +- if @account.user&.disabled? + .header + .nav-center + %h2 This user's account has been locked by a moderator. + .grid .column-0 .h-feed From 34ff641f0bac94c1d4ed170ede61d99f65188e77 Mon Sep 17 00:00:00 2001 From: nightpool Date: Sat, 15 Jun 2019 11:46:08 -0400 Subject: [PATCH 8/8] don't send spammy mentions --- app/lib/activitypub/activity.rb | 7 ++++++- app/models/status.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index a4a9baaee..ef35d34d1 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -90,7 +90,7 @@ class ActivityPub::Activity crawl_links(status) notify_about_reblog(status) if reblog_of_local_account?(status) - notify_about_mentions(status) + notify_about_mentions(status) unless spammy_mentions?(status) # Only continue if the status is supposed to have arrived in real-time. # Note that if @options[:override_timestamps] isn't set, the status @@ -105,6 +105,11 @@ class ActivityPub::Activity status.reblog? && status.reblog.account.local? end + def spammy_mentions?(status) + status.has_non_mention_links? && + @account.followers.local.count == 0 + end + def notify_about_reblog(status) NotifyService.new.call(status.reblog.account, status) end diff --git a/app/models/status.rb b/app/models/status.rb index 0c01a5389..50d7a1c74 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -398,6 +398,14 @@ class Status < ApplicationRecord super || build_status_stat end + def has_non_mention_links? + if local? + text.match? %r{https?://\w} + else + Nokogiri::HTML.fragment(text).css('a:not(.mention)').present? + end + end + private def update_status_stat!(attrs)