From 9417c9bb8f4e1b4a1b42e740d4ef4421b453cc33 Mon Sep 17 00:00:00 2001 From: Olivier Humbert Date: Wed, 6 Sep 2017 22:32:49 +0200 Subject: [PATCH 01/27] Update fr.json (#4830) typo --- app/javascript/mastodon/locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index fa8ea6c736..17f07c8a67 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -62,7 +62,7 @@ "confirmations.mute.confirm": "Masquer", "confirmations.mute.message": "Confirmez vous le masquage de {name} ?", "confirmations.unfollow.confirm": "Ne plus suivre", - "confirmations.unfollow.message": "Vous voulez-vous arrêter de suivre {name} ?", + "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name} ?", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activités", From be75b13d6848df36497296a609fa3ae4cb8c829f Mon Sep 17 00:00:00 2001 From: Quent-in Date: Thu, 7 Sep 2017 01:55:03 +0200 Subject: [PATCH 02/27] i10n update OC and FR files (#4824) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Onboarding: corrections Some missing letters and spaces or better wording * Embed Translated as Intégrer in FR / Embarcar in OC --- app/javascript/mastodon/locales/fr.json | 4 ++-- app/javascript/mastodon/locales/oc.json | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 17f07c8a67..a4cb2cdd0c 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -63,8 +63,8 @@ "confirmations.mute.message": "Confirmez vous le masquage de {name} ?", "confirmations.unfollow.confirm": "Ne plus suivre", "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name} ?", - "embed.instructions": "Embed this status on your website by copying the code below.", - "embed.preview": "Here is what it will look like:", + "embed.instructions": "Intégrez ce statut à votre site en copiant ce code ci-dessous.", + "embed.preview": "Il apparaîtra comme cela : ", "emoji_button.activity": "Activités", "emoji_button.flags": "Drapeaux", "emoji_button.food": "Boire et manger", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index a86033e6f6..52f4b7d64c 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -87,7 +87,7 @@ "getting_started.appsshort": "Apps", "getting_started.faq": "FAQ", "getting_started.heading": "Per començar", - "getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via{github} sus GitHub.", + "getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via {github} sus GitHub.", "getting_started.userguide": "Guida d’utilizacion", "home.column_settings.advanced": "Avançat", "home.column_settings.basic": "Basic", @@ -126,21 +126,21 @@ "notifications.column_settings.reblog": "Partatges :", "notifications.column_settings.show": "Mostrar dins la colomna", "notifications.column_settings.sound": "Emetre un son", - "onboarding.done": "Fach", + "onboarding.done": "Sortir", "onboarding.next": "Seguent", - "onboarding.page_five.public_timelines": "Lo flux local mòstra los estatuts publics del monde de vòstra instància, aquí {domain}. Lo flux federat mòstra los estatuts publics de tot lo mond sus {domain} sègon. Son los fluxes publics, un bon biais de trobar de mond.", + "onboarding.page_five.public_timelines": "Lo flux local mòstra los estatuts publics del monde de vòstra instància, aquí {domain}. Lo flux federat mòstra los estatuts publics de la gent que los de {domain} sègon. Son los fluxes publics, un bon biais de trobar de mond.", "onboarding.page_four.home": "Lo flux d’acuèlh mòstra los estatuts del mond que seguètz.", "onboarding.page_four.notifications": "La colomna de notificacions vos fa veire quand qualqu’un interagís amb vos", - "onboarding.page_one.federation": "Mastodon es un malhum de servidors independents que comunican per bastir un malhum ma larg. Òm los apèla instàncias.", + "onboarding.page_one.federation": "Mastodon es un malhum de servidors independents que comunican per bastir un malhum mai larg. Òm los apèla instàncias.", "onboarding.page_one.handle": "Sètz sus {domain}, doncas vòstre identificant complet es {handle}", "onboarding.page_one.welcome": "Benvengut a Mastodon !", "onboarding.page_six.admin": "Vòstre administrator d’instància es {admin}.", "onboarding.page_six.almost_done": "Gaireben acabat…", - "onboarding.page_six.appetoot": "Bon Appetut!", + "onboarding.page_six.appetoot": "Bon Appetut !", "onboarding.page_six.apps_available": "I a d’aplicacions per mobil per iOS, Android e mai.", "onboarding.page_six.github": "Mastodon es un logicial liure e open-source. Podètz senhalar de bugs, demandar de foncionalitats e contribuir al còdi sus {github}.", "onboarding.page_six.guidelines": "guida de la comunitat", - "onboarding.page_six.read_guidelines": "Mercés de legir la {guidelines} a {domain} !", + "onboarding.page_six.read_guidelines": "Mercés de legir la {guidelines} de {domain} !", "onboarding.page_six.various_app": "aplicacions per mobil", "onboarding.page_three.profile": "Modificatz vòstre perfil per cambiar vòstre avatar, bio e escais-nom. I a enlà totas las preferéncias.", "onboarding.page_three.search": "Emplegatz la barra de recèrca per trobar de mond e engachatz las etiquetas coma {illustration} e {introductions}. Per trobar una persona d’una autra instància, picatz son identificant complet.", @@ -183,7 +183,7 @@ "status.show_less": "Tornar plegar", "status.show_more": "Desplegar", "status.unmute_conversation": "Conversacions amb silenci levat", - "status.unpin": "Despenjar del perfil", + "status.unpin": "Tirar del perfil", "tabs_bar.compose": "Compausar", "tabs_bar.federated_timeline": "Flux public global", "tabs_bar.home": "Acuèlh", From 526449624014f7ec80f6f14570ef17d482019483 Mon Sep 17 00:00:00 2001 From: Joseph Mingrone Date: Wed, 6 Sep 2017 22:55:06 -0300 Subject: [PATCH 03/27] Use casecmp() instead of casecmp?() for now (#4832) * Use casecmp() instead of casecmp?() for now casecmp?() is only available in ruby 2.4.0. Users running earlier ruby versions would see errors, e.g., running RAILS_ENV=production rails mastodon:maintenance:remove_deprecated_preview_cards. * Correctly check whether casecmp() returns 0 --- lib/tasks/mastodon.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 307bc240db..3c65ece4be 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -47,7 +47,7 @@ namespace :mastodon do confirm = STDIN.gets.chomp puts - if confirm.casecmp?('y') + if confirm.casecmp('y').zero? password = SecureRandom.hex user = User.new(email: email, password: password, account_attributes: { username: username }) if user.save @@ -289,13 +289,13 @@ namespace :mastodon do puts 'Delete records and associated files from deprecated preview cards? [y/N]: ' confirm = STDIN.gets.chomp - if confirm.casecmp?('y') + if confirm.casecmp('y').zero? DeprecatedPreviewCard.in_batches.destroy_all puts 'Drop deprecated preview cards table? [y/N]: ' confirm = STDIN.gets.chomp - if confirm.casecmp?('y') + if confirm.casecmp('y').zero? ActiveRecord::Migration.drop_table :deprecated_preview_cards end end From 8185f988723dd95b814bcc5f03ccb3da94edbae4 Mon Sep 17 00:00:00 2001 From: voidSatisfaction Date: Thu, 7 Sep 2017 16:55:42 +0900 Subject: [PATCH 04/27] Feat add validation for report comment: characters under 1000 valid (#4833) --- app/models/report.rb | 2 ++ spec/models/report_spec.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/models/report.rb b/app/models/report.rb index 4d2552d30a..479aa17bb1 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -22,6 +22,8 @@ class Report < ApplicationRecord scope :unresolved, -> { where(action_taken: false) } scope :resolved, -> { where(action_taken: true) } + validates :comment, length: { maximum: 1000 } + def statuses Status.where(id: status_ids).includes(:account, :media_attachments, :mentions) end diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index 6c27238457..d40ebf6dc3 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -21,4 +21,18 @@ describe Report do expect(report.media_attachments).to eq [media_attachment] end end + + describe 'validatiions' do + it 'has a valid fabricator' do + report = Fabricate(:report) + report.valid? + expect(report).to be_valid + end + + it 'is invalid if comment is longer than 1000 characters' do + report = Fabricate.build(:report, comment: Faker::Lorem.characters(1001)) + report.valid? + expect(report).to model_have_error_on_field(:comment) + end + end end From 85c7c42098b5a5b10033eb8de79f6cb5dce8d462 Mon Sep 17 00:00:00 2001 From: voidSatisfaction Date: Thu, 7 Sep 2017 16:58:11 +0900 Subject: [PATCH 05/27] Add Pinned toot column (#4817) * Add Pinned_toot_section * Fix add frozen_string_literal * Fix delete no need controller and tests * Fix replace query strings to axios params * Fix change value to accountId and disabling more button --- .../mastodon/actions/pin_statuses.js | 39 ++++++++++++ .../features/getting_started/index.js | 8 ++- .../features/pinned_statuses/index.js | 59 +++++++++++++++++++ app/javascript/mastodon/features/ui/index.js | 2 + .../features/ui/util/async-components.js | 4 ++ app/javascript/mastodon/locales/en.json | 2 + app/javascript/mastodon/locales/ja.json | 2 + app/javascript/mastodon/locales/ko.json | 2 + .../mastodon/reducers/status_lists.js | 16 +++++ app/javascript/mastodon/reducers/statuses.js | 4 ++ 10 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 app/javascript/mastodon/actions/pin_statuses.js create mode 100644 app/javascript/mastodon/features/pinned_statuses/index.js diff --git a/app/javascript/mastodon/actions/pin_statuses.js b/app/javascript/mastodon/actions/pin_statuses.js new file mode 100644 index 0000000000..01bf8930b2 --- /dev/null +++ b/app/javascript/mastodon/actions/pin_statuses.js @@ -0,0 +1,39 @@ +import api from '../api'; + +export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST'; +export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS'; +export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL'; + +export function fetchPinnedStatuses() { + return (dispatch, getState) => { + dispatch(fetchPinnedStatusesRequest()); + + const accountId = getState().getIn(['meta', 'me']); + api(getState).get(`/api/v1/accounts/${accountId}/statuses`, { params: { pinned: true } }).then(response => { + dispatch(fetchPinnedStatusesSuccess(response.data, null)); + }).catch(error => { + dispatch(fetchPinnedStatusesFail(error)); + }); + }; +}; + +export function fetchPinnedStatusesRequest() { + return { + type: PINNED_STATUSES_FETCH_REQUEST, + }; +}; + +export function fetchPinnedStatusesSuccess(statuses, next) { + return { + type: PINNED_STATUSES_FETCH_SUCCESS, + statuses, + next, + }; +}; + +export function fetchPinnedStatusesFail(error) { + return { + type: PINNED_STATUSES_FETCH_FAIL, + error, + }; +}; diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index f8ea010247..973c8a4aef 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -23,6 +23,7 @@ const messages = defineMessages({ blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, + pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, }); const mapStateToProps = state => ({ @@ -66,15 +67,16 @@ export default class GettingStarted extends ImmutablePureComponent { navItems = navItems.concat([ , + , ]); if (me.get('locked')) { - navItems.push(); + navItems.push(); } navItems = navItems.concat([ - , - , + , + , ]); return ( diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js new file mode 100644 index 0000000000..b4a6c1e527 --- /dev/null +++ b/app/javascript/mastodon/features/pinned_statuses/index.js @@ -0,0 +1,59 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { fetchPinnedStatuses } from '../../actions/pin_statuses'; +import Column from '../ui/components/column'; +import ColumnBackButtonSlim from '../../components/column_back_button_slim'; +import StatusList from '../../components/status_list'; +import { defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + +const messages = defineMessages({ + heading: { id: 'column.pins', defaultMessage: 'Pinned toot' }, +}); + +const mapStateToProps = state => ({ + statusIds: state.getIn(['status_lists', 'pins', 'items']), + hasMore: !!state.getIn(['status_lists', 'pins', 'next']), +}); + +@connect(mapStateToProps) +@injectIntl +export default class PinnedStatuses extends ImmutablePureComponent { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + statusIds: ImmutablePropTypes.list.isRequired, + intl: PropTypes.object.isRequired, + hasMore: PropTypes.bool.isRequired, + }; + + componentWillMount () { + this.props.dispatch(fetchPinnedStatuses()); + } + + handleHeaderClick = () => { + this.column.scrollTop(); + } + + setRef = c => { + this.column = c; + } + + render () { + const { intl, statusIds, hasMore } = this.props; + + return ( + + + + + ); + } + +} diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8f971ae675..41ce0f8b54 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -35,6 +35,7 @@ import { FavouritedStatuses, Blocks, Mutes, + PinnedStatuses, } from './util/async-components'; // Dummy import, to make sure that ends up in the application bundle. @@ -208,6 +209,7 @@ export default class UI extends React.PureComponent { + diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 0882beb7fe..0b4e2df22b 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -34,6 +34,10 @@ export function GettingStarted () { return import(/* webpackChunkName: "features/getting_started" */'../../getting_started'); } +export function PinnedStatuses () { + return import(/* webpackChunkName: "features/pinned_statuses" */'../../pinned_statuses'); +} + export function AccountTimeline () { return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline'); } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 6d9b9c2087..f42851f459 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -34,6 +34,7 @@ "column.mutes": "Muted users", "column.notifications": "Notifications", "column.public": "Federated timeline", + "column.pins": "Pinned toots", "column_back_button.label": "Back", "column_header.hide_settings": "Hide settings", "column_header.moveLeft_settings": "Move column to the left", @@ -111,6 +112,7 @@ "navigation_bar.mutes": "Muted users", "navigation_bar.preferences": "Preferences", "navigation_bar.public_timeline": "Federated timeline", + "navigation_bar.pins": "Pinned toots", "notification.favourite": "{name} favourited your status", "notification.follow": "{name} followed you", "notification.mention": "{name} mentioned you", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 560d2b6684..65838a3f80 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -34,6 +34,7 @@ "column.mutes": "ミュートしたユーザー", "column.notifications": "通知", "column.public": "連合タイムライン", + "column.pins": "固定されたトゥート", "column_back_button.label": "戻る", "column_header.hide_settings": "設定を隠す", "column_header.moveLeft_settings": "カラムを左に移動する", @@ -111,6 +112,7 @@ "navigation_bar.mutes": "ミュートしたユーザー", "navigation_bar.preferences": "ユーザー設定", "navigation_bar.public_timeline": "連合タイムライン", + "navigation_bar.pins": "固定されたトゥート", "notification.favourite": "{name}さんがあなたのトゥートをお気に入りに登録しました", "notification.follow": "{name}さんにフォローされました", "notification.mention": "{name}さんがあなたに返信しました", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 7d573506cf..8393e82e57 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -34,6 +34,7 @@ "column.mutes": "뮤트 중인 사용자", "column.notifications": "알림", "column.public": "연합 타임라인", + "column.pins": "고정된 Toot", "column_back_button.label": "돌아가기", "column_header.hide_settings": "Hide settings", "column_header.moveLeft_settings": "Move column to the left", @@ -111,6 +112,7 @@ "navigation_bar.mutes": "뮤트 중인 사용자", "navigation_bar.preferences": "사용자 설정", "navigation_bar.public_timeline": "연합 타임라인", + "navigation_bar.pins": "고정된 Toot", "notification.favourite": "{name}님이 즐겨찾기 했습니다", "notification.follow": "{name}님이 나를 팔로우 했습니다", "notification.mention": "{name}님이 답글을 보냈습니다", diff --git a/app/javascript/mastodon/reducers/status_lists.js b/app/javascript/mastodon/reducers/status_lists.js index 2ce27a4545..c4aeb338f4 100644 --- a/app/javascript/mastodon/reducers/status_lists.js +++ b/app/javascript/mastodon/reducers/status_lists.js @@ -2,10 +2,15 @@ import { FAVOURITED_STATUSES_FETCH_SUCCESS, FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; +import { + PINNED_STATUSES_FETCH_SUCCESS, +} from '../actions/pin_statuses'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { FAVOURITE_SUCCESS, UNFAVOURITE_SUCCESS, + PIN_SUCCESS, + UNPIN_SUCCESS, } from '../actions/interactions'; const initialState = ImmutableMap({ @@ -14,6 +19,11 @@ const initialState = ImmutableMap({ loaded: false, items: ImmutableList(), }), + pins: ImmutableMap({ + next: null, + loaded: false, + items: ImmutableList(), + }), }); const normalizeList = (state, listType, statuses, next) => { @@ -53,6 +63,12 @@ export default function statusLists(state = initialState, action) { return prependOneToList(state, 'favourites', action.status); case UNFAVOURITE_SUCCESS: return removeOneFromList(state, 'favourites', action.status); + case PINNED_STATUSES_FETCH_SUCCESS: + return normalizeList(state, 'pins', action.statuses, action.next); + case PIN_SUCCESS: + return prependOneToList(state, 'pins', action.status); + case UNPIN_SUCCESS: + return removeOneFromList(state, 'pins', action.status); default: return state; } diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index 38691dc430..eec2a5f165 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -36,6 +36,9 @@ import { FAVOURITED_STATUSES_FETCH_SUCCESS, FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; +import { + PINNED_STATUSES_FETCH_SUCCESS, +} from '../actions/pin_statuses'; import { SEARCH_FETCH_SUCCESS } from '../actions/search'; import emojify from '../emoji'; import { Map as ImmutableMap, fromJS } from 'immutable'; @@ -138,6 +141,7 @@ export default function statuses(state = initialState, action) { case NOTIFICATIONS_EXPAND_SUCCESS: case FAVOURITED_STATUSES_FETCH_SUCCESS: case FAVOURITED_STATUSES_EXPAND_SUCCESS: + case PINNED_STATUSES_FETCH_SUCCESS: case SEARCH_FETCH_SUCCESS: return normalizeStatuses(state, action.statuses); case TIMELINE_DELETE: From 7d853b514a5b6d6405d2019e414b237e1e123571 Mon Sep 17 00:00:00 2001 From: PFM Date: Thu, 7 Sep 2017 23:18:41 +0900 Subject: [PATCH 06/27] Use ); } else { return ( -
+
+ ); } } if (this.state.preview && !autoplay) { return ( -
+
+ ); } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 7fd16f6b3d..75485d6b62 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -2347,22 +2347,6 @@ button.icon-button.active i.fa-retweet { height: 100%; } -.media-spoiler__video { - align-items: center; - background: $base-overlay-background; - color: $primary-text-color; - cursor: pointer; - display: flex; - flex-direction: column; - border: 0; - width: 100%; - height: 100%; - justify-content: center; - position: relative; - text-align: center; - z-index: 100; -} - .media-spoiler__warning { display: block; font-size: 14px; @@ -3821,6 +3805,8 @@ button.icon-button.active i.fa-retweet { cursor: pointer; margin-top: 8px; position: relative; + border: 0; + display: block; } .media-spoiler-video-play-icon { From 6859d4c0289e767955aac3f345074220fe200604 Mon Sep 17 00:00:00 2001 From: abcang Date: Thu, 7 Sep 2017 23:44:15 +0900 Subject: [PATCH 07/27] Enable UniqueRetryJobMiddleware even when called from sidekiq worker (#4836) --- config/initializers/sidekiq.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 61e1313364..0ee77730e8 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -9,6 +9,9 @@ end Sidekiq.configure_server do |config| config.redis = redis_params + config.client_middleware do |chain| + chain.add Mastodon::UniqueRetryJobMiddleware + end end Sidekiq.configure_client do |config| From dd6ede554fa0c0c6c7e6f3c74226a4ae64165999 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 7 Sep 2017 20:18:34 +0200 Subject: [PATCH 08/27] Fix #4834 - Adjust Status#local and Status#remote scopes (#4839) --- app/models/status.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/status.rb b/app/models/status.rb index fdc230d8f6..514cab2e4d 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -63,8 +63,8 @@ class Status < ApplicationRecord default_scope { recent } scope :recent, -> { reorder(id: :desc) } - scope :remote, -> { where.not(uri: nil) } - scope :local, -> { where(uri: nil) } + scope :remote, -> { where(local: false).or(where.not(uri: nil)) } + scope :local, -> { where(local: true).or(where(uri: nil)) } scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') } scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') } From b00cc4b9bdf24fb13f9034d2ac96f8f8acec6a26 Mon Sep 17 00:00:00 2001 From: Quent-in Date: Thu, 7 Sep 2017 22:07:03 +0200 Subject: [PATCH 09/27] i10n OC / FR update Pinned toots (#4842) * Added column.pins New strings * Added column.pins * Update confirmation_instructions.oc.html.erb * Update confirmation_instructions.oc.text.erb * Update password_change.oc.html.erb * Update password_change.oc.text.erb * Update reset_password_instructions.oc.html.erb * Update reset_password_instructions.oc.text.erb * Update confirmation_instructions.oc.html.erb * Update confirmation_instructions.oc.text.erb --- app/javascript/mastodon/locales/fr.json | 1 + app/javascript/mastodon/locales/oc.json | 1 + app/views/user_mailer/confirmation_instructions.oc.html.erb | 4 ++-- app/views/user_mailer/confirmation_instructions.oc.text.erb | 4 ++-- app/views/user_mailer/password_change.oc.html.erb | 4 ++-- app/views/user_mailer/password_change.oc.text.erb | 4 ++-- app/views/user_mailer/reset_password_instructions.oc.html.erb | 2 +- app/views/user_mailer/reset_password_instructions.oc.text.erb | 2 +- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index a4cb2cdd0c..628b887860 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -34,6 +34,7 @@ "column.mutes": "Comptes masqués", "column.notifications": "Notifications", "column.public": "Fil public global", + "column.pins": "Pouets épinglés", "column_back_button.label": "Retour", "column_header.hide_settings": "Masquer les paramètres", "column_header.moveLeft_settings": "Déplacer la colonne vers la gauche", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 52f4b7d64c..a04cbb51c9 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -34,6 +34,7 @@ "column.mutes": "Personas en silenci", "column.notifications": "Notificacions", "column.public": "Flux public global", + "column.pins": "Tuts penjats", "column_back_button.label": "Tornar", "column_header.hide_settings": "Amagar los paramètres", "column_header.moveLeft_settings": "Desplaçar la colomna a man drecha", diff --git a/app/views/user_mailer/confirmation_instructions.oc.html.erb b/app/views/user_mailer/confirmation_instructions.oc.html.erb index 9e9732bd8b..ed1428a55c 100644 --- a/app/views/user_mailer/confirmation_instructions.oc.html.erb +++ b/app/views/user_mailer/confirmation_instructions.oc.html.erb @@ -1,8 +1,8 @@ -

Bonjorn <%= @resource.email %> !

+

Bonjorn <%= @resource.email %> ! 

Venètz de vos crear un compte sus <%= @instance %> e vos mercegem :)

-

Per confirmar vòstre inscripcion, mercés de clicar sul ligam seguent :
+

Per confirmar vòstre inscripcion, mercés de clicar sul ligam seguent : 
<%= link_to 'Confirmar mon compte', confirmation_url(@resource, confirmation_token: @token) %>

Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de l’aisina.

diff --git a/app/views/user_mailer/confirmation_instructions.oc.text.erb b/app/views/user_mailer/confirmation_instructions.oc.text.erb index ff1abf62dd..444d296ce6 100644 --- a/app/views/user_mailer/confirmation_instructions.oc.text.erb +++ b/app/views/user_mailer/confirmation_instructions.oc.text.erb @@ -1,8 +1,8 @@ -Bonjorn <%= @resource.email %> ! +Bonjorn <%= @resource.email %> !  Venètz de vos crear un compte sus <%= @instance %> e vos mercegem :) -er confirmar vòstre inscripcion, mercés de clicar sul ligam seguent : +er confirmar vòstre inscripcion, mercés de clicar sul ligam seguent :  <%= link_to 'Confirmar mon compte', confirmation_url(@resource, confirmation_token: @token) %> Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de l’aisina. diff --git a/app/views/user_mailer/password_change.oc.html.erb b/app/views/user_mailer/password_change.oc.html.erb index a6b506f5e0..476db95366 100644 --- a/app/views/user_mailer/password_change.oc.html.erb +++ b/app/views/user_mailer/password_change.oc.html.erb @@ -1,3 +1,3 @@ -

Bonjorn <%= @resource.email %> !

+

Bonjorn <%= @resource.email %> ! 

-

Vos contactem per vos avisar que vòstre senhal per Mastodon es ben estat cambiat.

+

Vos contactem per vos avisar qu’avèm ben cambiat vòstre senhal Mastodon.

diff --git a/app/views/user_mailer/password_change.oc.text.erb b/app/views/user_mailer/password_change.oc.text.erb index 1caebde69a..e6caa045cd 100644 --- a/app/views/user_mailer/password_change.oc.text.erb +++ b/app/views/user_mailer/password_change.oc.text.erb @@ -1,3 +1,3 @@ -Bonjorn <%= @resource.email %> ! +Bonjorn <%= @resource.email %> !  -Vos contactem per vos avisar que vòstre senhal per Mastodon es ben estat cambiat. +Vos contactem per vos avisar qu’avèm ben cambiat vòstre senhal Mastodon. diff --git a/app/views/user_mailer/reset_password_instructions.oc.html.erb b/app/views/user_mailer/reset_password_instructions.oc.html.erb index acf2c0abde..7363ee4b6a 100644 --- a/app/views/user_mailer/reset_password_instructions.oc.html.erb +++ b/app/views/user_mailer/reset_password_instructions.oc.html.erb @@ -1,4 +1,4 @@ -

Bonjorn <%= @resource.email %> !

+

Bonjorn <%= @resource.email %> ! 

Qualqu’un a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion en clicant sul ligam çai-jos.

diff --git a/app/views/user_mailer/reset_password_instructions.oc.text.erb b/app/views/user_mailer/reset_password_instructions.oc.text.erb index 211974cc27..a95a1ae8cd 100644 --- a/app/views/user_mailer/reset_password_instructions.oc.text.erb +++ b/app/views/user_mailer/reset_password_instructions.oc.text.erb @@ -1,4 +1,4 @@ -Bonjorn <%= @resource.email %> ! +Bonjorn <%= @resource.email %> !  Qualqu’un a demandat una reĩnicializacion de vòstre senhal per Mastodon. Podètz realizar la reĩnicializacion en clicant sul ligam çai-jos.

From 7c2d84910cf427787b9fea2bdba67ec426c0b97d Mon Sep 17 00:00:00 2001 From: m4sk1n Date: Thu, 7 Sep 2017 22:51:48 +0200 Subject: [PATCH 10/27] i18n: Update Polish translation (#4845) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- app/javascript/mastodon/locales/pl.json | 8 +++++--- config/locales/pl.yml | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index e3e6529700..daa60128d5 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -12,7 +12,7 @@ "account.mute": "Wycisz @{name}", "account.posts": "Wpisy", "account.report": "Zgłoś @{name}", - "account.requested": "Oczekująca prośba", + "account.requested": "Oczekująca prośba, kliknij aby anulować", "account.share": "Udostępnij profil @{name}", "account.unblock": "Odblokuj @{name}", "account.unblock_domain": "Odblokuj domenę {domain}", @@ -33,6 +33,7 @@ "column.home": "Strona główna", "column.mutes": "Wyciszeni użytkownicy", "column.notifications": "Powiadomienia", + "column.pins": "Przypięte wpisy", "column.public": "Globalna oś czasu", "column_back_button.label": "Wróć", "column_header.hide_settings": "Ukryj ustawienia", @@ -109,6 +110,7 @@ "navigation_bar.info": "Szczegółowe informacje", "navigation_bar.logout": "Wyloguj", "navigation_bar.mutes": "Wyciszeni użytkownicy", + "navigation_bar.pins": "Przypięte wpisy", "navigation_bar.preferences": "Preferencje", "navigation_bar.public_timeline": "Oś czasu federacji", "notification.favourite": "{name} dodał Twój status do ulubionych", @@ -153,8 +155,8 @@ "privacy.private.short": "Tylko dla śledzących", "privacy.public.long": "Widoczny na publicznych osiach czasu", "privacy.public.short": "Publiczny", - "privacy.unlisted.long": "Niewidoczne na publicznych osiach czasu", - "privacy.unlisted.short": "Niewidoczne", + "privacy.unlisted.long": "Niewidoczny na publicznych osiach czasu", + "privacy.unlisted.short": "Niewidoczny", "reply_indicator.cancel": "Anuluj", "report.placeholder": "Dodatkowe komentarze", "report.submit": "Wyślij", diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 842baef451..88125f6923 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -451,11 +451,11 @@ pl: show_more: Pokaż więcej visibilities: private: Tylko dla śledzących - private_long: Widoczny tylko dla osób, które Cię śledzą - public: Publiczny - public_long: Widoczny dla wszystkich użytkowników - unlisted: Niewypisany - unlisted_long: Widoczny dla wszystkich, ale nie wyświetlany na publicznych osiach czasu + private_long: Widoczne tylko dla osób, które Cię śledzą + public: Publiczne + public_long: Widoczne dla wszystkich użytkowników + unlisted: Niewypisane + unlisted_long: Widoczne dla wszystkich, ale nie wyświetlane na publicznych osiach czasu stream_entries: click_to_show: Naciśnij aby wyświetlić pinned: Przypięty wpis From a4caa7eb6258ff7f8eea8e6791b86b1c7f4d172e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 8 Sep 2017 12:00:17 +0200 Subject: [PATCH 11/27] Fetch statuses/following/followers numbers from ActivityPub collections (#4840) --- .../activitypub/process_account_service.rb | 37 ++++++++++++++++--- spec/lib/activitypub/activity/update_spec.rb | 8 +++- .../fetch_remote_account_service_spec.rb | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 29eb1c2e10..c63577fe5d 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -8,11 +8,12 @@ class ActivityPub::ProcessAccountService < BaseService def call(username, domain, json) return if json['inbox'].blank? - @json = json - @uri = @json['id'] - @username = username - @domain = domain - @account = Account.find_by(uri: @uri) + @json = json + @uri = @json['id'] + @username = username + @domain = domain + @account = Account.find_by(uri: @uri) + @collections = {} create_account if @account.nil? upgrade_account if @account.ostatus? @@ -51,6 +52,9 @@ class ActivityPub::ProcessAccountService < BaseService @account.header_remote_url = image_url('image') @account.public_key = public_key || '' @account.locked = @json['manuallyApprovesFollowers'] || false + @account.statuses_count = outbox_total_items if outbox_total_items.present? + @account.following_count = following_total_items if following_total_items.present? + @account.followers_count = followers_total_items if followers_total_items.present? @account.save! end @@ -88,6 +92,29 @@ class ActivityPub::ProcessAccountService < BaseService value['href'] end + def outbox_total_items + collection_total_items('outbox') + end + + def following_total_items + collection_total_items('following') + end + + def followers_total_items + collection_total_items('followers') + end + + def collection_total_items(type) + return if @json[type].blank? + return @collections[type] if @collections.key?(type) + + collection = fetch_resource(@json[type]) + + @collections[type] = collection.is_a?(Hash) && collection['totalItems'].present? && collection['totalItems'].is_a?(Numeric) ? collection['totalItems'] : nil + rescue HTTP::Error, OpenSSL::SSL::SSLError + @collections[type] = nil + end + def auto_suspend? domain_block && domain_block.suspend? end diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb index 0bd6d00d9c..ea308e35cd 100644 --- a/spec/lib/activitypub/activity/update_spec.rb +++ b/spec/lib/activitypub/activity/update_spec.rb @@ -2,12 +2,16 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Update do let!(:sender) { Fabricate(:account) } - + before do + stub_request(:get, actor_json[:outbox]).to_return(status: 404) + stub_request(:get, actor_json[:followers]).to_return(status: 404) + stub_request(:get, actor_json[:following]).to_return(status: 404) + sender.update!(uri: ActivityPub::TagManager.instance.uri_for(sender)) end - let(:modified_sender) do + let(:modified_sender) do sender.dup.tap do |modified_sender| modified_sender.display_name = 'Totally modified now' end diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb index 391d051c1a..ed7e9bba83 100644 --- a/spec/services/activitypub/fetch_remote_account_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb @@ -41,7 +41,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService do before do actor[:inbox] = nil - + stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor)) stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end From 95f018a3d4f02ff4ce77d7b1cfa9a79fce3ce99a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 8 Sep 2017 12:00:30 +0200 Subject: [PATCH 12/27] "Mute conversation" option on all own toots, not just in notifications (#4844) That way you can mute notifications for a toot before you get replies to it or boosts or favourites --- app/javascript/mastodon/components/status_action_bar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index 850f3f8c40..692b1ef2bc 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -134,7 +134,7 @@ export default class StatusActionBar extends ImmutablePureComponent { menu.push(null); - if (withDismiss) { + if (status.getIn(['account', 'id']) === me || withDismiss) { menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); menu.push(null); } From 1caf11ddcc029d9e7735ab3b90607ab2d6973034 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 8 Sep 2017 12:32:22 +0200 Subject: [PATCH 13/27] Fix language filter codes (#4841) * Fix language filter codes CLD3 returns BCP-47 language identifier, filter settings expect identifiers in the ISO 639-1 format. Convert between formats, and exclude duplicate languages from filter choices (zh-CN->zh) * Fix zh name --- Gemfile | 1 + Gemfile.lock | 2 ++ app/helpers/settings_helper.rb | 5 +++++ app/lib/language_detector.rb | 11 ++++++++++- app/views/settings/preferences/show.html.haml | 2 +- spec/helpers/settings_helper_spec.rb | 4 ++-- 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 486e72cc4a..637bf53eeb 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,7 @@ gem 'addressable', '~> 2.5' gem 'bootsnap' gem 'browser' gem 'charlock_holmes', '~> 0.7.5' +gem 'iso-639' gem 'cld3', '~> 3.1' gem 'devise', '~> 4.2' gem 'devise-two-factor', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index ef99e0d7b3..ddb97dd940 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -225,6 +225,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.0) ipaddress (0.8.3) + iso-639 (0.2.8) jmespath (1.3.1) json (2.1.0) json-ld (2.1.5) @@ -560,6 +561,7 @@ DEPENDENCIES httplog (~> 0.99) i18n-tasks (~> 0.9) idn-ruby + iso-639 json-ld-preloaded (~> 2.2.1) kaminari (~> 1.0) letter_opener (~> 1.4) diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index af950aa634..369a456809 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -30,6 +30,7 @@ module SettingsHelper th: 'ภาษาไทย', tr: 'Türkçe', uk: 'Українська', + zh: '中文', 'zh-CN': '简体中文', 'zh-HK': '繁體中文(香港)', 'zh-TW': '繁體中文(臺灣)', @@ -39,6 +40,10 @@ module SettingsHelper HUMAN_LOCALES[locale] end + def filterable_languages + I18n.available_locales.map { |locale| locale.to_s.split('-').first.to_sym }.uniq + end + def hash_to_object(hash) HashObject.new(hash) end diff --git a/app/lib/language_detector.rb b/app/lib/language_detector.rb index cc7509fdc8..1d9932b528 100644 --- a/app/lib/language_detector.rb +++ b/app/lib/language_detector.rb @@ -20,7 +20,16 @@ class LanguageDetector private def detected_language_code - result.language.to_sym if detected_language_reliable? + iso6391(result.language).to_sym if detected_language_reliable? + end + + def iso6391(bcp47) + iso639 = bcp47.split('-').first + + # CLD3 returns grandfathered language code for Hebrew + return 'he' if iso639 == 'iw' + + ISO_639.find(iso639).alpha2 end def result diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index fae6090c81..f42f925080 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -13,7 +13,7 @@ selected: I18n.locale = f.input :filtered_languages, - collection: I18n.available_locales, + collection: filterable_languages, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, diff --git a/spec/helpers/settings_helper_spec.rb b/spec/helpers/settings_helper_spec.rb index 5a51e0ef1f..092c375836 100644 --- a/spec/helpers/settings_helper_spec.rb +++ b/spec/helpers/settings_helper_spec.rb @@ -4,10 +4,10 @@ require 'rails_helper' describe SettingsHelper do describe 'the HUMAN_LOCALES constant' do - it 'has the same number of keys as I18n locales exist' do + it 'includes all I18n locales' do options = I18n.available_locales - expect(described_class::HUMAN_LOCALES.keys).to eq(options) + expect(described_class::HUMAN_LOCALES.keys).to include(*options) end end From dabc309ca3329c455e4e26a8bf9a691fcafe20a7 Mon Sep 17 00:00:00 2001 From: Quent-in Date: Fri, 8 Sep 2017 13:55:47 +0200 Subject: [PATCH 14/27] i10n update OC and FR (#4849) * Missing "navigation_bar.pins" * Missing "navigation_bar.pins" --- app/javascript/mastodon/locales/fr.json | 1 + app/javascript/mastodon/locales/oc.json | 1 + 2 files changed, 2 insertions(+) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 628b887860..8ca632accd 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -112,6 +112,7 @@ "navigation_bar.mutes": "Comptes masqués", "navigation_bar.preferences": "Préférences", "navigation_bar.public_timeline": "Fil public global", + "navigation_bar.pins": "Pouets épinglés", "notification.favourite": "{name} a ajouté à ses favoris :", "notification.follow": "{name} vous suit.", "notification.mention": "{name} vous a mentionné⋅e :", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index a04cbb51c9..d2b2dd48f7 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -112,6 +112,7 @@ "navigation_bar.mutes": "Personas rescondudas", "navigation_bar.preferences": "Preferéncias", "navigation_bar.public_timeline": "Flux public global", + "navigation_bar.pins": "Tuts penjats", "notification.favourite": "{name} a ajustat a sos favorits :", "notification.follow": "{name} vos sèc", "notification.mention": "{name} vos a mencionat :", From a12572e074581832744b243f2fadd1742e8418b6 Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 9 Sep 2017 01:20:03 +0900 Subject: [PATCH 15/27] Handle stream_entry URL correctly in ActivityPub (#4854) In before, the method uses stream_entry id as status id, so replied status was wrongly selected. This PR uses StatusFinder which was introduced with `Api::Web::EmbedsController`. --- app/lib/activitypub/tag_manager.rb | 4 +++- spec/lib/activitypub/tag_manager_spec.rb | 28 +++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index de575d9e6f..929e87852c 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -96,12 +96,14 @@ class ActivityPub::TagManager when 'Account' klass.find_local(uri_to_local_id(uri, :username)) else - klass.find_by(id: uri_to_local_id(uri)) + StatusFinder.new(uri).status end elsif ::TagManager.instance.local_id?(uri) klass.find_by(id: ::TagManager.instance.unique_tag_to_local_id(uri, klass.to_s)) else klass.find_by(uri: uri.split('#').first) end + rescue ActiveRecord::RecordNotFound + nil end end diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 8f7662e24a..dea8abc655 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -91,9 +91,35 @@ RSpec.describe ActivityPub::TagManager do end describe '#uri_to_resource' do - it 'returns the local resource' do + it 'returns the local account' do account = Fabricate(:account) expect(subject.uri_to_resource(subject.uri_for(account), Account)).to eq account end + + it 'returns the remote account by matching URI without fragment part' do + account = Fabricate(:account, uri: 'https://example.com/123') + expect(subject.uri_to_resource('https://example.com/123#456', Account)).to eq account + end + + it 'returns the local status for ActivityPub URI' do + status = Fabricate(:status) + expect(subject.uri_to_resource(subject.uri_for(status), Status)).to eq status + end + + it 'returns the local status for OStatus tag: URI' do + status = Fabricate(:status) + expect(subject.uri_to_resource(::TagManager.instance.uri_for(status), Status)).to eq status + end + + it 'returns the local status for OStatus StreamEntry URL' do + status = Fabricate(:status) + stream_entry_url = account_stream_entry_url(status.account, status.stream_entry) + expect(subject.uri_to_resource(stream_entry_url, Status)).to eq status + end + + it 'returns the remote status by matching URI without fragment part' do + status = Fabricate(:status, uri: 'https://example.com/123') + expect(subject.uri_to_resource('https://example.com/123#456', Status)).to eq status + end end end From 1ae5d49a7192ba247429670fec5f4749c8895edf Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 9 Sep 2017 04:43:34 +0900 Subject: [PATCH 16/27] Refresh timeline after toot while the timeline is disconnected (#4858) To reflect status posting immediately, we've inserted the status into timelines directly. However, status insertion changes "latest status", and it means next timeline refresh only fetches statuses since the inserted status. This behavior is very bad for disconnected timeline and mobile views. After this patch, it refreshes timeline for disconnected timelines, instead of direct insertion. --- app/javascript/mastodon/actions/compose.js | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index ebb75f36e9..1f26907f24 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -1,6 +1,11 @@ import api from '../api'; -import { updateTimeline } from './timelines'; +import { + updateTimeline, + refreshHomeTimeline, + refreshCommunityTimeline, + refreshPublicTimeline, +} from './timelines'; export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; @@ -95,16 +100,20 @@ export function submitCompose() { dispatch(submitComposeSuccess({ ...response.data })); // To make the app more responsive, immediately get the status into the columns - dispatch(updateTimeline('home', { ...response.data })); + + const insertOrRefresh = (timelineId, refreshAction) => { + if (getState().getIn(['timelines', timelineId, 'online'])) { + dispatch(updateTimeline(timelineId, { ...response.data })); + } else if (getState().getIn(['timelines', timelineId, 'loaded'])) { + dispatch(refreshAction()); + } + }; + + insertOrRefresh('home', refreshHomeTimeline); if (response.data.in_reply_to_id === null && response.data.visibility === 'public') { - if (getState().getIn(['timelines', 'community', 'loaded'])) { - dispatch(updateTimeline('community', { ...response.data })); - } - - if (getState().getIn(['timelines', 'public', 'loaded'])) { - dispatch(updateTimeline('public', { ...response.data })); - } + insertOrRefresh('community', refreshCommunityTimeline); + insertOrRefresh('public', refreshPublicTimeline); } }).catch(function (error) { dispatch(submitComposeFail(error)); From 7ca173be47c8aa5e8bd2b33aac642193ebb673a0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 02:02:29 +0200 Subject: [PATCH 17/27] Fix #4850 - When visibility missing from API call to toot, fallback to user preference (#4861) --- app/services/post_status_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 568f5a9e7f..97c55c4b2b 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -27,9 +27,10 @@ class PostStatusService < BaseService thread: in_reply_to, sensitive: options[:sensitive], spoiler_text: options[:spoiler_text] || '', - visibility: options[:visibility], + visibility: options[:visibility] || account.user&.setting_default_privacy, language: detect_language_for(text, account), application: options[:application]) + attach_media(status, media) end From 4b460bc57188c5dab31a921daed3c0012df28761 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 02:02:44 +0200 Subject: [PATCH 18/27] Fix #4852 - Check if already requested from FollowService (#4855) --- app/services/follow_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index a92eb6b888..941556b604 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -12,7 +12,7 @@ class FollowService < BaseService raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended? raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) - return if source_account.following?(target_account) + return if source_account.following?(target_account) || source_account.requested?(target_account) if target_account.locked? || target_account.activitypub? request_follow(source_account, target_account) From baa8b82179203a8c035dab23fbea48a0e6cfc886 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 02:26:41 +0200 Subject: [PATCH 19/27] Fix #1004 - Temporarily pause timeline if there's been recent mouse movement (#4859) --- .../mastodon/components/scrollable_list.js | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index e47b1e9aa5..42763ddd3d 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -27,6 +27,10 @@ export default class ScrollableList extends PureComponent { trackScroll: true, }; + state = { + lastMouseMove: null, + }; + intersectionObserverWrapper = new IntersectionObserverWrapper(); handleScroll = throttle(() => { @@ -47,6 +51,14 @@ export default class ScrollableList extends PureComponent { trailing: true, }); + handleMouseMove = throttle(() => { + this.setState({ lastMouseMove: new Date() }); + }, 300); + + handleMouseLeave = () => { + this.setState({ lastMouseMove: null }); + } + componentDidMount () { this.attachScrollListener(); this.attachIntersectionObserver(); @@ -58,9 +70,10 @@ export default class ScrollableList extends PureComponent { componentDidUpdate (prevProps) { // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. - if (React.Children.count(prevProps.children) < React.Children.count(this.props.children) && this._oldScrollPosition && this.node.scrollTop > 0) { + if (React.Children.count(prevProps.children) < React.Children.count(this.props.children) && this._oldScrollPosition && (this.node.scrollTop > 0 || this._recentlyMoved())) { if (this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props)) { const newScrollTop = this.node.scrollHeight - this._oldScrollPosition; + if (this.node.scrollTop !== newScrollTop) { this.node.scrollTop = newScrollTop; } @@ -114,6 +127,10 @@ export default class ScrollableList extends PureComponent { this.props.onScrollToBottom(); } + _recentlyMoved () { + return this.state.lastMouseMove === null || ((new Date()) - this.state.lastMouseMove < 600); + } + handleKeyDown = (e) => { if (['PageDown', 'PageUp'].includes(e.key) || (e.ctrlKey && ['End', 'Home'].includes(e.key))) { const article = (() => { @@ -149,7 +166,7 @@ export default class ScrollableList extends PureComponent { if (isLoading || childrenCount > 0 || !emptyMessage) { scrollableArea = ( -
+
{prepend} From 3c45d3963a21812f00318353be90010df636bd0d Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Sat, 9 Sep 2017 09:26:58 +0900 Subject: [PATCH 20/27] Scrollable tables in settings pages (#4857) * Scrollable tables in settings pages * Add space before curly brace --- app/javascript/styles/admin.scss | 8 +- app/javascript/styles/tables.scss | 19 +-- app/views/admin/accounts/_card.html.haml | 31 ++-- app/views/admin/accounts/index.html.haml | 23 +-- app/views/admin/accounts/show.html.haml | 142 +++++++++--------- app/views/admin/domain_blocks/index.html.haml | 19 +-- app/views/admin/instances/index.html.haml | 15 +- app/views/admin/reports/index.html.haml | 25 +-- app/views/admin/subscriptions/index.html.haml | 21 +-- .../auth/registrations/_sessions.html.haml | 47 +++--- .../authorized_applications/index.html.haml | 39 ++--- .../settings/applications/index.html.haml | 25 +-- .../settings/applications/show.html.haml | 33 ++-- app/views/settings/exports/show.html.haml | 37 ++--- .../settings/follower_domains/show.html.haml | 27 ++-- 15 files changed, 262 insertions(+), 249 deletions(-) diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss index b86de75b67..fa7859e38f 100644 --- a/app/javascript/styles/admin.scss +++ b/app/javascript/styles/admin.scss @@ -190,11 +190,15 @@ .filters { display: flex; - margin-bottom: 20px; + flex-wrap: wrap; .filter-subset { flex: 0 0 auto; - margin-right: 40px; + margin: 0 40px 10px 0; + + &:last-child { + margin-bottom: 20px; + } ul { margin-top: 5px; diff --git a/app/javascript/styles/tables.scss b/app/javascript/styles/tables.scss index f6e57e1961..ad46f5f9f5 100644 --- a/app/javascript/styles/tables.scss +++ b/app/javascript/styles/tables.scss @@ -3,7 +3,6 @@ max-width: 100%; border-spacing: 0; border-collapse: collapse; - margin-bottom: 20px; th, td { @@ -43,19 +42,17 @@ font-weight: 500; } - &.inline-table { - td, - th { - padding: 8px 2px; - } - - & > tbody > tr:nth-child(odd) > td, - & > tbody > tr:nth-child(odd) > th { - background: transparent; - } + &.inline-table > tbody > tr:nth-child(odd) > td, + &.inline-table > tbody > tr:nth-child(odd) > th { + background: transparent; } } +.table-wrapper { + overflow: auto; + margin-bottom: 20px; +} + samp { font-family: 'mastodon-font-monospace', monospace; } diff --git a/app/views/admin/accounts/_card.html.haml b/app/views/admin/accounts/_card.html.haml index bb33582eba..2f59550113 100644 --- a/app/views/admin/accounts/_card.html.haml +++ b/app/views/admin/accounts/_card.html.haml @@ -1,16 +1,17 @@ -%table.table - %tbody - %tr - %td= t('admin.accounts.show.created_reports') - %td= link_to pluralize(account.reports.count, t('admin.accounts.show.report')), admin_reports_path(account_id: account.id) - %tr - %td= t('admin.accounts.show.targeted_reports') - %td= link_to pluralize(account.targeted_reports.count, t('admin.accounts.show.report')), admin_reports_path(target_account_id: account.id) - - if account.silenced? || account.suspended? +.table-wrapper + %table.table + %tbody %tr - %td= t('admin.accounts.moderation.title') - %td - - if account.silenced? - %p= t('admin.accounts.moderation.silenced') - - if account.suspended? - %p= t('admin.accounts.moderation.suspended') + %td= t('admin.accounts.show.created_reports') + %td= link_to pluralize(account.reports.count, t('admin.accounts.show.report')), admin_reports_path(account_id: account.id) + %tr + %td= t('admin.accounts.show.targeted_reports') + %td= link_to pluralize(account.targeted_reports.count, t('admin.accounts.show.report')), admin_reports_path(target_account_id: account.id) + - if account.silenced? || account.suspended? + %tr + %td= t('admin.accounts.moderation.title') + %td + - if account.silenced? + %p= t('admin.accounts.moderation.silenced') + - if account.suspended? + %p= t('admin.accounts.moderation.suspended') diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml index 1f36aeb312..1b56a3a316 100644 --- a/app/views/admin/accounts/index.html.haml +++ b/app/views/admin/accounts/index.html.haml @@ -50,16 +50,17 @@ %button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' -%table.table - %thead - %tr - %th= t('admin.accounts.username') - %th= t('admin.accounts.domain') - %th= t('admin.accounts.protocol') - %th= t('admin.accounts.confirmed') - %th= fa_icon 'paper-plane-o' - %th - %tbody - = render @accounts +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.accounts.username') + %th= t('admin.accounts.domain') + %th= t('admin.accounts.protocol') + %th= t('admin.accounts.confirmed') + %th= fa_icon 'paper-plane-o' + %th + %tbody + = render @accounts = paginate @accounts diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index dc2f16cc99..89355281a3 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -1,86 +1,86 @@ - content_for :page_title do = @account.acct -%table.table - %tbody - %tr - %th= t('admin.accounts.username') - %td= @account.username - %tr - %th= t('admin.accounts.domain') - %td= @account.domain - %tr - %th= t('admin.accounts.display_name') - %td= @account.display_name +.table-wrapper + %table.table + %tbody + %tr + %th= t('admin.accounts.username') + %td= @account.username + %tr + %th= t('admin.accounts.domain') + %td= @account.domain + %tr + %th= t('admin.accounts.display_name') + %td= @account.display_name - - if @account.local? - %tr - %th= t('admin.accounts.email') - %td= @account.user_email - %tr - %th= t('admin.accounts.most_recent_ip') - %td= @account.user_current_sign_in_ip - %tr - %th= t('admin.accounts.most_recent_activity') - %td - - if @account.user_current_sign_in_at - %time.formatted{ datetime: @account.user_current_sign_in_at.iso8601, title: l(@account.user_current_sign_in_at) } - = l @account.user_current_sign_in_at - - else - Never - - else - %tr - %th= t('admin.accounts.profile_url') - %td= link_to @account.url, @account.url - %tr - %th= t('admin.accounts.protocol') - %td= @account.protocol.humanize - - - if @account.ostatus? + - if @account.local? %tr - %th= t('admin.accounts.feed_url') - %td= link_to @account.remote_url, @account.remote_url + %th= t('admin.accounts.email') + %td= @account.user_email %tr - %th= t('admin.accounts.push_subscription_expires') + %th= t('admin.accounts.most_recent_ip') + %td= @account.user_current_sign_in_ip + %tr + %th= t('admin.accounts.most_recent_activity') %td - - if @account.subscribed? - %time.formatted{ datetime: @account.subscription_expires_at.iso8601, title: l(@account.subscription_expires_at) } - = l @account.subscription_expires_at + - if @account.user_current_sign_in_at + %time.formatted{ datetime: @account.user_current_sign_in_at.iso8601, title: l(@account.user_current_sign_in_at) } + = l @account.user_current_sign_in_at - else - = t('admin.accounts.not_subscribed') + Never + - else %tr - %th= t('admin.accounts.salmon_url') - %td= link_to @account.salmon_url, @account.salmon_url - - elsif @account.activitypub? + %th= t('admin.accounts.profile_url') + %td= link_to @account.url, @account.url %tr - %th= t('admin.accounts.inbox_url') - %td= link_to @account.inbox_url, @account.inbox_url - %tr - %th= t('admin.accounts.outbox_url') - %td= link_to @account.outbox_url, @account.outbox_url + %th= t('admin.accounts.protocol') + %td= @account.protocol.humanize - %tr - %th= t('admin.accounts.follows') - %td= @account.following_count - %tr - %th= t('admin.accounts.followers') - %td= @account.followers_count - %tr - %th= t('admin.accounts.statuses') - %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id) - %tr - %th= t('admin.accounts.media_attachments') - %td - = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true }) - = surround '(', ')' do - = number_to_human_size @account.media_attachments.sum('file_file_size') - %tr - %th= t('.created_reports') - %td= link_to pluralize(@account.reports.count, t('.report')), admin_reports_path(account_id: @account.id) - %tr - %th= t('.targeted_reports') - %td= link_to pluralize(@account.targeted_reports.count, t('.report')), admin_reports_path(target_account_id: @account.id) + - if @account.ostatus? + %tr + %th= t('admin.accounts.feed_url') + %td= link_to @account.remote_url, @account.remote_url + %tr + %th= t('admin.accounts.push_subscription_expires') + %td + - if @account.subscribed? + %time.formatted{ datetime: @account.subscription_expires_at.iso8601, title: l(@account.subscription_expires_at) } + = l @account.subscription_expires_at + - else + = t('admin.accounts.not_subscribed') + %tr + %th= t('admin.accounts.salmon_url') + %td= link_to @account.salmon_url, @account.salmon_url + - elsif @account.activitypub? + %tr + %th= t('admin.accounts.inbox_url') + %td= link_to @account.inbox_url, @account.inbox_url + %tr + %th= t('admin.accounts.outbox_url') + %td= link_to @account.outbox_url, @account.outbox_url + %tr + %th= t('admin.accounts.follows') + %td= @account.following_count + %tr + %th= t('admin.accounts.followers') + %td= @account.followers_count + %tr + %th= t('admin.accounts.statuses') + %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id) + %tr + %th= t('admin.accounts.media_attachments') + %td + = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true }) + = surround '(', ')' do + = number_to_human_size @account.media_attachments.sum('file_file_size') + %tr + %th= t('.created_reports') + %td= link_to pluralize(@account.reports.count, t('.report')), admin_reports_path(account_id: @account.id) + %tr + %th= t('.targeted_reports') + %td= link_to pluralize(@account.targeted_reports.count, t('.report')), admin_reports_path(target_account_id: @account.id) %div{ style: 'float: right' } - if @account.local? diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml index 5ae9fec530..42b8ca53f4 100644 --- a/app/views/admin/domain_blocks/index.html.haml +++ b/app/views/admin/domain_blocks/index.html.haml @@ -1,15 +1,16 @@ - content_for :page_title do = t('admin.domain_blocks.title') -%table.table - %thead - %tr - %th= t('admin.domain_blocks.domain') - %th= t('admin.domain_blocks.severity') - %th= t('admin.domain_blocks.reject_media') - %th - %tbody - = render @domain_blocks +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.domain_blocks.domain') + %th= t('admin.domain_blocks.severity') + %th= t('admin.domain_blocks.reject_media') + %th + %tbody + = render @domain_blocks = paginate @domain_blocks = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path, class: 'button' diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml index be21d6bf7b..edbd3b2173 100644 --- a/app/views/admin/instances/index.html.haml +++ b/app/views/admin/instances/index.html.haml @@ -1,12 +1,13 @@ - content_for :page_title do = t('admin.instances.title') -%table.table - %thead - %tr - %th= t('admin.instances.domain_name') - %th= t('admin.instances.account_count') - %tbody - = render @instances +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.instances.domain_name') + %th= t('admin.instances.account_count') + %tbody + = render @instances = paginate paginated_instances diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index f1c4a93c47..577c68a864 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -10,17 +10,18 @@ = form_tag do - %table.table - %thead - %tr - -# %th - %th= t('admin.reports.id') - %th= t('admin.reports.target') - %th= t('admin.reports.reported_by') - %th= t('admin.reports.comment.label') - %th= t('admin.reports.report_contents') - %th - %tbody - = render @reports + .table-wrapper + %table.table + %thead + %tr + -# %th + %th= t('admin.reports.id') + %th= t('admin.reports.target') + %th= t('admin.reports.reported_by') + %th= t('admin.reports.comment.label') + %th= t('admin.reports.report_contents') + %th + %tbody + = render @reports = paginate @reports diff --git a/app/views/admin/subscriptions/index.html.haml b/app/views/admin/subscriptions/index.html.haml index 21b3238a6b..83704c8ee5 100644 --- a/app/views/admin/subscriptions/index.html.haml +++ b/app/views/admin/subscriptions/index.html.haml @@ -1,15 +1,16 @@ - content_for :page_title do = t('admin.subscriptions.title') -%table.table - %thead - %tr - %th= t('admin.subscriptions.topic') - %th= t('admin.subscriptions.callback_url') - %th= t('admin.subscriptions.confirmed') - %th= t('admin.subscriptions.expires_in') - %th= t('admin.subscriptions.last_delivery') - %tbody - = render @subscriptions +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.subscriptions.topic') + %th= t('admin.subscriptions.callback_url') + %th= t('admin.subscriptions.confirmed') + %th= t('admin.subscriptions.expires_in') + %th= t('admin.subscriptions.last_delivery') + %tbody + = render @subscriptions = paginate @subscriptions diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml index 7ac578bb17..c1e9764b3c 100644 --- a/app/views/auth/registrations/_sessions.html.haml +++ b/app/views/auth/registrations/_sessions.html.haml @@ -1,28 +1,29 @@ %h6= t 'sessions.title' %p.muted-hint= t 'sessions.explanation' -%table.table.inline-table - %thead - %tr - %th= t 'sessions.browser' - %th= t 'sessions.ip' - %th= t 'sessions.activity' - %td - %tbody - - @sessions.each do |session| +.table-wrapper + %table.table.inline-table + %thead %tr + %th= t 'sessions.browser' + %th= t 'sessions.ip' + %th= t 'sessions.activity' %td - %span{ title: session.user_agent }< - = fa_icon "#{session_device_icon(session)} fw", 'aria-label' => session_device_icon(session) - = ' ' - = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}"), platform: t("sessions.platforms.#{session.platform}") - %td - %samp= session.ip - %td - - if current_session.session_id == session.session_id - = t 'sessions.current_session' - - else - %time.time-ago{ datetime: session.updated_at.iso8601, title: l(session.updated_at) }= l(session.updated_at) - %td - - if current_session.session_id != session.session_id - = table_link_to 'times', t('sessions.revoke'), settings_session_path(session), method: :delete + %tbody + - @sessions.each do |session| + %tr + %td + %span{ title: session.user_agent }< + = fa_icon "#{session_device_icon(session)} fw", 'aria-label' => session_device_icon(session) + = ' ' + = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}"), platform: t("sessions.platforms.#{session.platform}") + %td + %samp= session.ip + %td + - if current_session.session_id == session.session_id + = t 'sessions.current_session' + - else + %time.time-ago{ datetime: session.updated_at.iso8601, title: l(session.updated_at) }= l(session.updated_at) + %td + - if current_session.session_id != session.session_id + = table_link_to 'times', t('sessions.revoke'), settings_session_path(session), method: :delete diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index ba0c084950..19af5f55db 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -1,23 +1,24 @@ - content_for :page_title do = t('doorkeeper.authorized_applications.index.title') -%table.table - %thead - %tr - %th= t('doorkeeper.authorized_applications.index.application') - %th= t('doorkeeper.authorized_applications.index.scopes') - %th= t('doorkeeper.authorized_applications.index.created_at') - %th - %tbody - - @applications.each do |application| +.table-wrapper + %table.table + %thead %tr - %td - - if application.website.blank? - = application.name - - else - = link_to application.name, application.website, target: '_blank', rel: 'noopener' - %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join('
') - %td= l application.created_at - %td - - unless application.superapp? - = table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') } + %th= t('doorkeeper.authorized_applications.index.application') + %th= t('doorkeeper.authorized_applications.index.scopes') + %th= t('doorkeeper.authorized_applications.index.created_at') + %th + %tbody + - @applications.each do |application| + %tr + %td + - if application.website.blank? + = application.name + - else + = link_to application.name, application.website, target: '_blank', rel: 'noopener' + %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join('
') + %td= l application.created_at + %td + - unless application.superapp? + = table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') } diff --git a/app/views/settings/applications/index.html.haml b/app/views/settings/applications/index.html.haml index eea550388a..919472c2e8 100644 --- a/app/views/settings/applications/index.html.haml +++ b/app/views/settings/applications/index.html.haml @@ -1,19 +1,20 @@ - content_for :page_title do = t('doorkeeper.applications.index.title') -%table.table - %thead - %tr - %th= t('doorkeeper.applications.index.application') - %th= t('doorkeeper.applications.index.scopes') - %th - %tbody - - @applications.each do |application| +.table-wrapper + %table.table + %thead %tr - %td= link_to application.name, settings_application_path(application) - %th= application.scopes - %td - = table_link_to 'times', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') } + %th= t('doorkeeper.applications.index.application') + %th= t('doorkeeper.applications.index.scopes') + %th + %tbody + - @applications.each do |application| + %tr + %td= link_to application.name, settings_application_path(application) + %th= application.scopes + %td + = table_link_to 'times', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') } = paginate @applications = link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button' diff --git a/app/views/settings/applications/show.html.haml b/app/views/settings/applications/show.html.haml index 4d85551111..12baed0881 100644 --- a/app/views/settings/applications/show.html.haml +++ b/app/views/settings/applications/show.html.haml @@ -3,22 +3,23 @@ %p.hint= t('applications.warning') -%table.table - %tbody - %tr - %th= t('doorkeeper.applications.show.application_id') - %td - %code= @application.uid - %tr - %th= t('doorkeeper.applications.show.secret') - %td - %code= @application.secret - %tr - %th{ rowspan: 2}= t('applications.your_token') - %td - %code= current_user.token_for_app(@application).token - %tr - %td= table_link_to 'refresh', t('applications.regenerate_token'), regenerate_settings_application_path(@application), method: :post +.table-wrapper + %table.table + %tbody + %tr + %th= t('doorkeeper.applications.show.application_id') + %td + %code= @application.uid + %tr + %th= t('doorkeeper.applications.show.secret') + %td + %code= @application.secret + %tr + %th{ rowspan: 2}= t('applications.your_token') + %td + %code= current_user.token_for_app(@application).token + %tr + %td= table_link_to 'refresh', t('applications.regenerate_token'), regenerate_settings_application_path(@application), method: :post %hr/ diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml index f2f6f95563..e0df1c4802 100644 --- a/app/views/settings/exports/show.html.haml +++ b/app/views/settings/exports/show.html.haml @@ -1,21 +1,22 @@ - content_for :page_title do = t('settings.export') -%table.table - %tbody - %tr - %th= t('exports.storage') - %td= number_to_human_size @export.total_storage - %td - %tr - %th= t('exports.follows') - %td= @export.total_follows - %td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv) - %tr - %th= t('exports.blocks') - %td= @export.total_blocks - %td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv) - %tr - %th= t('exports.mutes') - %td= @export.total_mutes - %td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv) +.table-wrapper + %table.table + %tbody + %tr + %th= t('exports.storage') + %td= number_to_human_size @export.total_storage + %td + %tr + %th= t('exports.follows') + %td= @export.total_follows + %td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv) + %tr + %th= t('exports.blocks') + %td= @export.total_blocks + %td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv) + %tr + %th= t('exports.mutes') + %td= @export.total_mutes + %td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv) diff --git a/app/views/settings/follower_domains/show.html.haml b/app/views/settings/follower_domains/show.html.haml index dad2770f10..f1687d4d2f 100644 --- a/app/views/settings/follower_domains/show.html.haml +++ b/app/views/settings/follower_domains/show.html.haml @@ -12,20 +12,21 @@ %p= t('followers.explanation_html') %p= t('followers.true_privacy_html') - %table.table - %thead - %tr - %th - %th= t('followers.domain') - %th= t('followers.followers_count') - %tbody - - @domains.each do |domain| + .table-wrapper + %table.table + %thead %tr - %td - = check_box_tag 'select[]', domain.domain, false, disabled: !@account.locked? unless domain.domain.nil? - %td - %samp= domain.domain.presence || Rails.configuration.x.local_domain - %td= number_with_delimiter domain.accounts_from_domain + %th + %th= t('followers.domain') + %th= t('followers.followers_count') + %tbody + - @domains.each do |domain| + %tr + %td + = check_box_tag 'select[]', domain.domain, false, disabled: !@account.locked? unless domain.domain.nil? + %td + %samp= domain.domain.presence || Rails.configuration.x.local_domain + %td= number_with_delimiter domain.accounts_from_domain .action-pagination .actions From 9e15eeec63b36f147700303961ad5ef207b81986 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 13:41:45 +0200 Subject: [PATCH 21/27] Add missing reject_media check before avatar download via ActivityPub (#4862) --- app/services/activitypub/process_account_service.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index c63577fe5d..b54e447ad0 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -48,14 +48,14 @@ class ActivityPub::ProcessAccountService < BaseService @account.url = url || @uri @account.display_name = @json['name'] || '' @account.note = @json['summary'] || '' - @account.avatar_remote_url = image_url('icon') - @account.header_remote_url = image_url('image') + @account.avatar_remote_url = image_url('icon') unless skip_download? + @account.header_remote_url = image_url('image') unless skip_download? @account.public_key = public_key || '' @account.locked = @json['manuallyApprovesFollowers'] || false @account.statuses_count = outbox_total_items if outbox_total_items.present? @account.following_count = following_total_items if following_total_items.present? @account.followers_count = followers_total_items if followers_total_items.present? - @account.save! + @account.save_with_optional_media! end def upgrade_account @@ -115,6 +115,10 @@ class ActivityPub::ProcessAccountService < BaseService @collections[type] = nil end + def skip_download? + @account.suspended? || domain_block&.reject_media? + end + def auto_suspend? domain_block && domain_block.suspend? end From c9d04f1c39dc95a54d058eaed794c2fcb9e62b6b Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 9 Sep 2017 20:42:48 +0900 Subject: [PATCH 22/27] Fix second report (regression from 3b81baaaaf51ff1c70fb1f865eef07fdb33a5950) (#4863) --- app/javascript/mastodon/reducers/reports.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/reducers/reports.js b/app/javascript/mastodon/reducers/reports.js index 283c5b6f5e..a08bbec387 100644 --- a/app/javascript/mastodon/reducers/reports.js +++ b/app/javascript/mastodon/reducers/reports.js @@ -28,7 +28,7 @@ export default function reports(state = initialState, action) { if (state.getIn(['new', 'account_id']) !== action.account.get('id')) { map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet()); map.setIn(['new', 'comment'], ''); - } else { + } else if (action.status) { map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id')))); } }); From c7908e2d094481537a96ef2a1b13c6159744210b Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 9 Sep 2017 21:16:11 +0900 Subject: [PATCH 23/27] Fix scroll behavior and others on paused timeline (#4864) Resolved: * Lot of redundant renders while mouse moving * Scroll jumping when timeline loaded * Scroll position isn't kept when statuses below the scrollTop was deleted then new status arrived Unresolved: * Scroll position isn't kept when statuses over the scrollTop was deleted then new status arrived -> It needs to know which statuses are over the scrollTop * New status indicator should be active when new statuses arrived while mouse moved recently -> It needs a) update indicator in ScrollableList, or b) set scrollTop status while mouse moving --- .../mastodon/components/scrollable_list.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index 42763ddd3d..aeb0b0f4b5 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -52,11 +52,11 @@ export default class ScrollableList extends PureComponent { }); handleMouseMove = throttle(() => { - this.setState({ lastMouseMove: new Date() }); + this._lastMouseMove = new Date(); }, 300); handleMouseLeave = () => { - this.setState({ lastMouseMove: null }); + this._lastMouseMove = null; } componentDidMount () { @@ -68,18 +68,20 @@ export default class ScrollableList extends PureComponent { } componentDidUpdate (prevProps) { + const someItemInserted = React.Children.count(prevProps.children) > 0 && + React.Children.count(prevProps.children) < React.Children.count(this.props.children) && + this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); + // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. - if (React.Children.count(prevProps.children) < React.Children.count(this.props.children) && this._oldScrollPosition && (this.node.scrollTop > 0 || this._recentlyMoved())) { - if (this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props)) { - const newScrollTop = this.node.scrollHeight - this._oldScrollPosition; + if (someItemInserted && this._oldScrollPosition && (this.node.scrollTop > 0 || this._recentlyMoved())) { + const newScrollTop = this.node.scrollHeight - this._oldScrollPosition; - if (this.node.scrollTop !== newScrollTop) { - this.node.scrollTop = newScrollTop; - } - } else { - this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop; + if (this.node.scrollTop !== newScrollTop) { + this.node.scrollTop = newScrollTop; } + } else { + this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop; } } @@ -128,7 +130,7 @@ export default class ScrollableList extends PureComponent { } _recentlyMoved () { - return this.state.lastMouseMove === null || ((new Date()) - this.state.lastMouseMove < 600); + return this._lastMouseMove !== null && ((new Date()) - this._lastMouseMove < 600); } handleKeyDown = (e) => { From 2ff7146b6d7ce408badd4ff4bcc323b2fd9b774c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 14:53:49 +0200 Subject: [PATCH 24/27] Bump version to 1.6.0rc4 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index de2516d6c2..3ab705e260 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -21,7 +21,7 @@ module Mastodon end def flags - 'rc2' + 'rc4' end def to_a From bdc8b4fd91aa90c624bca6cd6fe202876b4f654f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 15:09:50 +0200 Subject: [PATCH 25/27] Disable mouse-based pause from #4859 (#4865) It wasn't working ideally and introduced some annoying false positivies --- app/javascript/mastodon/components/scrollable_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index aeb0b0f4b5..723dd322b0 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -74,7 +74,7 @@ export default class ScrollableList extends PureComponent { // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. - if (someItemInserted && this._oldScrollPosition && (this.node.scrollTop > 0 || this._recentlyMoved())) { + if (someItemInserted && this._oldScrollPosition && this.node.scrollTop > 0) { const newScrollTop = this.node.scrollHeight - this._oldScrollPosition; if (this.node.scrollTop !== newScrollTop) { From 6867681c7c0b4a5ec48511c013c3f3aa8684bdae Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Sep 2017 16:23:44 +0200 Subject: [PATCH 26/27] Add script to make embedded iframes autosize (#4853) --- .../features/ui/components/embed_modal.js | 4 +- app/javascript/packs/public.js | 34 ++++++++++++--- app/javascript/styles/basics.scss | 1 + app/serializers/oembed_serializer.rb | 4 +- public/embed.js | 43 +++++++++++++++++++ 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 public/embed.js diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.js index 992aed8a3f..1afffb51bc 100644 --- a/app/javascript/mastodon/features/ui/components/embed_modal.js +++ b/app/javascript/mastodon/features/ui/components/embed_modal.js @@ -33,7 +33,8 @@ export default class EmbedModal extends ImmutablePureComponent { iframeDocument.close(); iframeDocument.body.style.margin = 0; - this.iframe.height = iframeDocument.body.scrollHeight + 'px'; + this.iframe.width = iframeDocument.body.scrollWidth; + this.iframe.height = iframeDocument.body.scrollHeight; }); } @@ -71,7 +72,6 @@ export default class EmbedModal extends ImmutablePureComponent {