diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 10c548b294..c1197a6961 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -25,8 +25,8 @@ class Api::V1::StatusesController < Api::BaseController
def context
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(CONTEXT_LIMIT, current_account)
- treeId = ENV['TREE_ADDRESS'].split('/')[-1].to_i
- depth = @status.id == treeId ? 1 : ((!ancestors_results.empty? && ancestors_results[0].id == treeId) ? 2 : nil)
+ treeId = Rails.configuration.x.tree_address.split('/')[-1].to_i
+ depth = (@status.id == treeId || (!ancestors_results.empty? && ancestors_results[0].id == treeId)) ? 1 : nil
descendants_results = @status.descendants(CONTEXT_LIMIT, current_account, nil, nil, depth)
loaded_ancestors = cache_collection(ancestors_results, Status)
@@ -50,9 +50,10 @@ class Api::V1::StatusesController < Api::BaseController
end
def create
- masked = status_params[:status].end_with?('[mask]')
- sender = masked ? Account.find_local('mask_bot') : current_user.account
- st_text = masked ? ("$#{to_cn(7919**(current_account.id + 1000 * Time.new.day) % 1000000007)}:\n" + status_params[:status][0..4900]) : status_params[:status]
+ p Rails.configuration.x.anon_tag
+ anon = Rails.configuration.x.anon_acc && status_params[:status].end_with?(Rails.configuration.x.anon_tag)
+ sender = anon ? Account.find(Rails.configuration.x.anon_acc) : current_user.account
+ st_text = anon ? ("$#{to_cn(7919**(current_account.id + 1000 * Time.new.day) % 1000000007)}:\n" + status_params[:status][0..4990]) : status_params[:status]
@status = PostStatusService.new.call(sender,
text: st_text,
diff --git a/app/controllers/api/v1/statuses_controller.rb.orig b/app/controllers/api/v1/statuses_controller.rb.orig
deleted file mode 100644
index 3aefc886fa..0000000000
--- a/app/controllers/api/v1/statuses_controller.rb.orig
+++ /dev/null
@@ -1,126 +0,0 @@
-# frozen_string_literal: true
-
-class Api::V1::StatusesController < Api::BaseController
- include Authorization
-
- before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
- before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
- before_action :require_user!, except: [:show, :context]
- before_action :set_status, only: [:show, :context]
- before_action :set_thread, only: [:create]
-
- override_rate_limit_headers :create, family: :statuses
-
- # This API was originally unlimited, pagination cannot be introduced without
- # breaking backwards-compatibility. Arbitrarily high number to cover most
- # conversations as quasi-unlimited, it would be too much work to render more
- # than this anyway
- CONTEXT_LIMIT = 4_096
-
- def show
- @status = cache_collection([@status], Status).first
- render json: @status, serializer: REST::StatusSerializer
- end
-
- def context
- ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(CONTEXT_LIMIT, current_account)
-
- treeId = ENV['TREE_ADDRESS'].split('/')[-1].to_i
- depth = @status.id == treeId ? 1 : ((!ancestors_results.empty? && ancestors_results[0].id == treeId) ? 2 : nil)
-
- descendants_results = @status.descendants(CONTEXT_LIMIT, current_account, nil, nil, depth)
- loaded_ancestors = cache_collection(ancestors_results, Status)
- loaded_descendants = cache_collection(descendants_results, Status)
-
- @context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
- statuses = [@status] + @context.ancestors + @context.descendants
-
- render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
- end
-
- def to_cn(n)
- case Time.new.wday
- when 0, 3
- "秦汉魏晋隋唐宋元明清"[n % 10] + [n % 20873, n % 20899].map{|i| i+0x4e00}.pack('U*')
- when 1, 4, 6
- "甲乙丙丁戊己庚辛壬癸"[n % 10] + [n % 20873, n % 20899].map{|i| i+0x4e00}.pack('U*')
- else
- "鼠牛虎兔龙蛇马羊猴鸡狗猪" [n % 12] + [n % 20873, n % 20899].map{|i| i+0x4e00}.pack('U*')
- end
- end
-
- def create
-<<<<<<< HEAD
- thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id])
- masked = status_params[:status].end_with?('[mask]')
- sender = masked ? Account.find_local('mask_bot') : current_account
- st_text = masked ? ("$#{to_cn(7919**(current_account.id + 1000 * Time.new.day) % 1000000007)}:\n" + status_params[:status][0..4900]) : status_params[:status]
-
- @status = PostStatusService.new.call(sender,
- text: st_text,
- thread: thread,
-=======
- @status = PostStatusService.new.call(current_user.account,
- text: status_params[:status],
- thread: @thread,
->>>>>>> master
- media_ids: status_params[:media_ids],
- sensitive: status_params[:sensitive],
- spoiler_text: status_params[:spoiler_text],
- visibility: status_params[:visibility],
- scheduled_at: status_params[:scheduled_at],
- application: doorkeeper_token.application,
- poll: status_params[:poll],
- idempotency: request.headers['Idempotency-Key'],
- with_rate_limit: true)
-
- render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
- end
-
- def destroy
- @status = Status.where(account_id: current_user.account).find(params[:id])
- authorize @status, :destroy?
-
- @status.discard
- RemovalWorker.perform_async(@status.id, redraft: true)
-
- render json: @status, serializer: REST::StatusSerializer, source_requested: true
- end
-
- private
-
- def set_status
- @status = Status.find(params[:id])
- authorize @status, :show?
- rescue Mastodon::NotPermittedError
- not_found
- end
-
- def set_thread
- @thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id])
- rescue ActiveRecord::RecordNotFound
- render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
- end
-
- def status_params
- params.permit(
- :status,
- :in_reply_to_id,
- :sensitive,
- :spoiler_text,
- :visibility,
- :scheduled_at,
- media_ids: [],
- poll: [
- :multiple,
- :hide_totals,
- :expires_in,
- options: [],
- ]
- )
- end
-
- def pagination_params(core_params)
- params.slice(:limit).permit(:limit).merge(core_params)
- end
-end
diff --git a/app/helpers/statuses_helper.rb.orig b/app/helpers/statuses_helper.rb.orig
deleted file mode 100644
index 771537c5ad..0000000000
--- a/app/helpers/statuses_helper.rb.orig
+++ /dev/null
@@ -1,218 +0,0 @@
-# frozen_string_literal: true
-
-module StatusesHelper
- EMBEDDED_CONTROLLER = 'statuses'
- EMBEDDED_ACTION = 'embed'
-
-<<<<<<< HEAD
- def display_name(account, **options)
- if options[:custom_emojify]
- Formatter.instance.format_display_name(account, options)
- else
- account.display_name.presence || account.username
- end
- end
-
- def account_action_button(account)
- if user_signed_in?
- if account.id == current_user.account_id
- link_to settings_profile_url, class: 'button logo-button' do
- safe_join([svg_logo, t('settings.edit_profile')])
- end
- elsif current_account.following?(account) || current_account.requested?(account)
- link_to account_unfollow_path(account), class: 'button logo-button button--destructive', data: { method: :post } do
- safe_join([svg_logo, t('accounts.unfollow')])
- end
- elsif !(account.memorial? || account.moved?)
- link_to account_follow_path(account), class: "button logo-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post } do
- safe_join([svg_logo, t('accounts.follow')])
- end
- end
- elsif !(account.memorial? || account.moved?)
- link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
- safe_join([svg_logo, t('accounts.follow')])
- end
- end
- end
-
- def minimal_account_action_button(account)
- if user_signed_in?
- return if account.id == current_user.account_id
-
- if current_account.following?(account) || current_account.requested?(account)
- link_to account_unfollow_path(account), class: 'icon-button active', data: { method: :post }, title: t('accounts.unfollow') do
- fa_icon('user-times fw')
- end
- elsif !(account.memorial? || account.moved?)
- link_to account_follow_path(account), class: "icon-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post }, title: t('accounts.follow') do
- fa_icon('user-plus fw')
- end
- end
- elsif !(account.memorial? || account.moved?)
- link_to account_remote_follow_path(account), class: 'icon-button modal-button', target: '_new', title: t('accounts.follow') do
- fa_icon('user-plus fw')
- end
- end
- end
-
- def svg_logo
- content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976')
- end
-
- def svg_logo_full
- content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo-full'), 'viewBox' => '0 0 180 80')
- end
-
- def account_badge(account, all: false)
- if account.bot?
- content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
- elsif (Setting.show_staff_badge && account.user_staff?) || all
- content_tag(:div, class: 'roles') do
- if all && !account.user_staff?
- content_tag(:div, t('admin.accounts.roles.user'), class: 'account-role')
- elsif account.user_admin?
- content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
- elsif account.user_moderator?
- content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
- end
- end
- end
- end
-
-=======
->>>>>>> master
- def link_to_more(url)
- link_to t('statuses.show_more'), url, class: 'load-more load-gap'
- end
-
- def nothing_here(extra_classes = '')
- content_tag(:div, class: "nothing-here #{extra_classes}") do
- t('accounts.nothing_here')
- end
- end
-
- def media_summary(status)
- attachments = { image: 0, video: 0, audio: 0 }
-
- status.media_attachments.each do |media|
- if media.video?
- attachments[:video] += 1
- elsif media.audio?
- attachments[:audio] += 1
- else
- attachments[:image] += 1
- end
- end
-
- text = attachments.to_a.reject { |_, value| value.zero? }.map { |key, value| I18n.t("statuses.attached.#{key}", count: value) }.join(' · ')
-
- return if text.blank?
-
- I18n.t('statuses.attached.description', attached: text)
- end
-
- def status_text_summary(status)
- return if status.spoiler_text.blank?
-
- I18n.t('statuses.content_warning', warning: status.spoiler_text)
- end
-
- def poll_summary(status)
- return unless status.preloadable_poll
-
- status.preloadable_poll.options.map { |o| "[ ] #{o}" }.join("\n")
- end
-
- def status_description(status)
- components = [[media_summary(status), status_text_summary(status)].reject(&:blank?).join(' · ')]
-
- if status.spoiler_text.blank?
- components << status.text
- components << poll_summary(status)
- end
-
- components.reject(&:blank?).join("\n\n")
- end
-
- def stream_link_target
- embedded_view? ? '_blank' : nil
- end
-
- def style_classes(status, is_predecessor, is_successor, include_threads)
- classes = ['entry']
- classes << 'entry-predecessor' if is_predecessor
- classes << 'entry-reblog' if status.reblog?
- classes << 'entry-successor' if is_successor
- classes << 'entry-center' if include_threads
- classes.join(' ')
- end
-
- def microformats_classes(status, is_direct_parent, is_direct_child)
- classes = []
- classes << 'p-in-reply-to' if is_direct_parent
- classes << 'p-repost-of' if status.reblog? && is_direct_parent
- classes << 'p-comment' if is_direct_child
- classes.join(' ')
- end
-
- def microformats_h_class(status, is_predecessor, is_successor, include_threads)
- if is_predecessor || status.reblog? || is_successor
- 'h-cite'
- elsif include_threads
- ''
- else
- 'h-entry'
- end
- end
-
- def rtl_status?(status)
- status.local? ? rtl?(status.text) : rtl?(strip_tags(status.text))
- end
-
- def rtl?(text)
- text = simplified_text(text)
- rtl_words = text.scan(/[\p{Hebrew}\p{Arabic}\p{Syriac}\p{Thaana}\p{Nko}]+/m)
-
- if rtl_words.present?
- total_size = text.size.to_f
- rtl_size(rtl_words) / total_size > 0.3
- else
- false
- end
- end
-
- def fa_visibility_icon(status)
- case status.visibility
- when 'public'
- fa_icon 'globe fw'
- when 'unlisted'
- fa_icon 'unlock fw'
- when 'private'
- fa_icon 'lock fw'
- when 'direct'
- fa_icon 'envelope fw'
- end
- end
-
- private
-
- def simplified_text(text)
- text.dup.tap do |new_text|
- URI.extract(new_text).each do |url|
- new_text.gsub!(url, '')
- end
-
- new_text.gsub!(Account::MENTION_RE, '')
- new_text.gsub!(Tag::HASHTAG_RE, '')
- new_text.gsub!(/\s+/, '')
- end
- end
-
- def rtl_size(words)
- words.reduce(0) { |acc, elem| acc + elem.size }.to_f
- end
-
- def embedded_view?
- params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION
- end
-end
diff --git a/app/javascript/mastodon/components/icon_button.js.orig b/app/javascript/mastodon/components/icon_button.js.orig
deleted file mode 100644
index 101c7e6c0e..0000000000
--- a/app/javascript/mastodon/components/icon_button.js.orig
+++ /dev/null
@@ -1,153 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import Icon from 'mastodon/components/icon';
-
-export default class IconButton extends React.PureComponent {
-
- static propTypes = {
- className: PropTypes.string,
- title: PropTypes.string.isRequired,
- icon: PropTypes.string.isRequired,
- onClick: PropTypes.func,
- onMouseDown: PropTypes.func,
- onKeyDown: PropTypes.func,
- onKeyPress: PropTypes.func,
- size: PropTypes.number,
- active: PropTypes.bool,
- pressed: PropTypes.bool,
- expanded: PropTypes.bool,
- style: PropTypes.object,
- activeStyle: PropTypes.object,
- disabled: PropTypes.bool,
- inverted: PropTypes.bool,
- animate: PropTypes.bool,
- overlay: PropTypes.bool,
- tabIndex: PropTypes.string,
- };
-
- static defaultProps = {
- size: 18,
- active: false,
- disabled: false,
- animate: false,
- overlay: false,
- tabIndex: '0',
- };
-
- state = {
- activate: false,
- deactivate: false,
- }
-
- componentWillReceiveProps (nextProps) {
- if (!nextProps.animate) return;
-
- if (this.props.active && !nextProps.active) {
- this.setState({ activate: false, deactivate: true });
- } else if (!this.props.active && nextProps.active) {
- this.setState({ activate: true, deactivate: false });
- }
- }
-
- handleClick = (e) => {
- e.preventDefault();
-
- if (!this.props.disabled) {
- this.props.onClick(e);
- }
- }
-
- handleKeyPress = (e) => {
- if (this.props.onKeyPress && !this.props.disabled) {
- this.props.onKeyPress(e);
- }
- }
-
- handleMouseDown = (e) => {
- if (!this.props.disabled && this.props.onMouseDown) {
- this.props.onMouseDown(e);
- }
- }
-
- handleKeyDown = (e) => {
- if (!this.props.disabled && this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- render () {
- const style = {
- fontSize: `${this.props.size}px`,
- width: `${this.props.size * 1.28571429}px`,
- height: `${this.props.size * 1.28571429}px`,
- lineHeight: `${this.props.size}px`,
- ...this.props.style,
- ...(this.props.active ? this.props.activeStyle : {}),
- };
-
- const {
- active,
- className,
- disabled,
- expanded,
- icon,
- inverted,
- overlay,
- pressed,
- tabIndex,
- title,
- } = this.props;
-
- const {
- activate,
- deactivate,
- } = this.state;
-
- const classes = classNames(className, 'icon-button', {
- active,
- disabled,
- inverted,
- activate,
- deactivate,
- overlayed: overlay,
- });
-
- return (
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/components/status.js.orig b/app/javascript/mastodon/components/status.js.orig
deleted file mode 100644
index b0db14614d..0000000000
--- a/app/javascript/mastodon/components/status.js.orig
+++ /dev/null
@@ -1,631 +0,0 @@
-import React from 'react';
-import Immutable from 'immutable';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from './avatar';
-import AvatarOverlay from './avatar_overlay';
-import AvatarComposite from './avatar_composite';
-import RelativeTimestamp from './relative_timestamp';
-import DisplayName from './display_name';
-import StatusContent from './status_content';
-import StatusActionBar from './status_action_bar';
-import AttachmentList from './attachment_list';
-import Card from '../features/status/components/card';
-<<<<<<< HEAD
-import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
-=======
-import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
->>>>>>> master
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
-import { HotKeys } from 'react-hotkeys';
-import classNames from 'classnames';
-import Icon from 'mastodon/components/icon';
-import { displayMedia } from '../initial_state';
-
-import StatusContainer from '../containers/status_container2';
-
-// We use the component (and not the container) since we do not want
-// to use the progress bar to show download progress
-import Bundle from '../features/ui/components/bundle';
-
-export const textForScreenReader = (intl, status, rebloggedByText = false) => {
- const displayName = status.getIn(['account', 'display_name']);
-
- const values = [
- displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName,
- status.get('spoiler_text') && status.get('hidden') ? status.get('spoiler_text') : status.get('search_index').slice(status.get('spoiler_text').length),
- intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }),
- status.getIn(['account', 'acct']),
- ];
-
- if (rebloggedByText) {
- values.push(rebloggedByText);
- }
-
- return values.join(', ');
-};
-
-export const defaultMediaVisibility = (status) => {
- if (!status) {
- return undefined;
- }
-
- if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
- status = status.get('reblog');
- }
-
- return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all');
-};
-
-const messages = defineMessages({
- public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
- unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
- private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
- direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-});
-
-export default @injectIntl
-class Status extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- status: ImmutablePropTypes.map,
- account: ImmutablePropTypes.map,
- otherAccounts: ImmutablePropTypes.list,
- onClick: PropTypes.func,
- onReply: PropTypes.func,
- onFavourite: PropTypes.func,
- onReblog: PropTypes.func,
- onDelete: PropTypes.func,
- onDirect: PropTypes.func,
- onMention: PropTypes.func,
- onPin: PropTypes.func,
- onOpenMedia: PropTypes.func,
- onOpenVideo: PropTypes.func,
- onBlock: PropTypes.func,
- onEmbed: PropTypes.func,
- onHeightChange: PropTypes.func,
- onToggleHidden: PropTypes.func,
- onToggleCollapsed: PropTypes.func,
- muted: PropTypes.bool,
- hidden: PropTypes.bool,
- unread: PropTypes.bool,
- onMoveUp: PropTypes.func,
- onMoveDown: PropTypes.func,
- showThread: PropTypes.bool,
- getScrollPosition: PropTypes.func,
- updateScrollBottom: PropTypes.func,
- cacheMediaWidth: PropTypes.func,
- cachedMediaWidth: PropTypes.number,
-
- sonsIds: ImmutablePropTypes.list,
- onPreview: PropTypes.func
- };
-
- // Avoid checking props that are functions (and whose equality will always
- // evaluate to false. See react-immutable-pure-component for usage.
- updateOnProps = [
- 'status',
- 'account',
- 'muted',
- 'hidden',
- 'sonsIds',
- 'ancestorsText',
- ];
-
- state = {
- showMedia: defaultMediaVisibility(this.props.status),
- statusId: undefined,
- noStartPD: true
- };
-
-<<<<<<< HEAD
- _isMounted = false;
- // Track height changes we know about to compensate scrolling
- componentDidMount () {
- this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
- this._isMounted = true;
- setTimeout(this.loadContext, Math.ceil(Math.random() * 4000 + 1000));
- }
-
- getSnapshotBeforeUpdate () {
- if (this.props.getScrollPosition) {
- return this.props.getScrollPosition();
- } else {
- return null;
- }
- }
-
-=======
->>>>>>> master
- static getDerivedStateFromProps(nextProps, prevState) {
- if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
- return {
- showMedia: defaultMediaVisibility(nextProps.status),
- statusId: nextProps.status.get('id'),
- };
- } else {
- return null;
- }
- }
-
-<<<<<<< HEAD
- // Compensate height changes
- componentDidUpdate (prevProps, prevState, snapshot) {
- const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-
- if (doShowCard && !this.didShowCard) {
- this.didShowCard = true;
-
- if (snapshot !== null && this.props.updateScrollBottom) {
- if (this.node && this.node.offsetTop < snapshot.top) {
- this.props.updateScrollBottom(snapshot.height - snapshot.top);
- }
- }
- }
- }
-
- componentWillUnmount() {
- if (this.node && this.props.getScrollPosition) {
- const position = this.props.getScrollPosition();
- if (position !== null && this.node.offsetTop < position.top) {
- requestAnimationFrame(() => {
- this.props.updateScrollBottom(position.height - position.top);
- });
- }
- }
-
- this._isMounted = false;
- }
-
-=======
->>>>>>> master
- handleToggleMediaVisibility = () => {
- this.setState({ showMedia: !this.state.showMedia });
- }
-
- handleClick = () => {
- if (this.props.onClick) {
- this.props.onClick();
- return;
- }
-
- if (!this.context.router) {
- return;
- }
-
- const { status } = this.props;
- this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
- }
-
- loadContext = () => {
- if(!this._isMounted) {
- //console.log('cancel');
- return;
- }
- const { status } = this.props;
- const r_status = status.get('reblog') || status;
- if(this.props.showThread && this.state.noStartPD && (r_status.get('replies_count') || r_status.get('in_reply_to_id') || r_status.get('visibility') == 'private')) {
- this.setState({noStartPD: false});
- this.props.onPreview(r_status.get('id'));
- }
- }
-
- handleExpandClick = (e) => {
- if (this.props.onClick) {
- this.props.onClick();
- return;
- }
-
- if (e.button === 0) {
- if (!this.context.router) {
- return;
- }
-
- const { status } = this.props;
- this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
- }
- }
-
- handleAccountClick = (e) => {
- if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
- const id = e.currentTarget.getAttribute('data-id');
- e.preventDefault();
- this.context.router.history.push(`/accounts/${id}`);
- }
- }
-
- handleExpandedToggle = () => {
- this.props.onToggleHidden(this._properStatus());
- }
-
- handleCollapsedToggle = isCollapsed => {
- this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
- }
-
- renderLoadingMediaGallery () {
- return
;
- }
-
- renderLoadingVideoPlayer () {
- return ;
- }
-
- renderLoadingAudioPlayer () {
- return ;
- }
-
- handleOpenVideo = (media, options) => {
- this.props.onOpenVideo(media, options);
- }
-
- handleHotkeyOpenMedia = e => {
- const { onOpenMedia, onOpenVideo } = this.props;
- const status = this._properStatus();
-
- e.preventDefault();
-
- if (status.get('media_attachments').size > 0) {
- if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
- // TODO: toggle play/paused?
- } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
- onOpenVideo(status.getIn(['media_attachments', 0]), { startTime: 0 });
- } else {
- onOpenMedia(status.get('media_attachments'), 0);
- }
- }
- }
-
- handleHotkeyReply = e => {
- e.preventDefault();
- this.props.onReply(this._properStatus(), this.context.router.history);
- }
-
- handleHotkeyFavourite = () => {
- this.props.onFavourite(this._properStatus());
- }
-
- handleHotkeyBoost = e => {
- this.props.onReblog(this._properStatus(), e);
- }
-
- handleHotkeyMention = e => {
- e.preventDefault();
- this.props.onMention(this._properStatus().get('account'), this.context.router.history);
- }
-
- handleHotkeyOpen = () => {
- this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`);
- }
-
- handleHotkeyOpenProfile = () => {
- this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`);
- }
-
- handleHotkeyMoveUp = e => {
- this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
- }
-
- handleHotkeyMoveDown = e => {
- this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
- }
-
- handleHotkeyToggleHidden = () => {
- this.props.onToggleHidden(this._properStatus());
- }
-
- handleHotkeyToggleSensitive = () => {
- this.handleToggleMediaVisibility();
- }
-
- _properStatus () {
- const { status } = this.props;
-
- if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
- return status.get('reblog');
- } else {
- return status;
- }
- }
-
- handleRef = c => {
- this.node = c;
- }
-
-
- renderChildren (list) {
- return list.map(e => (
- e.id ?
-
-
{}}
- onMoveDown={()=>{}}
- contextType='comments-timeline'
- />
- { e.sonsIds &&
- {this.renderChildren(e.sonsIds)}
- }
-
- :
- {}}
- onMoveDown={()=>{}}
- contextType='comments-timeline'
- />
- ));
-
- }
-
- render () {
- let media = null;
- let statusAvatar, prepend, rebloggedByText;
- let sons, quote;
-
- const { intl, hidden, featured, otherAccounts, unread, showThread, deep, tree_type, ancestorsText, sonsIds } = this.props;
-
- let { status, account, ...other } = this.props;
-
- if (status === null) {
- return null;
- }
-
- const handlers = this.props.muted ? {} : {
- reply: this.handleHotkeyReply,
- favourite: this.handleHotkeyFavourite,
- boost: this.handleHotkeyBoost,
- mention: this.handleHotkeyMention,
- open: this.handleHotkeyOpen,
- openProfile: this.handleHotkeyOpenProfile,
- moveUp: this.handleHotkeyMoveUp,
- moveDown: this.handleHotkeyMoveDown,
- toggleHidden: this.handleHotkeyToggleHidden,
- toggleSensitive: this.handleHotkeyToggleSensitive,
- openMedia: this.handleHotkeyOpenMedia,
- };
-
- if (hidden) {
- return (
-
-
- {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
- {status.get('content')}
-
-
- );
- }
-
- if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) {
- const minHandlers = this.props.muted ? {} : {
- moveUp: this.handleHotkeyMoveUp,
- moveDown: this.handleHotkeyMoveDown,
- };
-
- return (
-
-
-
-
-
- );
- }
-
- if (featured) {
- prepend = (
-
- );
- } else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
- const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
-
- prepend = (
-
- );
-
- rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
-
- account = status.get('account');
- status = status.get('reblog');
- }
-
- if (status.get('media_attachments').size > 0) {
- if (this.props.muted) {
- media = (
-
- );
- } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
- const attachment = status.getIn(['media_attachments', 0]);
-
- media = (
-
- {Component => (
-
- )}
-
- );
- } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
- const attachment = status.getIn(['media_attachments', 0]);
-
- media = (
-
- {Component => (
-
- )}
-
- );
- } else {
- media = (
-
- {Component => (
-
- )}
-
- );
- }
- } else if (status.get('spoiler_text').length === 0 && status.get('card')) {
- media = (
-
- );
- }
-
- if (otherAccounts && otherAccounts.size > 0) {
- statusAvatar = ;
- } else if (account === undefined || account === null) {
- statusAvatar = ;
- } else {
- statusAvatar = ;
- }
-
-<<<<<<< HEAD
- if (sonsIds && sonsIds.size > 0) {
- sons = {this.renderChildren(sonsIds)}
;
- }
-
- if (rebloggedByText && status.get('in_reply_to_id')) {
- quote = ancestorsText ?
-
-
- {ancestorsText}
-
- :
-
- {}}
- onMoveDown={()=>{}}
- contextType='quote'
- />
-
;
- }
-
- let deepRec;
- if(deep != null) {
- deepRec = (
-
-
-
- 【】
-
-
- );
- }
-
-=======
- const visibilityIconInfo = {
- 'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
- 'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
- 'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
- 'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) },
- };
-
- const visibilityIcon = visibilityIconInfo[status.get('visibility')];
->>>>>>> master
-
- return (
-
-
- {prepend}
-
-
-
-
-
-<<<<<<< HEAD
- {deepRec}
-
-
- {media}
-
- {quote}
-
- {showThread && status.get('in_reply_to_id') && (
-
- )}
-
- {(deep == null || tree_type != 'ance') && (
-
- )}
-=======
-
-
- {media}
-
-
->>>>>>> master
-
-
- {sons}
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/components/status_action_bar.js.orig b/app/javascript/mastodon/components/status_action_bar.js.orig
deleted file mode 100644
index 5e48e3acd4..0000000000
--- a/app/javascript/mastodon/components/status_action_bar.js.orig
+++ /dev/null
@@ -1,349 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import IconButton from './icon_button';
-import DropdownMenuContainer from '../containers/dropdown_menu_container';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me, isStaff } from '../initial_state';
-
-const messages = defineMessages({
- delete: { id: 'status.delete', defaultMessage: 'Delete' },
- redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
- direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
- mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
- mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
- block: { id: 'account.block', defaultMessage: 'Block @{name}' },
- reply: { id: 'status.reply', defaultMessage: 'Reply' },
- comment: {id: 'status.comment', defaultMessage: 'Comment' },
- share: { id: 'status.share', defaultMessage: 'Share' },
- more: { id: 'status.more', defaultMessage: 'More' },
- replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
- reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
- reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
- cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
- cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
- favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
- bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
- removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
- open: { id: 'status.open', defaultMessage: 'Expand this status' },
- report: { id: 'status.report', defaultMessage: 'Report @{name}' },
- muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
- unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
- pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
- unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
- embed: { id: 'status.embed', defaultMessage: 'Embed' },
- admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
- admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
- copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
- blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
- unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
- unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
- unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-});
-
-const obfuscatedCount = count => {
- if (count < 0) {
- return 0;
- } else if (count <= 10) {
- return count;
- } else {
- return '10+';
- }
-};
-
-const mapStateToProps = (state, { status }) => ({
- relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
-});
-
-export default @connect(mapStateToProps)
-@injectIntl
-class StatusActionBar extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- status: ImmutablePropTypes.map.isRequired,
- relationship: ImmutablePropTypes.map,
- onReply: PropTypes.func,
- onFavourite: PropTypes.func,
- onReblog: PropTypes.func,
- onDelete: PropTypes.func,
- onDirect: PropTypes.func,
- onMention: PropTypes.func,
- onMute: PropTypes.func,
- onUnmute: PropTypes.func,
- onBlock: PropTypes.func,
- onUnblock: PropTypes.func,
- onBlockDomain: PropTypes.func,
- onUnblockDomain: PropTypes.func,
- onReport: PropTypes.func,
- onEmbed: PropTypes.func,
- onMuteConversation: PropTypes.func,
- onPin: PropTypes.func,
- onBookmark: PropTypes.func,
- withDismiss: PropTypes.bool,
- intl: PropTypes.object.isRequired,
- };
-
- // Avoid checking props that are functions (and whose equality will always
- // evaluate to false. See react-immutable-pure-component for usage.
- updateOnProps = [
- 'status',
- 'relationship',
- 'withDismiss',
- ]
-
- handleReplyClick = () => {
- if (me) {
- this.props.onReply(this.props.status, this.context.router.history);
- } else {
- this._openInteractionDialog('reply');
- }
- }
-
- handleShareClick = () => {
- navigator.share({
- text: this.props.status.get('search_index'),
- url: this.props.status.get('url'),
- }).catch((e) => {
- if (e.name !== 'AbortError') console.error(e);
- });
- }
-
- handleFavouriteClick = () => {
- if (me) {
- this.props.onFavourite(this.props.status);
- } else {
- this._openInteractionDialog('favourite');
- }
- }
-
- handleReblogClick = e => {
- if (me) {
- this.props.onReblog(this.props.status, e);
- } else {
- this._openInteractionDialog('reblog');
- }
- }
-
- _openInteractionDialog = type => {
- window.open(`/interact/${this.props.status.get('id')}?type=${type}`, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
- }
-
- handleBookmarkClick = () => {
- this.props.onBookmark(this.props.status);
- }
-
- handleDeleteClick = () => {
- this.props.onDelete(this.props.status, this.context.router.history);
- }
-
- handleRedraftClick = () => {
- this.props.onDelete(this.props.status, this.context.router.history, true);
- }
-
- handlePinClick = () => {
- this.props.onPin(this.props.status);
- }
-
- handleMentionClick = () => {
- this.props.onMention(this.props.status.get('account'), this.context.router.history);
- }
-
- handleDirectClick = () => {
- this.props.onDirect(this.props.status.get('account'), this.context.router.history);
- }
-
- handleMuteClick = () => {
- const { status, relationship, onMute, onUnmute } = this.props;
- const account = status.get('account');
-
- if (relationship && relationship.get('muting')) {
- onUnmute(account);
- } else {
- onMute(account);
- }
- }
-
- handleBlockClick = () => {
- const { status, relationship, onBlock, onUnblock } = this.props;
- const account = status.get('account');
-
- if (relationship && relationship.get('blocking')) {
- onUnblock(account);
- } else {
- onBlock(status);
- }
- }
-
- handleBlockDomain = () => {
- const { status, onBlockDomain } = this.props;
- const account = status.get('account');
-
- onBlockDomain(account.get('acct').split('@')[1]);
- }
-
- handleUnblockDomain = () => {
- const { status, onUnblockDomain } = this.props;
- const account = status.get('account');
-
- onUnblockDomain(account.get('acct').split('@')[1]);
- }
-
- handleOpen = () => {
- this.context.router.history.push(`/statuses/${this.props.status.get('id')}`);
- }
-
- handleEmbed = () => {
- this.props.onEmbed(this.props.status);
- }
-
- handleReport = () => {
- this.props.onReport(this.props.status);
- }
-
- handleConversationMuteClick = () => {
- this.props.onMuteConversation(this.props.status);
- }
-
- handleCopy = () => {
- const url = this.props.status.get('url');
- const textarea = document.createElement('textarea');
-
- textarea.textContent = url;
- textarea.style.position = 'fixed';
-
- document.body.appendChild(textarea);
-
- try {
- textarea.select();
- document.execCommand('copy');
- } catch (e) {
-
- } finally {
- document.body.removeChild(textarea);
- }
- }
-
- render () {
- const { status, relationship, intl, withDismiss } = this.props;
-
- const mutingConversation = status.get('muted');
- const anonymousAccess = !me;
- const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
- const account = status.get('account');
-
- let menu = [];
-
- menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
-
- if (publicStatus) {
- menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
- menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
- }
-
- menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
- menu.push(null);
-
- if (status.getIn(['account', 'id']) === me || withDismiss) {
- menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
- menu.push(null);
- }
-
- if (status.getIn(['account', 'id']) === me) {
- if (publicStatus) {
- menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
- }
-
- menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
- menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick });
- menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick });
- menu.push(null);
-
- if (relationship && relationship.get('muting')) {
- menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
- }
-
- if (relationship && relationship.get('blocking')) {
- menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
- }
-
- menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport });
-
- if (account.get('acct') !== account.get('username')) {
- const domain = account.get('acct').split('@')[1];
-
- menu.push(null);
-
- if (relationship && relationship.get('domain_blocking')) {
- menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
- } else {
- menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
- }
- }
-
- if (isStaff) {
- menu.push(null);
- menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
- menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
- }
- }
-
- let replyIcon;
- let replyTitle;
- if (status.get('in_reply_to_id', null) === null) {
- replyIcon = 'comment';
- replyTitle = intl.formatMessage(messages.comment);
- } else {
- replyIcon = 'reply';
- replyTitle = intl.formatMessage(messages.reply);
- }
-
- const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
-
- let reblogTitle = '';
- if (status.get('reblogged')) {
- reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
- } else if (publicStatus) {
- reblogTitle = intl.formatMessage(messages.reblog);
- } else if (reblogPrivate) {
- reblogTitle = intl.formatMessage(messages.reblog_private);
- } else {
- reblogTitle = intl.formatMessage(messages.cannot_reblog);
- }
-
- const shareButton = ('share' in navigator) && publicStatus && (
-
- );
-
- return (
-
-<<<<<<< HEAD
-
{obfuscatedCount(status.get('replies_count'))}
-
{publicStatus && {obfuscatedCount(status.get('reblogs_count'))}}
-
{obfuscatedCount(status.get('favourites_count'))}
-=======
-
{obfuscatedCount(status.get('replies_count'))}
-
-
->>>>>>> master
- {shareButton}
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/containers/status_container.js.orig b/app/javascript/mastodon/containers/status_container.js.orig
deleted file mode 100644
index 2cf21f6606..0000000000
--- a/app/javascript/mastodon/containers/status_container.js.orig
+++ /dev/null
@@ -1,286 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import Immutable from 'immutable';
-import { createSelector } from 'reselect';
-import Status from '../components/status';
-import { makeGetStatus } from '../selectors';
-import {
- replyCompose,
- mentionCompose,
- directCompose,
-} from '../actions/compose';
-import {
- reblog,
- favourite,
- bookmark,
- unreblog,
- unfavourite,
- unbookmark,
- pin,
- unpin,
-} from '../actions/interactions';
-import {
- muteStatus,
- unmuteStatus,
- deleteStatus,
- hideStatus,
- revealStatus,
-<<<<<<< HEAD
- fetchContext,
-=======
- toggleStatusCollapse,
->>>>>>> master
-} from '../actions/statuses';
-import {
- unmuteAccount,
- unblockAccount,
-} from '../actions/accounts';
-import {
- blockDomain,
- unblockDomain,
-} from '../actions/domain_blocks';
-import { initMuteModal } from '../actions/mutes';
-import { initBlockModal } from '../actions/blocks';
-import { initReport } from '../actions/reports';
-import { openModal } from '../actions/modal';
-<<<<<<< HEAD
-import { defineMessages, injectIntl } from 'react-intl';
-import { boostModal, deleteModal, treeRoot } from '../initial_state';
-=======
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { boostModal, deleteModal } from '../initial_state';
->>>>>>> master
-import { showAlertForError } from '../actions/alerts';
-
-const messages = defineMessages({
- deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
- deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
- redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
- redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
- replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
- replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
- blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
-});
-
-const makeMapStateToProps = () => {
- const getStatus = makeGetStatus();
-
- const getAncestorsIds = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'inReplyTos']),
- ], (statusId, inReplyTos) => {
- let ancestorsIds = Immutable.List();
- ancestorsIds = ancestorsIds.withMutations(mutable => {
- let id = statusId;
-
- while (id) {
- mutable.unshift(id);
- id = inReplyTos.get(id);
- }
- });
-
- return ancestorsIds;
- });
-
- const getAncestorsText = createSelector([
- (_, {ids}) => ids,
- state => state.get('statuses'),
- ], (ids, statuses) => ids.map(i => {
- let text = statuses.get(i) ? statuses.get(i).get('search_index') : i;
- if(text.length > 16)
- text = text.slice(0,13) + "...";
- return text;
- }).join(' >> ')
- );
-
- const getSonsIds = createSelector([
- (_, {id}) => id,
- state => state.getIn(['contexts', 'replies']),
- ], (statusId, contextReplies) => {
- const sons = contextReplies.get(statusId);
- return sons ? sons.map(id => ({
- 'id': id,
- 'sonsIds' : contextReplies.get(id),
- }))
- : null;
- });
-
- const mapStateToProps = (state, props) => {
- const status = getStatus(state, props);
- let ancestorsIds = Immutable.List();
- let ancestorsText;
- let sonsIds;
-
- if (props.showThread && status) {
- sonsIds = getSonsIds(state, { id : status.getIn(['reblog', 'id'], props.id)});
- if(status.get('reblog')) {
- ancestorsIds = getAncestorsIds(state, { id: status.getIn(['reblog', 'in_reply_to_id']) });
- if(ancestorsIds && ancestorsIds.first() == treeRoot.split('/').pop()) {
- ancestorsText = getAncestorsText(state, { ids: ancestorsIds.shift() });
- }
- }
- }
- return {
- status,
- ancestorsText,
- sonsIds,
- };
- };
-
- return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
- onReply (status, router) {
- dispatch((_, getState) => {
- let state = getState();
-
- if (state.getIn(['compose', 'text']).trim().length !== 0) {
- dispatch(openModal('CONFIRM', {
- message: intl.formatMessage(messages.replyMessage),
- confirm: intl.formatMessage(messages.replyConfirm),
- onConfirm: () => dispatch(replyCompose(status, router)),
- }));
- } else {
- dispatch(replyCompose(status, router));
- }
- });
- },
-
- onModalReblog (status) {
- if (status.get('reblogged')) {
- dispatch(unreblog(status));
- } else {
- dispatch(reblog(status));
- }
- },
-
- onReblog (status, e) {
- if ((e && e.shiftKey) || !boostModal) {
- this.onModalReblog(status);
- } else {
- dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
- }
- },
-
- onFavourite (status) {
- if (status.get('favourited')) {
- dispatch(unfavourite(status));
- } else {
- dispatch(favourite(status));
- }
- },
-
- onBookmark (status) {
- if (status.get('bookmarked')) {
- dispatch(unbookmark(status));
- } else {
- dispatch(bookmark(status));
- }
- },
-
- onPin (status) {
- if (status.get('pinned')) {
- dispatch(unpin(status));
- } else {
- dispatch(pin(status));
- }
- },
-
- onEmbed (status) {
- dispatch(openModal('EMBED', {
- url: status.get('url'),
- onError: error => dispatch(showAlertForError(error)),
- }));
- },
-
- onDelete (status, history, withRedraft = false) {
- if (!deleteModal) {
- dispatch(deleteStatus(status.get('id'), history, withRedraft));
- } else {
- dispatch(openModal('CONFIRM', {
- message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
- confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
- onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
- }));
- }
- },
-
- onDirect (account, router) {
- dispatch(directCompose(account, router));
- },
-
- onMention (account, router) {
- dispatch(mentionCompose(account, router));
- },
-
- onOpenMedia (media, index) {
- dispatch(openModal('MEDIA', { media, index }));
- },
-
- onOpenVideo (media, options) {
- dispatch(openModal('VIDEO', { media, options }));
- },
-
- onBlock (status) {
- const account = status.get('account');
- dispatch(initBlockModal(account));
- },
-
- onUnblock (account) {
- dispatch(unblockAccount(account.get('id')));
- },
-
- onReport (status) {
- dispatch(initReport(status.get('account'), status));
- },
-
- onMute (account) {
- dispatch(initMuteModal(account));
- },
-
- onUnmute (account) {
- dispatch(unmuteAccount(account.get('id')));
- },
-
- onMuteConversation (status) {
- if (status.get('muted')) {
- dispatch(unmuteStatus(status.get('id')));
- } else {
- dispatch(muteStatus(status.get('id')));
- }
- },
-
- onToggleHidden (status) {
- if (status.get('hidden')) {
- dispatch(revealStatus(status.get('id')));
- } else {
- dispatch(hideStatus(status.get('id')));
- }
- },
-
-<<<<<<< HEAD
- onPreview (id) {
- dispatch(fetchContext(id));
-=======
- onToggleCollapsed (status, isCollapsed) {
- dispatch(toggleStatusCollapse(status.get('id'), isCollapsed));
- },
-
- onBlockDomain (domain) {
- dispatch(openModal('CONFIRM', {
- message: {domain} }} />,
- confirm: intl.formatMessage(messages.blockDomainConfirm),
- onConfirm: () => dispatch(blockDomain(domain)),
- }));
- },
-
- onUnblockDomain (domain) {
- dispatch(unblockDomain(domain));
->>>>>>> master
- },
-
-});
-
-export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
diff --git a/app/javascript/mastodon/features/getting_started/index.js.orig b/app/javascript/mastodon/features/getting_started/index.js.orig
deleted file mode 100644
index 7ff06c0c74..0000000000
--- a/app/javascript/mastodon/features/getting_started/index.js.orig
+++ /dev/null
@@ -1,183 +0,0 @@
-import React from 'react';
-import Column from '../ui/components/column';
-import ColumnLink from '../ui/components/column_link';
-import ColumnSubheading from '../ui/components/column_subheading';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me, profile_directory, showTrends } from '../../initial_state';
-import { fetchFollowRequests } from 'mastodon/actions/accounts';
-import { List as ImmutableList } from 'immutable';
-import NavigationBar from '../compose/components/navigation_bar';
-import Icon from 'mastodon/components/icon';
-import LinkFooter from 'mastodon/features/ui/components/link_footer';
-import TrendsContainer from './containers/trends_container';
-
-const messages = defineMessages({
- home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
- notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
- public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
- settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
- community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
- direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
- bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
- preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
- follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
- favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
- blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
- domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
- mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
- pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
- lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
- discover: { id: 'navigation_bar.discover', defaultMessage: 'Discover' },
- personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' },
- security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
- menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
- profile_directory: { id: 'getting_started.directory', defaultMessage: 'Profile directory' },
-});
-
-const mapStateToProps = state => ({
- myAccount: state.getIn(['accounts', me]),
- unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
-});
-
-const mapDispatchToProps = dispatch => ({
- fetchFollowRequests: () => dispatch(fetchFollowRequests()),
-});
-
-const badgeDisplay = (number, limit) => {
- if (number === 0) {
- return undefined;
- } else if (limit && number >= limit) {
- return `${limit}+`;
- } else {
- return number;
- }
-};
-
-const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class GettingStarted extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object.isRequired,
- };
-
- static propTypes = {
- intl: PropTypes.object.isRequired,
- myAccount: ImmutablePropTypes.map.isRequired,
- columns: ImmutablePropTypes.list,
- multiColumn: PropTypes.bool,
- fetchFollowRequests: PropTypes.func.isRequired,
- unreadFollowRequests: PropTypes.number,
- unreadNotifications: PropTypes.number,
- };
-
- componentDidMount () {
- const { fetchFollowRequests, multiColumn } = this.props;
-
- if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) {
- this.context.router.history.replace('/timelines/home');
- return;
- }
-
- fetchFollowRequests();
- }
-
- render () {
- const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
-
- const navItems = [];
- let i = 1;
- let height = (multiColumn) ? 0 : 60;
-
- if (multiColumn) {
- navItems.push(
- ,
- ,
- ,
- );
-
- height += 34 + 48*2;
-
- if (profile_directory) {
- navItems.push(
- ,
- );
-
- height += 48;
- }
-
- navItems.push(
- ,
- );
-
- height += 34;
- } else if (profile_directory) {
- navItems.push(
- ,
- );
-
- height += 48;
- }
-
- navItems.push(
- ,
-<<<<<<< HEAD
- ,
-
-=======
- ,
- ,
- ,
->>>>>>> master
- );
-
- height += 48*4;
-
- if (myAccount.get('locked') || unreadFollowRequests > 0) {
- navItems.push();
- height += 48;
- }
-
- if (!multiColumn) {
- navItems.push(
- ,
- ,
- );
-
- height += 34 + 48;
- }
-
- return (
-
- {multiColumn &&
-
-
-
- }
-
-
-
- {!multiColumn && }
- {navItems}
-
-
- {!multiColumn &&
}
-
-
-
-
- {multiColumn && showTrends && }
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js.orig b/app/javascript/mastodon/features/status/components/action_bar.js.orig
deleted file mode 100644
index 66c01b44eb..0000000000
--- a/app/javascript/mastodon/features/status/components/action_bar.js.orig
+++ /dev/null
@@ -1,294 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import IconButton from '../../../components/icon_button';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
-import { defineMessages, injectIntl } from 'react-intl';
-import { me, isStaff } from '../../../initial_state';
-
-const messages = defineMessages({
- delete: { id: 'status.delete', defaultMessage: 'Delete' },
- redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
- direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
- mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
- reply: { id: 'status.reply', defaultMessage: 'Reply' },
- reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
- reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
- cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
- cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
- favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
- bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
- more: { id: 'status.more', defaultMessage: 'More' },
- mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
- muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
- unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
- block: { id: 'status.block', defaultMessage: 'Block @{name}' },
- report: { id: 'status.report', defaultMessage: 'Report @{name}' },
- share: { id: 'status.share', defaultMessage: 'Share' },
- pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
- unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
- embed: { id: 'status.embed', defaultMessage: 'Embed' },
- admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
- admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
- copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
- blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
- unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
- unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
- unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-});
-
-const mapStateToProps = (state, { status }) => ({
- relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
-});
-
-export default @connect(mapStateToProps)
-@injectIntl
-class ActionBar extends React.PureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- status: ImmutablePropTypes.map.isRequired,
- relationship: ImmutablePropTypes.map,
- onReply: PropTypes.func.isRequired,
- onReblog: PropTypes.func.isRequired,
- onFavourite: PropTypes.func.isRequired,
- onBookmark: PropTypes.func.isRequired,
- onDelete: PropTypes.func.isRequired,
- onDirect: PropTypes.func.isRequired,
- onMention: PropTypes.func.isRequired,
- onMute: PropTypes.func,
- onUnmute: PropTypes.func,
- onBlock: PropTypes.func,
- onUnblock: PropTypes.func,
- onBlockDomain: PropTypes.func,
- onUnblockDomain: PropTypes.func,
- onMuteConversation: PropTypes.func,
- onReport: PropTypes.func,
- onPin: PropTypes.func,
- onEmbed: PropTypes.func,
- intl: PropTypes.object.isRequired,
- };
-
- handleReplyClick = () => {
- this.props.onReply(this.props.status);
- }
-
- handleReblogClick = (e) => {
- this.props.onReblog(this.props.status, e);
- }
-
- handleFavouriteClick = () => {
- this.props.onFavourite(this.props.status);
- }
-
- handleBookmarkClick = (e) => {
- this.props.onBookmark(this.props.status, e);
- }
-
- handleDeleteClick = () => {
- this.props.onDelete(this.props.status, this.context.router.history);
- }
-
- handleRedraftClick = () => {
- this.props.onDelete(this.props.status, this.context.router.history, true);
- }
-
- handleDirectClick = () => {
- this.props.onDirect(this.props.status.get('account'), this.context.router.history);
- }
-
- handleMentionClick = () => {
- this.props.onMention(this.props.status.get('account'), this.context.router.history);
- }
-
- handleMuteClick = () => {
- const { status, relationship, onMute, onUnmute } = this.props;
- const account = status.get('account');
-
- if (relationship && relationship.get('muting')) {
- onUnmute(account);
- } else {
- onMute(account);
- }
- }
-
- handleBlockClick = () => {
- const { status, relationship, onBlock, onUnblock } = this.props;
- const account = status.get('account');
-
- if (relationship && relationship.get('blocking')) {
- onUnblock(account);
- } else {
- onBlock(status);
- }
- }
-
- handleBlockDomain = () => {
- const { status, onBlockDomain } = this.props;
- const account = status.get('account');
-
- onBlockDomain(account.get('acct').split('@')[1]);
- }
-
- handleUnblockDomain = () => {
- const { status, onUnblockDomain } = this.props;
- const account = status.get('account');
-
- onUnblockDomain(account.get('acct').split('@')[1]);
- }
-
- handleConversationMuteClick = () => {
- this.props.onMuteConversation(this.props.status);
- }
-
- handleReport = () => {
- this.props.onReport(this.props.status);
- }
-
- handlePinClick = () => {
- this.props.onPin(this.props.status);
- }
-
- handleShare = () => {
- navigator.share({
- text: this.props.status.get('search_index'),
- url: this.props.status.get('url'),
- });
- }
-
- handleEmbed = () => {
- this.props.onEmbed(this.props.status);
- }
-
- handleCopy = () => {
- const url = this.props.status.get('url');
- const textarea = document.createElement('textarea');
-
- textarea.textContent = url;
- textarea.style.position = 'fixed';
-
- document.body.appendChild(textarea);
-
- try {
- textarea.select();
- document.execCommand('copy');
- } catch (e) {
-
- } finally {
- document.body.removeChild(textarea);
- }
- }
-
- render () {
- const { status, relationship, intl } = this.props;
-
- const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
- const mutingConversation = status.get('muted');
- const account = status.get('account');
-
- let menu = [];
-
- if (publicStatus) {
- menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
- menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
- menu.push(null);
- }
-
- if (me === status.getIn(['account', 'id'])) {
- if (publicStatus) {
- menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
- }
-
- menu.push(null);
- menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
- menu.push(null);
- menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
- menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
- menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
- menu.push(null);
-
- if (relationship && relationship.get('muting')) {
- menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
- }
-
- if (relationship && relationship.get('blocking')) {
- menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
- } else {
- menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
- }
-
- menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-
- if (account.get('acct') !== account.get('username')) {
- const domain = account.get('acct').split('@')[1];
-
- menu.push(null);
-
- if (relationship && relationship.get('domain_blocking')) {
- menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
- } else {
- menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
- }
- }
-
- if (isStaff) {
- menu.push(null);
- menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
- menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
- }
- }
-
- const shareButton = ('share' in navigator) && publicStatus && (
-
- );
-
- let replyIcon;
- if (status.get('in_reply_to_id', null) === null) {
- replyIcon = 'comment';
- } else {
- replyIcon = 'reply';
- }
-
- const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
-
- let reblogTitle;
- if (status.get('reblogged')) {
- reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
- } else if (publicStatus) {
- reblogTitle = intl.formatMessage(messages.reblog);
- } else if (reblogPrivate) {
- reblogTitle = intl.formatMessage(messages.reblog_private);
- } else {
- reblogTitle = intl.formatMessage(messages.cannot_reblog);
- }
-
- return (
-
-<<<<<<< HEAD
-
-
-
-=======
-
-
-
->>>>>>> master
- {shareButton}
-
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js.orig b/app/javascript/mastodon/features/status/components/detailed_status.js.orig
deleted file mode 100644
index b7ad9dda82..0000000000
--- a/app/javascript/mastodon/features/status/components/detailed_status.js.orig
+++ /dev/null
@@ -1,271 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Avatar from '../../../components/avatar';
-import DisplayName from '../../../components/display_name';
-import StatusContent from '../../../components/status_content';
-import MediaGallery from '../../../components/media_gallery';
-import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
-import Card from './card';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Video from '../../video';
-import Audio from '../../audio';
-import scheduleIdleTask from '../../ui/util/schedule_idle_task';
-import classNames from 'classnames';
-import Icon from 'mastodon/components/icon';
-import AnimatedNumber from 'mastodon/components/animated_number';
-
-const messages = defineMessages({
- public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
- unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
- private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
- direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-});
-
-export default @injectIntl
-class DetailedStatus extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- status: ImmutablePropTypes.map,
- onOpenMedia: PropTypes.func.isRequired,
- onOpenVideo: PropTypes.func.isRequired,
- onToggleHidden: PropTypes.func.isRequired,
- measureHeight: PropTypes.bool,
- onHeightChange: PropTypes.func,
- domain: PropTypes.string.isRequired,
- compact: PropTypes.bool,
- showMedia: PropTypes.bool,
- onToggleMediaVisibility: PropTypes.func,
- };
-
- state = {
- height: null,
- };
-
- handleAccountClick = (e) => {
- if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.context.router) {
- e.preventDefault();
- this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
- }
-
- e.stopPropagation();
- }
-
- handleOpenVideo = (media, options) => {
- this.props.onOpenVideo(media, options);
- }
-
- handleExpandedToggle = () => {
- this.props.onToggleHidden(this.props.status);
- }
-
- _measureHeight (heightJustChanged) {
- if (this.props.measureHeight && this.node) {
- scheduleIdleTask(() => this.node && this.setState({ height: Math.ceil(this.node.scrollHeight) + 1 }));
-
- if (this.props.onHeightChange && heightJustChanged) {
- this.props.onHeightChange();
- }
- }
- }
-
- setRef = c => {
- this.node = c;
- this._measureHeight();
- }
-
- componentDidUpdate (prevProps, prevState) {
- this._measureHeight(prevState.height !== this.state.height);
- }
-
- handleModalLink = e => {
- e.preventDefault();
-
- let href;
-
- if (e.target.nodeName !== 'A') {
- href = e.target.parentNode.href;
- } else {
- href = e.target.href;
- }
-
- window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
- }
-
- render () {
- const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
- const outerStyle = { boxSizing: 'border-box' };
-<<<<<<< HEAD
- const { compact, deep } = this.props;
-=======
- const { intl, compact } = this.props;
->>>>>>> master
-
- if (!status) {
- return null;
- }
-
- let media = '';
- let applicationLink = '';
- let reblogLink = '';
- let reblogIcon = 'retweet';
- let favouriteLink = '';
-
- if (this.props.measureHeight) {
- outerStyle.height = `${this.state.height}px`;
- }
-
- if (status.get('media_attachments').size > 0) {
- if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
- const attachment = status.getIn(['media_attachments', 0]);
-
- media = (
-
- );
- } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
- const attachment = status.getIn(['media_attachments', 0]);
-
- media = (
-
- );
- } else {
- media = (
-
- );
- }
- } else if (status.get('spoiler_text').length === 0) {
- media = ;
- }
-
- if (status.get('application')) {
- applicationLink = · {status.getIn(['application', 'name'])};
- }
-
- const visibilityIconInfo = {
- 'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
- 'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
- 'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
- 'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) },
- };
-
- const visibilityIcon = visibilityIconInfo[status.get('visibility')];
- const visibilityLink = · ;
-
- if (['private', 'direct'].includes(status.get('visibility'))) {
- reblogLink = '';
- } else if (this.context.router) {
- reblogLink = (
-
- ·
-
-
-
-
-
-
-
- );
- } else {
- reblogLink = (
-
- ·
-
-
-
-
-
-
-
- );
- }
-
- if (this.context.router) {
- favouriteLink = (
-
-
-
-
-
-
- );
- } else {
- favouriteLink = (
-
-
-
-
-
-
- );
- }
-
- let deepRec;
- if(deep != null) {
- deepRec = (
-
-
-
- 【】
-
-
- );
- }
-
- return (
-
-
-
-
-
-
-
- {deepRec}
-
-
- {media}
-
-
-
-
- {visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
-
-
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/status/index.js.orig b/app/javascript/mastodon/features/status/index.js.orig
deleted file mode 100644
index 0287a79432..0000000000
--- a/app/javascript/mastodon/features/status/index.js.orig
+++ /dev/null
@@ -1,673 +0,0 @@
-import Immutable from 'immutable';
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { createSelector } from 'reselect';
-import { fetchStatus } from '../../actions/statuses';
-import MissingIndicator from '../../components/missing_indicator';
-import DetailedStatus from './components/detailed_status';
-import ActionBar from './components/action_bar';
-import Column from '../ui/components/column';
-import Tree from 'react-tree-graph';
-import {
- favourite,
- unfavourite,
- bookmark,
- unbookmark,
- reblog,
- unreblog,
- pin,
- unpin,
-} from '../../actions/interactions';
-import {
- replyCompose,
- mentionCompose,
- directCompose,
-} from '../../actions/compose';
-import {
- muteStatus,
- unmuteStatus,
- deleteStatus,
- hideStatus,
- revealStatus,
-} from '../../actions/statuses';
-import {
- unblockAccount,
- unmuteAccount,
-} from '../../actions/accounts';
-import {
- blockDomain,
- unblockDomain,
-} from '../../actions/domain_blocks';
-import { initMuteModal } from '../../actions/mutes';
-import { initBlockModal } from '../../actions/blocks';
-import { initReport } from '../../actions/reports';
-import { makeGetStatus } from '../../selectors';
-import { ScrollContainer } from 'react-router-scroll-4';
-import ColumnBackButton from '../../components/column_back_button';
-import ColumnHeader from '../../components/column_header';
-import StatusContainer from '../../containers/status_container';
-import { openModal } from '../../actions/modal';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { HotKeys } from 'react-hotkeys';
-import { boostModal, deleteModal } from '../../initial_state';
-import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
-import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
-import Icon from 'mastodon/components/icon';
-
-const messages = defineMessages({
- deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
- deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
- redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
- redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
- revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
- hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
- detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
- replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
- replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
- blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
-});
-
-const makeMapStateToProps = () => {
- const getStatus = makeGetStatus();
-
- const getAncestorsIds = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'inReplyTos']),
- ], (statusId, inReplyTos) => {
- let ancestorsIds = Immutable.List();
- ancestorsIds = ancestorsIds.withMutations(mutable => {
- let id = statusId;
-
- while (id) {
- mutable.unshift(id);
- id = inReplyTos.get(id);
- }
- });
-
- return ancestorsIds;
- });
-
- const getDescendantsIds = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'replies']),
- state => state.get('statuses'),
- ], (statusId, contextReplies, statuses) => {
- let descendantsIds = [];
- const ids = [statusId];
-
- while (ids.length > 0) {
- let id = ids.shift();
- const replies = contextReplies.get(id);
-
- if (statusId !== id) {
- descendantsIds.push(id);
- }
-
- if (replies) {
- replies.reverse().forEach(reply => {
- ids.unshift(reply);
- });
- }
- }
-
- let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account'));
- if (insertAt !== -1) {
- descendantsIds.forEach((id, idx) => {
- if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === statuses.get(id).get('account')) {
- descendantsIds.splice(idx, 1);
- descendantsIds.splice(insertAt, 0, id);
- insertAt += 1;
- }
- });
- }
-
- return Immutable.List(descendantsIds);
- });
-
- const getTreeData = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'replies']),
- state => state.get('statuses'),
- ], (statusId, contextReplies, statuses) => {
-
- const getMore = (id, notRoot) => {
- const replies = contextReplies.get(id);
- const cur_status = statuses.get(id);
- const text = cur_status.get('search_index').replace(/@\S+?\s/,'@..');
- return {
- statusId: id,
- name: (text.length > 16 ? text.slice(0,13) + "..." : text) + (cur_status.get('media_attachments').size > 0 ? " [图片]" : ""),
- children: replies ? Array.from(replies.map( i => getMore(i, true) )) : [],
- }
- }
-
- let treeData = getMore(statusId, false)
-
- return treeData;
- });
-
- const mapStateToProps = (state, props) => {
- const status = getStatus(state, { id: props.params.statusId });
- let ancestorsIds = Immutable.List();
- let descendantsIds = Immutable.List();
- let rootAcct;
- let deep;
- let treeData;
-
- if (status) {
- ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
- const root_status = ancestorsIds.size? getStatus(state, {id: ancestorsIds.get(0)}) : status; //error is directly visit url of non-root detailedStatus, feature!
- rootAcct = root_status? root_status.getIn(['account', 'acct']) : -1;
- if(rootAcct == '0') {
- descendantsIds = state.getIn(['contexts', 'replies', status.get('id')]);
- if(descendantsIds)
- descendantsIds = descendantsIds.reverse();
- }
- else {
- descendantsIds = getDescendantsIds(state, { id: status.get('id') });
- }
- deep = rootAcct == '0' ? ancestorsIds.size : null;
- treeData = rootAcct == '0' ? getTreeData(state, {id: status.get('id')}) : null;
- }
-
- return {
- status,
- deep,
- ancestorsIds,
- descendantsIds,
- treeData,
- askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
- domain: state.getIn(['meta', 'domain']),
- };
- };
-
- return mapStateToProps;
-};
-
-export default @injectIntl
-@connect(makeMapStateToProps)
-class Status extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- params: PropTypes.object.isRequired,
- dispatch: PropTypes.func.isRequired,
- status: ImmutablePropTypes.map,
- ancestorsIds: ImmutablePropTypes.list,
- descendantsIds: ImmutablePropTypes.list,
- intl: PropTypes.object.isRequired,
- askReplyConfirmation: PropTypes.bool,
- multiColumn: PropTypes.bool,
- domain: PropTypes.string.isRequired,
- };
-
- state = {
- fullscreen: false,
- showMedia: defaultMediaVisibility(this.props.status),
- loadedStatusId: undefined,
- showTree: false,
- svgWidth: 400,
- activeNode: null
- };
-
- componentWillMount () {
- this.props.dispatch(fetchStatus(this.props.params.statusId));
- }
-
- componentDidMount () {
- attachFullscreenListener(this.onFullScreenChange);
- }
-
- componentWillReceiveProps (nextProps) {
- if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
- this._scrolledIntoView = false;
- this.props.dispatch(fetchStatus(nextProps.params.statusId));
- }
-
- if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
- this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
- }
- }
-
- handleToggleMediaVisibility = () => {
- this.setState({ showMedia: !this.state.showMedia });
- }
-
- handleFavouriteClick = (status) => {
- if (status.get('favourited')) {
- this.props.dispatch(unfavourite(status));
- } else {
- this.props.dispatch(favourite(status));
- }
- }
-
- handlePin = (status) => {
- if (status.get('pinned')) {
- this.props.dispatch(unpin(status));
- } else {
- this.props.dispatch(pin(status));
- }
- }
-
- handleReplyClick = (status) => {
- let { askReplyConfirmation, dispatch, intl } = this.props;
- if (askReplyConfirmation) {
- dispatch(openModal('CONFIRM', {
- message: intl.formatMessage(messages.replyMessage),
- confirm: intl.formatMessage(messages.replyConfirm),
- onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
- }));
- } else {
- dispatch(replyCompose(status, this.context.router.history));
- }
- }
-
- handleModalReblog = (status) => {
- this.props.dispatch(reblog(status));
- }
-
- handleReblogClick = (status, e) => {
- if (status.get('reblogged')) {
- this.props.dispatch(unreblog(status));
- } else {
- if ((e && e.shiftKey) || !boostModal) {
- this.handleModalReblog(status);
- } else {
- this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
- }
- }
- }
-
- handleBookmarkClick = (status) => {
- if (status.get('bookmarked')) {
- this.props.dispatch(unbookmark(status));
- } else {
- this.props.dispatch(bookmark(status));
- }
- }
-
- handleDeleteClick = (status, history, withRedraft = false) => {
- const { dispatch, intl } = this.props;
-
- if (!deleteModal) {
- dispatch(deleteStatus(status.get('id'), history, withRedraft));
- } else {
- dispatch(openModal('CONFIRM', {
- message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
- confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
- onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
- }));
- }
- }
-
- handleDirectClick = (account, router) => {
- this.props.dispatch(directCompose(account, router));
- }
-
- handleMentionClick = (account, router) => {
- this.props.dispatch(mentionCompose(account, router));
- }
-
- handleOpenMedia = (media, index) => {
- this.props.dispatch(openModal('MEDIA', { media, index }));
- }
-
- handleOpenVideo = (media, options) => {
- this.props.dispatch(openModal('VIDEO', { media, options }));
- }
-
- handleHotkeyOpenMedia = e => {
- const status = this._properStatus();
-
- e.preventDefault();
-
- if (status.get('media_attachments').size > 0) {
- if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
- // TODO: toggle play/paused?
- } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
- this.handleOpenVideo(status.getIn(['media_attachments', 0]), { startTime: 0 });
- } else {
- this.handleOpenMedia(status.get('media_attachments'), 0);
- }
- }
- }
-
- handleMuteClick = (account) => {
- this.props.dispatch(initMuteModal(account));
- }
-
- handleConversationMuteClick = (status) => {
- if (status.get('muted')) {
- this.props.dispatch(unmuteStatus(status.get('id')));
- } else {
- this.props.dispatch(muteStatus(status.get('id')));
- }
- }
-
- handleToggleHidden = (status) => {
- if (status.get('hidden')) {
- this.props.dispatch(revealStatus(status.get('id')));
- } else {
- this.props.dispatch(hideStatus(status.get('id')));
- }
- }
-
- handleToggleAll = () => {
- const { status, ancestorsIds, descendantsIds } = this.props;
- const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS());
-
- if (status.get('hidden')) {
- this.props.dispatch(revealStatus(statusIds));
- } else {
- this.props.dispatch(hideStatus(statusIds));
- }
- }
-
- handleShowTree = () => {
- this.setState({
- activeNode: null,
- showTree: !this.state.showTree
- });
- }
-
- handleBlockClick = (status) => {
- const { dispatch } = this.props;
- const account = status.get('account');
- dispatch(initBlockModal(account));
- }
-
- handleReport = (status) => {
- this.props.dispatch(initReport(status.get('account'), status));
- }
-
- handleEmbed = (status) => {
- this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
- }
-
- handleUnmuteClick = account => {
- this.props.dispatch(unmuteAccount(account.get('id')));
- }
-
- handleUnblockClick = account => {
- this.props.dispatch(unblockAccount(account.get('id')));
- }
-
- handleBlockDomainClick = domain => {
- this.props.dispatch(openModal('CONFIRM', {
- message: {domain} }} />,
- confirm: this.props.intl.formatMessage(messages.blockDomainConfirm),
- onConfirm: () => this.props.dispatch(blockDomain(domain)),
- }));
- }
-
- handleUnblockDomainClick = domain => {
- this.props.dispatch(unblockDomain(domain));
- }
-
-
- handleHotkeyMoveUp = () => {
- this.handleMoveUp(this.props.status.get('id'));
- }
-
- handleHotkeyMoveDown = () => {
- this.handleMoveDown(this.props.status.get('id'));
- }
-
- handleHotkeyReply = e => {
- e.preventDefault();
- this.handleReplyClick(this.props.status);
- }
-
- handleHotkeyFavourite = () => {
- this.handleFavouriteClick(this.props.status);
- }
-
- handleHotkeyBoost = () => {
- this.handleReblogClick(this.props.status);
- }
-
- handleHotkeyMention = e => {
- e.preventDefault();
- this.handleMentionClick(this.props.status.get('account'));
- }
-
- handleHotkeyOpenProfile = () => {
- this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
- }
-
- handleHotkeyToggleHidden = () => {
- this.handleToggleHidden(this.props.status);
- }
-
- handleHotkeyToggleSensitive = () => {
- this.handleToggleMediaVisibility();
- }
-
- handleMoveUp = id => {
- const { status, ancestorsIds, descendantsIds } = this.props;
-
- if (id === status.get('id')) {
- this._selectChild(ancestorsIds.size - 1, true);
- } else {
- let index = ancestorsIds.indexOf(id);
-
- if (index === -1) {
- index = descendantsIds.indexOf(id);
- this._selectChild(ancestorsIds.size + index, true);
- } else {
- this._selectChild(index - 1, true);
- }
- }
- }
-
- handleMoveDown = id => {
- const { status, ancestorsIds, descendantsIds } = this.props;
-
- if (id === status.get('id')) {
- this._selectChild(ancestorsIds.size + 1, false);
- } else {
- let index = ancestorsIds.indexOf(id);
-
- if (index === -1) {
- index = descendantsIds.indexOf(id);
- this._selectChild(ancestorsIds.size + index + 2, false);
- } else {
- this._selectChild(index + 1, false);
- }
- }
- }
-
- _selectChild (index, align_top) {
- const container = this.node;
- const element = container.querySelectorAll('.focusable')[index];
-
- if (element) {
- if (align_top && container.scrollTop > element.offsetTop) {
- element.scrollIntoView(true);
- } else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
- element.scrollIntoView(false);
- }
- element.focus();
- }
- }
-
- renderChildren (list, type) {
- const { deep } = this.props;
- return list.map((id,idx) => (
-
- ));
- }
-
- setRef = c => {
- this.node = c;
- }
-
- componentDidUpdate () {
- if (this._scrolledIntoView) {
- return;
- }
-
- const { status, ancestorsIds } = this.props;
-
- if (status && ancestorsIds && ancestorsIds.size > 0) {
- const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
-
- window.requestAnimationFrame(() => {
- element.scrollIntoView(true);
- });
- this._scrolledIntoView = true;
- }
- }
-
- componentWillUnmount () {
- detachFullscreenListener(this.onFullScreenChange);
- }
-
- onFullScreenChange = () => {
- this.setState({ fullscreen: isFullscreen() });
- }
-
- handleNodeClick = (ev, node) => {
- if (!this.context.router) {
- return;
- }
-
- const { status } = this.props;
- this.context.router.history.push(`/statuses/${node}`);
- }
-
- render () {
- let ancestors, descendants;
- const { shouldUpdateScroll, status, deep, ancestorsIds, descendantsIds, treeData, intl, domain, multiColumn } = this.props;
- const { fullscreen, showTree, svgWidth, activeNode } = this.state;
-
- if (status === null) {
- return (
-
-
-
-
- );
- }
-
- if (ancestorsIds && ancestorsIds.size > 0) {
- ancestors = {this.renderChildren(ancestorsIds, 'ance')}
;
- }
-
- if (descendantsIds && descendantsIds.size > 0) {
- descendants = {this.renderChildren(descendantsIds, 'desc')}
;
- }
-
- const handlers = {
- moveUp: this.handleHotkeyMoveUp,
- moveDown: this.handleHotkeyMoveDown,
- reply: this.handleHotkeyReply,
- favourite: this.handleHotkeyFavourite,
- boost: this.handleHotkeyBoost,
- mention: this.handleHotkeyMention,
- openProfile: this.handleHotkeyOpenProfile,
- toggleHidden: this.handleHotkeyToggleHidden,
- toggleSensitive: this.handleHotkeyToggleSensitive,
- openMedia: this.handleHotkeyOpenMedia,
- };
-
- return (
-
-
- )}
- />
-
-
-
- {ancestors}
-
-
- {if(e) this.setState({svgWidth: e.clientWidth})}}>
-
- {showTree ?
-
- :
-
>>>>>> master
- status={status}
- deep={deep}
- onOpenVideo={this.handleOpenVideo}
- onOpenMedia={this.handleOpenMedia}
- onToggleHidden={this.handleToggleHidden}
- domain={domain}
- showMedia={this.state.showMedia}
- onToggleMediaVisibility={this.handleToggleMediaVisibility}
- />
- }
-
-
-
-
-
- {descendants}
-
-
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js.orig b/app/javascript/mastodon/features/ui/components/columns_area.js.orig
deleted file mode 100644
index 3f17d3e5cc..0000000000
--- a/app/javascript/mastodon/features/ui/components/columns_area.js.orig
+++ /dev/null
@@ -1,247 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import ReactSwipeableViews from 'react-swipeable-views';
-import TabsBar, { links, getIndex, getLink } from './tabs_bar';
-import { Link } from 'react-router-dom';
-
-import BundleContainer from '../containers/bundle_container';
-import ColumnLoading from './column_loading';
-import DrawerLoading from './drawer_loading';
-import BundleColumnError from './bundle_column_error';
-import {
- Compose,
- Notifications,
- HomeTimeline,
- CommunityTimeline,
- PublicTimeline,
- HashtagTimeline,
- DirectTimeline,
- FavouritedStatuses,
- BookmarkedStatuses,
- ListTimeline,
- Directory,
-} from '../../ui/util/async-components';
-import Icon from 'mastodon/components/icon';
-import ComposePanel from './compose_panel';
-import NavigationPanel from './navigation_panel';
-
-import detectPassiveEvents from 'detect-passive-events';
-import { scrollRight } from '../../../scroll';
-
-import TrendsContainer from '../../getting_started/containers/trends_container';
-
-const componentMap = {
- 'COMPOSE': Compose,
- 'HOME': HomeTimeline,
- 'NOTIFICATIONS': Notifications,
- 'PUBLIC': PublicTimeline,
- 'REMOTE': PublicTimeline,
- 'COMMUNITY': CommunityTimeline,
- 'HASHTAG': HashtagTimeline,
- 'DIRECT': DirectTimeline,
- 'FAVOURITES': FavouritedStatuses,
- 'BOOKMARKS': BookmarkedStatuses,
- 'LIST': ListTimeline,
- 'DIRECTORY': Directory,
-};
-
-const messages = defineMessages({
- publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
-});
-
-const shouldHideFAB = path => path.match(/^\/statuses\/|^\/search|^\/getting-started/);
-
-export default @(component => injectIntl(component, { withRef: true }))
-class ColumnsArea extends ImmutablePureComponent {
-
- static contextTypes = {
- router: PropTypes.object.isRequired,
- };
-
- static propTypes = {
- intl: PropTypes.object.isRequired,
- columns: ImmutablePropTypes.list.isRequired,
- isModalOpen: PropTypes.bool.isRequired,
- singleColumn: PropTypes.bool,
- children: PropTypes.node,
- };
-
- state = {
- shouldAnimate: false,
- }
-
- componentWillReceiveProps() {
- this.setState({ shouldAnimate: false });
- }
-
- componentDidMount() {
- if (!this.props.singleColumn) {
- this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false);
- }
-
- this.lastIndex = getIndex(this.context.router.history.location.pathname);
- this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
-
- this.setState({ shouldAnimate: true });
- }
-
- componentWillUpdate(nextProps) {
- if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
- this.node.removeEventListener('wheel', this.handleWheel);
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) {
- this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false);
- }
- this.lastIndex = getIndex(this.context.router.history.location.pathname);
- this.setState({ shouldAnimate: true });
- }
-
- componentWillUnmount () {
- if (!this.props.singleColumn) {
- this.node.removeEventListener('wheel', this.handleWheel);
- }
- }
-
- handleChildrenContentChange() {
- if (!this.props.singleColumn) {
- const modifier = this.isRtlLayout ? -1 : 1;
- this._interruptScrollAnimation = scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier);
- }
- }
-
- handleSwipe = (index) => {
- this.pendingIndex = index;
-
- const nextLinkTranslationId = links[index].props['data-preview-title-id'];
- const currentLinkSelector = '.tabs-bar__link.active';
- const nextLinkSelector = `.tabs-bar__link[data-preview-title-id="${nextLinkTranslationId}"]`;
-
- // HACK: Remove the active class from the current link and set it to the next one
- // React-router does this for us, but too late, feeling laggy.
- document.querySelector(currentLinkSelector).classList.remove('active');
- document.querySelector(nextLinkSelector).classList.add('active');
-
- if (!this.state.shouldAnimate && typeof this.pendingIndex === 'number') {
- this.context.router.history.push(getLink(this.pendingIndex));
- this.pendingIndex = null;
- }
- }
-
- handleAnimationEnd = () => {
- if (typeof this.pendingIndex === 'number') {
- this.context.router.history.push(getLink(this.pendingIndex));
- this.pendingIndex = null;
- }
- }
-
- handleWheel = () => {
- if (typeof this._interruptScrollAnimation !== 'function') {
- return;
- }
-
- this._interruptScrollAnimation();
- }
-
- setRef = (node) => {
- this.node = node;
- }
-
- renderView = (link, index) => {
- const columnIndex = getIndex(this.context.router.history.location.pathname);
- const title = this.props.intl.formatMessage({ id: link.props['data-preview-title-id'] });
- const icon = link.props['data-preview-icon'];
-
- const view = (index === columnIndex) ?
- React.cloneElement(this.props.children) :
- ;
-
- return (
-
- {
- link.props['data-preview-title-id'] == 'column.community' &&
-
- }
- {view}
-
- );
- }
-
- renderLoading = columnId => () => {
- return columnId === 'COMPOSE' ? : ;
- }
-
- renderError = (props) => {
- return ;
- }
-
- render () {
- const { columns, children, singleColumn, isModalOpen, intl } = this.props;
- const { shouldAnimate } = this.state;
-
- const columnIndex = getIndex(this.context.router.history.location.pathname);
-
- if (singleColumn) {
- const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : ;
-
- const content = columnIndex !== -1 ? (
-<<<<<<< HEAD
-
-=======
-
->>>>>>> master
- {links.map(this.renderView)}
-
- ) : (
- {children}
- );
-
- return (
-
-
-
-
-
- {content}
-
-
-
-
- {floatingActionButton}
-
- );
- }
-
- return (
-
- {columns.map(column => {
- const params = column.get('params', null) === null ? null : column.get('params').toJS();
- const other = params && params.other ? params.other : {};
-
- return (
-
- {SpecificComponent => }
-
- );
- })}
-
- {React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
-
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js.orig b/app/javascript/mastodon/features/ui/components/navigation_panel.js.orig
deleted file mode 100644
index 7ffc1836e5..0000000000
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.js.orig
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react';
-import { NavLink, withRouter } from 'react-router-dom';
-import { FormattedMessage } from 'react-intl';
-import Icon from 'mastodon/components/icon';
-import { profile_directory, showTrends, treeRoot } from 'mastodon/initial_state';
-import NotificationsCounterIcon from './notifications_counter_icon';
-import FollowRequestsNavLink from './follow_requests_nav_link';
-import ListPanel from './list_panel';
-import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
-
-const NavigationPanel = () => (
-
-
-
-
-
-
-
-
-<<<<<<< HEAD
-
-=======
-
-
->>>>>>> master
-
- {profile_directory &&
}
-
-
-
-
-
-
-
-
- {showTrends &&
}
- {showTrends &&
}
-
-);
-
-export default withRouter(NavigationPanel);
diff --git a/app/javascript/mastodon/locales/zh-HK.json.orig b/app/javascript/mastodon/locales/zh-HK.json.orig
deleted file mode 100644
index 15316b0835..0000000000
--- a/app/javascript/mastodon/locales/zh-HK.json.orig
+++ /dev/null
@@ -1,475 +0,0 @@
-{
- "account.account_note_header": "Your note for @{name}",
- "account.add_account_note": "Add note for @{name}",
- "account.add_or_remove_from_list": "從名單中新增或移除",
- "account.badges.bot": "機械人",
- "account.badges.group": "群組",
- "account.block": "封鎖 @{name}",
- "account.block_domain": "隱藏來自 {domain} 的一切文章",
- "account.blocked": "封鎖",
- "account.browse_more_on_origin_server": "Browse more on the original profile",
- "account.cancel_follow_request": "取消關注請求",
- "account.direct": "私訊 @{name}",
- "account.domain_blocked": "服務站被隱藏",
- "account.edit_profile": "修改個人資料",
- "account.endorse": "在個人資料推薦對方",
- "account.follow": "關注",
- "account.followers": "關注的人",
- "account.followers.empty": "尚沒有人關注這位使用者。",
- "account.follows": "正關注",
- "account.follows.empty": "這位使用者尚未關注任何使用者。",
- "account.follows_you": "關注你",
- "account.hide_reblogs": "隱藏 @{name} 的轉推",
- "account.last_status": "上次活躍時間",
- "account.link_verified_on": "此連結的所有權已在 {date} 檢查過",
- "account.locked_info": "這隻帳戶的隱私狀態被設成鎖定。該擁有者會手動審核能關注這隻帳號的人。",
- "account.media": "媒體",
- "account.mention": "提及 @{name}",
- "account.moved_to": "{name} 已經遷移到:",
- "account.mute": "將 @{name} 靜音",
- "account.mute_notifications": "將來自 @{name} 的通知靜音",
- "account.muted": "靜音",
- "account.never_active": "永不",
- "account.posts": "文章",
- "account.posts_with_replies": "包含回覆的文章",
- "account.report": "舉報 @{name}",
- "account.requested": "等候審批",
- "account.share": "分享 @{name} 的個人資料",
- "account.show_reblogs": "顯示 @{name} 的推文",
- "account.unblock": "解除對 @{name} 的封鎖",
- "account.unblock_domain": "不再隱藏 {domain}",
- "account.unendorse": "不再於個人資料頁面推薦對方",
- "account.unfollow": "取消關注",
- "account.unmute": "取消 @{name} 的靜音",
- "account.unmute_notifications": "取消來自 @{name} 通知的靜音",
- "account_note.cancel": "Cancel",
- "account_note.edit": "Edit",
- "account_note.placeholder": "No comment provided",
- "account_note.save": "Save",
- "alert.rate_limited.message": "請在 {retry_time, time, medium} 過後重試",
- "alert.rate_limited.title": "已限速",
- "alert.unexpected.message": "發生不可預期的錯誤。",
- "alert.unexpected.title": "噢!",
- "announcement.announcement": "公告",
- "autosuggest_hashtag.per_week": "{count} / 週",
- "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
- "bundle_column_error.body": "加載本組件出錯。",
- "bundle_column_error.retry": "重試",
- "bundle_column_error.title": "網絡錯誤",
- "bundle_modal_error.close": "關閉",
- "bundle_modal_error.message": "加載本組件出錯。",
- "bundle_modal_error.retry": "重試",
- "column.blocks": "封鎖用戶",
- "column.bookmarks": "書籤",
- "column.community": "本站時間軸",
- "column.direct": "個人訊息",
- "column.directory": "瀏覽個人資料",
- "column.domain_blocks": "隱藏的服務站",
- "column.favourites": "最愛的文章",
- "column.follow_requests": "關注請求",
- "column.home": "主頁",
- "column.lists": "列表",
- "column.mutes": "靜音名單",
- "column.notifications": "通知",
- "column.pins": "置頂文章",
- "column.public": "跨站時間軸",
- "column_back_button.label": "返回",
- "column_header.hide_settings": "隱藏設定",
- "column_header.moveLeft_settings": "將欄左移",
- "column_header.moveRight_settings": "將欄右移",
- "column_header.pin": "固定",
- "column_header.show_settings": "顯示設定",
- "column_header.unpin": "取下",
- "column_subheading.settings": "設定",
- "community.column_settings.local_only": "只有本地",
- "community.column_settings.media_only": "僅媒體",
- "community.column_settings.remote_only": "只有遠端",
- "compose_form.direct_message_warning": "這文章只有被提及的用戶才可以看到。",
- "compose_form.direct_message_warning_learn_more": "了解更多",
- "compose_form.hashtag_warning": "這文章因為不是公開,所以不會被標籤搜索。只有公開的文章才會被標籤搜索。",
- "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。",
- "compose_form.lock_disclaimer.lock": "公共",
- "compose_form.placeholder": "你在想甚麼?",
- "compose_form.poll.add_option": "新增選擇",
- "compose_form.poll.duration": "投票期限",
- "compose_form.poll.option_placeholder": "第 {number} 個選擇",
- "compose_form.poll.remove_option": "移除此選擇",
- "compose_form.poll.switch_to_multiple": "變更投票為允許多個選項",
- "compose_form.poll.switch_to_single": "變更投票為允許單一選項",
- "compose_form.publish": "發文",
- "compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive.hide": "標記媒體為敏感內容",
- "compose_form.sensitive.marked": "媒體被標示為敏感",
- "compose_form.sensitive.unmarked": "媒體沒有被標示為敏感",
- "compose_form.spoiler.marked": "文字被警告隱藏",
- "compose_form.spoiler.unmarked": "文字沒有被隱藏",
- "compose_form.spoiler_placeholder": "敏感警告訊息",
- "confirmation_modal.cancel": "取消",
- "confirmations.block.block_and_report": "封鎖並檢舉",
- "confirmations.block.confirm": "封鎖",
- "confirmations.block.message": "你確定要封鎖{name}嗎?",
- "confirmations.delete.confirm": "刪除",
- "confirmations.delete.message": "你確定要刪除這文章嗎?",
- "confirmations.delete_list.confirm": "刪除",
- "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?",
- "confirmations.domain_block.confirm": "隱藏整個網站",
- "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或靜音幾個特定目標就好。你從此將不會再看到該站的內容和通知。來自該站的關注者亦會被移除。",
- "confirmations.logout.confirm": "登出",
- "confirmations.logout.message": "確定要登出嗎?",
- "confirmations.mute.confirm": "靜音",
- "confirmations.mute.explanation": "這將會隱藏來自他們的貼文與通知,但是他們還是可以查閱你的貼文與關注你。",
- "confirmations.mute.message": "你確定要將{name}靜音嗎?",
- "confirmations.redraft.confirm": "刪除並編輯",
- "confirmations.redraft.message": "你確定要刪除並重新編輯嗎?所有相關的回覆、轉推與最愛都會被刪除。",
- "confirmations.reply.confirm": "回覆",
- "confirmations.reply.message": "現在回覆將蓋掉您目前正在撰寫的訊息。是否仍要回覆?",
- "confirmations.unfollow.confirm": "取消關注",
- "confirmations.unfollow.message": "真的不要繼續關注 {name} 了嗎?",
- "conversation.delete": "刪除對話",
- "conversation.mark_as_read": "標為已讀",
- "conversation.open": "檢視對話",
- "conversation.with": "與 {names}",
- "directory.federated": "來自已知聯邦宇宙",
- "directory.local": "僅來自 {domain}",
- "directory.new_arrivals": "新貨",
- "directory.recently_active": "最近活躍",
- "embed.instructions": "要內嵌此文章,請將以下代碼貼進你的網站。",
- "embed.preview": "看上去會是這樣:",
- "emoji_button.activity": "活動",
- "emoji_button.custom": "自訂",
- "emoji_button.flags": "旗幟",
- "emoji_button.food": "飲飲食食",
- "emoji_button.label": "加入表情符號",
- "emoji_button.nature": "自然",
- "emoji_button.not_found": "沒有表情符號!! (╯°□°)╯︵ ┻━┻",
- "emoji_button.objects": "物品",
- "emoji_button.people": "人物",
- "emoji_button.recent": "常用",
- "emoji_button.search": "搜尋…",
- "emoji_button.search_results": "搜尋結果",
- "emoji_button.symbols": "符號",
- "emoji_button.travel": "旅遊景物",
- "empty_column.account_timeline": "這裡還沒有嘟文!",
- "empty_column.account_unavailable": "無法取得個人資料",
- "empty_column.blocks": "你還沒有封鎖任何使用者。",
- "empty_column.bookmarked_statuses": "你還沒建立任何書籤。這裡將會顯示你建立的書籤。",
- "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
- "empty_column.direct": "你沒有個人訊息。當你發出或接收個人訊息,就會在這裡出現。",
- "empty_column.domain_blocks": "尚未隱藏任何網域。",
- "empty_column.favourited_statuses": "你還沒收藏任何嘟文。這裡將會顯示你收藏的嘟文。",
- "empty_column.favourites": "還沒有人收藏這則嘟文。這裡將會顯示被收藏的嘟文。",
- "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。",
- "empty_column.hashtag": "這個標籤暫時未有內容。",
- "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
- "empty_column.home.public_timeline": "公共時間軸",
- "empty_column.list": "這個列表暫時未有內容。",
- "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。",
- "empty_column.mutes": "你尚未靜音任何使用者。",
- "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
- "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
- "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,故無法正常顯示頁面。",
- "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
- "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿",
- "errors.unexpected_crash.report_issue": "舉報問題",
- "follow_request.authorize": "批准",
- "follow_request.reject": "拒絕",
- "follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的員工認為可能想要自己審核這些帳號的追蹤請求。",
- "getting_started.developers": "開發者",
- "getting_started.directory": "個人資料目錄",
- "getting_started.documentation": "文件",
- "getting_started.heading": "開始使用",
- "getting_started.invite": "邀請使用者",
- "getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。",
- "getting_started.security": "帳戶安全",
- "getting_started.terms": "服務條款",
- "hashtag.column_header.tag_mode.all": "以及{additional}",
- "hashtag.column_header.tag_mode.any": "或是{additional}",
- "hashtag.column_header.tag_mode.none": "而無需{additional}",
- "hashtag.column_settings.select.no_options_message": "找不到建議",
- "hashtag.column_settings.select.placeholder": "輸入主題標籤…",
- "hashtag.column_settings.tag_mode.all": "全部",
- "hashtag.column_settings.tag_mode.any": "任一",
- "hashtag.column_settings.tag_mode.none": "全不",
- "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
- "home.column_settings.basic": "基本",
- "home.column_settings.show_reblogs": "顯示被轉推的文章",
- "home.column_settings.show_replies": "顯示回應文章",
- "home.hide_announcements": "隱藏公告",
- "home.show_announcements": "顯示公告",
- "intervals.full.days": "{number, plural, one {# 天} other {# 天}}",
- "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}",
- "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}",
- "introduction.federation.action": "下一步",
- "introduction.federation.federated.headline": "站台聯盟",
- "introduction.federation.federated.text": "來自聯盟宇宙中其他站台的公開嘟文將會在站點聯盟時間軸中顯示。",
- "introduction.federation.home.headline": "首頁",
- "introduction.federation.home.text": "你關注使用者的嘟文將會在首頁動態中顯示。你可以關注任何伺服器上的任何人!",
- "introduction.federation.local.headline": "本機",
- "introduction.federation.local.text": "跟您同伺服器之使用者所發的公開嘟文將會顯示在本機時間軸中。",
- "introduction.interactions.action": "Finish toot-orial!",
- "introduction.interactions.favourite.headline": "關注",
- "introduction.interactions.favourite.text": "您能儲存嘟文供稍候觀看,或者收藏嘟文,讓作者知道您喜歡這則嘟文。",
- "introduction.interactions.reblog.headline": "轉嘟",
- "introduction.interactions.reblog.text": "您能藉由轉嘟他人嘟文來分享給您的關注者。",
- "introduction.interactions.reply.headline": "回覆",
- "introduction.interactions.reply.text": "您能回覆其他人或自己的嘟文,這麼做會把這些回覆串成一串對話。",
- "introduction.welcome.action": "開始旅程吧!",
- "introduction.welcome.headline": "第一步",
- "introduction.welcome.text": "歡迎來到聯盟宇宙!等等你就可以廣播訊息及跨越各種各式各樣的伺服器與朋友聊天。但這台伺服器,{domain},非常特別 - 它寄管了你的個人資料,所以請記住它的名字。",
- "keyboard_shortcuts.back": "後退",
- "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單",
- "keyboard_shortcuts.boost": "轉推",
- "keyboard_shortcuts.column": "把標示移動到其中一列",
- "keyboard_shortcuts.compose": "把標示移動到文字輸入區",
- "keyboard_shortcuts.description": "描述",
- "keyboard_shortcuts.direct": "開啟私訊欄",
- "keyboard_shortcuts.down": "在列表往下移動",
- "keyboard_shortcuts.enter": "打開文章",
-<<<<<<< HEAD
- "keyboard_shortcuts.favourite": "赞藏",
- "keyboard_shortcuts.favourites": "to open favourites list",
- "keyboard_shortcuts.federated": "to open federated timeline",
-=======
- "keyboard_shortcuts.favourite": "收藏",
- "keyboard_shortcuts.favourites": "開啟收藏名單",
- "keyboard_shortcuts.federated": "開啟站點聯盟時間軸",
->>>>>>> master
- "keyboard_shortcuts.heading": "鍵盤快速鍵",
- "keyboard_shortcuts.home": "開啟首頁時間軸",
- "keyboard_shortcuts.hotkey": "快速鍵",
- "keyboard_shortcuts.legend": "顯示這個說明",
- "keyboard_shortcuts.local": "開啟本機時間軸",
- "keyboard_shortcuts.mention": "提及作者",
- "keyboard_shortcuts.muted": "開啟靜音使用者名單",
- "keyboard_shortcuts.my_profile": "開啟個人資料頁面",
- "keyboard_shortcuts.notifications": "開啟通知欄",
- "keyboard_shortcuts.open_media": "開啟媒體",
- "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單",
- "keyboard_shortcuts.profile": "開啟作者的個人資料頁面",
- "keyboard_shortcuts.reply": "回覆",
- "keyboard_shortcuts.requests": "開啟關注請求名單",
- "keyboard_shortcuts.search": "把標示移動到搜索",
- "keyboard_shortcuts.spoilers": "to show/hide CW field",
- "keyboard_shortcuts.start": "開啟「開始使用」欄位",
- "keyboard_shortcuts.toggle_hidden": "顯示或隱藏被標為敏感的文字",
- "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體",
- "keyboard_shortcuts.toot": "新的推文",
- "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索",
- "keyboard_shortcuts.up": "在列表往上移動",
- "lightbox.close": "關閉",
- "lightbox.next": "繼續",
- "lightbox.previous": "回退",
- "lightbox.view_context": "檢視內文",
- "lists.account.add": "新增到列表",
- "lists.account.remove": "從列表刪除",
- "lists.delete": "刪除列表",
- "lists.edit": "編輯列表",
- "lists.edit.submit": "變更標題",
- "lists.new.create": "新增列表",
- "lists.new.title_placeholder": "新列表標題",
- "lists.search": "從你關注的用戶中搜索",
- "lists.subheading": "列表",
- "load_pending": "{count, plural, other {# 個新項目}}",
- "loading_indicator.label": "載入中...",
- "media_gallery.toggle_visible": "打開或關上",
- "missing_indicator.label": "找不到內容",
- "missing_indicator.sublabel": "無法找到內容",
- "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
- "navigation_bar.apps": "封鎖的使用者",
- "navigation_bar.blocks": "被你封鎖的用戶",
- "navigation_bar.bookmarks": "書籤",
- "navigation_bar.community_timeline": "本站時間軸",
- "navigation_bar.compose": "撰寫新嘟文",
- "navigation_bar.direct": "個人訊息",
- "navigation_bar.discover": "探索",
- "navigation_bar.domain_blocks": "隱藏的服務站",
- "navigation_bar.edit_profile": "修改個人資料",
- "navigation_bar.favourites": "最愛的內容",
- "navigation_bar.filters": "靜音詞彙",
- "navigation_bar.follow_requests": "關注請求",
- "navigation_bar.follows_and_followers": "關注及關注者",
- "navigation_bar.info": "關於本服務站",
- "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵",
- "navigation_bar.lists": "列表",
- "navigation_bar.logout": "登出",
- "navigation_bar.mutes": "被你靜音的用戶",
- "navigation_bar.personal": "個人",
- "navigation_bar.pins": "置頂文章",
- "navigation_bar.preferences": "偏好設定",
- "navigation_bar.public_timeline": "跨站時間軸",
- "navigation_bar.security": "安全",
- "notification.favourite": "{name} 赞藏了你的文章",
- "notification.follow": "{name} 開始關注你",
- "notification.follow_request": "{name} 要求關注你",
- "notification.mention": "{name} 提及你",
- "notification.own_poll": "您的投票已結束",
- "notification.poll": "您投過的投票已經結束",
- "notification.reblog": "{name} 轉推你的文章",
- "notifications.clear": "清空通知紀錄",
- "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?",
- "notifications.column_settings.alert": "顯示桌面通知",
-<<<<<<< HEAD
- "notifications.column_settings.favourite": "赞藏了你的文章:",
- "notifications.column_settings.filter_bar.advanced": "Display all categories",
- "notifications.column_settings.filter_bar.category": "Quick filter bar",
- "notifications.column_settings.filter_bar.show": "Show",
-=======
- "notifications.column_settings.favourite": "收藏了你的文章:",
- "notifications.column_settings.filter_bar.advanced": "顯示所有分類",
- "notifications.column_settings.filter_bar.category": "快速過濾欄",
- "notifications.column_settings.filter_bar.show": "顯示",
->>>>>>> master
- "notifications.column_settings.follow": "關注你:",
- "notifications.column_settings.follow_request": "新的關注請求:",
- "notifications.column_settings.mention": "提及你:",
- "notifications.column_settings.poll": "投票結果:",
- "notifications.column_settings.push": "推送通知",
- "notifications.column_settings.reblog": "轉推你的文章:",
- "notifications.column_settings.show": "在通知欄顯示",
- "notifications.column_settings.sound": "播放音效",
- "notifications.filter.all": "全部",
- "notifications.filter.boosts": "轉嘟",
- "notifications.filter.favourites": "最愛",
- "notifications.filter.follows": "關注的使用者",
- "notifications.filter.mentions": "提及",
- "notifications.filter.polls": "投票結果",
- "notifications.group": "{count} 條通知",
- "poll.closed": "已關閉",
- "poll.refresh": "重新整理",
- "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}",
- "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}",
- "poll.vote": "投票",
- "poll.voted": "你已對此問題投票",
- "poll_button.add_poll": "建立投票",
- "poll_button.remove_poll": "移除投票",
- "privacy.change": "調整私隱設定",
- "privacy.direct.long": "只有提及的用戶能看到",
- "privacy.direct.short": "私人訊息",
- "privacy.private.long": "只有關注你用戶能看到",
- "privacy.private.short": "關注者",
- "privacy.public.long": "在公共時間軸顯示",
- "privacy.public.short": "公共",
- "privacy.unlisted.long": "公開,但不在公共時間軸顯示",
- "privacy.unlisted.short": "公開",
- "refresh": "重新整理",
- "regeneration_indicator.label": "載入中……",
- "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!",
- "relative_time.days": "{number}日",
- "relative_time.hours": "{number}小時",
- "relative_time.just_now": "剛剛",
- "relative_time.minutes": "{number}分鐘",
- "relative_time.seconds": "{number}秒",
- "relative_time.today": "今天",
- "reply_indicator.cancel": "取消",
- "report.forward": "轉寄到 {target}",
- "report.forward_hint": "這個帳戶屬於其他服務站。要向該服務站發送匿名的舉報訊息嗎?",
- "report.hint": "這訊息會發送到你服務站的管理員。你可以提供舉報這個帳戶的理由:",
- "report.placeholder": "額外訊息",
- "report.submit": "提交",
- "report.target": "舉報",
- "search.placeholder": "搜尋",
- "search_popout.search_format": "高級搜索格式",
- "search_popout.tips.full_text": "輸入簡單的文字,搜索由你發放、赞藏、轉推和提及你的文章,以及符合的用戶名稱,帳號名稱和標籤。",
- "search_popout.tips.hashtag": "標籤",
- "search_popout.tips.status": "文章",
- "search_popout.tips.text": "輸入簡單的文字,搜索符合的用戶名稱,帳號名稱和標籤",
- "search_popout.tips.user": "用戶",
- "search_results.accounts": "使用者",
- "search_results.hashtags": "標籤",
- "search_results.statuses": "文章",
- "search_results.statuses_fts_disabled": "「依內容搜尋嘟文」未在此 Mastodon 伺服器啟用。",
- "search_results.total": "{count, number} 項結果",
- "status.admin_account": "開啟 @{name} 的管理介面",
- "status.admin_status": "在管理介面開啟此嘟文",
- "status.block": "封鎖 @{name}",
- "status.bookmark": "書籤",
- "status.cancel_reblog_private": "取消轉推",
- "status.cannot_reblog": "這篇文章無法被轉推",
- "status.copy": "將連結複製到嘟文中",
- "status.delete": "刪除",
- "status.detailed_status": "對話的詳細內容",
- "status.direct": "私訊 @{name}",
- "status.embed": "鑲嵌",
-<<<<<<< HEAD
- "status.favourite": "赞藏",
- "status.filtered": "Filtered",
-=======
- "status.favourite": "收藏",
- "status.filtered": "已過濾",
->>>>>>> master
- "status.load_more": "載入更多",
- "status.media_hidden": "隱藏媒體內容",
- "status.mention": "提及 @{name}",
- "status.more": "更多",
- "status.mute": "把 @{name} 靜音",
- "status.mute_conversation": "靜音對話",
- "status.open": "展開文章",
- "status.pin": "置頂到資料頁",
- "status.pinned": "置頂文章",
- "status.read_more": "閱讀更多",
- "status.reblog": "轉推",
- "status.reblog_private": "轉推到原讀者",
- "status.reblogged_by": "{name} 轉推",
- "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
- "status.redraft": "刪除並編輯",
- "status.remove_bookmark": "移除書籤",
- "status.reply": "回應",
- "status.replyAll": "回應所有人",
- "status.report": "舉報 @{name}",
- "status.sensitive_warning": "敏感內容",
- "status.share": "分享",
- "status.show_less": "減少顯示",
- "status.show_less_all": "減少顯示這類文章",
- "status.show_more": "顯示更多",
- "status.show_more_all": "顯示更多這類文章",
- "status.show_thread": "顯示討論串",
- "status.uncached_media_warning": "無法使用",
- "status.unmute_conversation": "解禁對話",
- "status.unpin": "解除置頂",
- "suggestions.dismiss": "關閉建議",
- "suggestions.header": "您可能對這些東西有興趣…",
- "tabs_bar.federated_timeline": "跨站",
- "tabs_bar.home": "主頁",
- "tabs_bar.local_timeline": "本站",
- "tabs_bar.notifications": "通知",
- "tabs_bar.search": "搜尋",
- "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}",
- "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}",
- "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}",
- "time_remaining.moments": "剩餘時間",
- "time_remaining.seconds": "剩餘 {number, plural, one {# 秒} other {# 秒}}",
- "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
- "timeline_hint.resources.followers": "Followers",
- "timeline_hint.resources.follows": "Follows",
- "timeline_hint.resources.statuses": "Older toots",
- "trends.count_by_accounts": "{count} 位用戶在討論",
- "trends.trending_now": "目前趨勢",
- "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
- "upload_area.title": "將檔案拖放至此上載",
- "upload_button.label": "上載媒體檔案",
- "upload_error.limit": "已達到檔案上傳限制。",
- "upload_error.poll": "不允許在投票上傳檔案。",
- "upload_form.audio_description": "簡單描述內容給聽障人士",
- "upload_form.description": "為視覺障礙人士添加文字說明",
- "upload_form.edit": "編輯",
- "upload_form.undo": "刪除",
- "upload_form.video_description": "簡單描述給聽障或視障人士",
- "upload_modal.analyzing_picture": "正在分析圖片…",
- "upload_modal.apply": "套用",
- "upload_modal.description_placeholder": "A quick brown fox 跳過那隻懶狗",
- "upload_modal.detect_text": "從圖片偵測文字",
- "upload_modal.edit_media": "編輯媒體",
- "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。",
- "upload_modal.preview_label": "預覽 ({ratio})",
- "upload_progress.label": "上載中……",
- "video.close": "關閉影片",
- "video.download": "下載檔案",
- "video.exit_fullscreen": "退出全熒幕",
- "video.expand": "展開影片",
- "video.fullscreen": "全熒幕",
- "video.hide": "隱藏影片",
- "video.mute": "靜音",
- "video.pause": "暫停",
- "video.play": "播放",
- "video.unmute": "解除靜音"
-}
diff --git a/app/models/status.rb.orig b/app/models/status.rb.orig
deleted file mode 100644
index aeb8ef4315..0000000000
--- a/app/models/status.rb.orig
+++ /dev/null
@@ -1,523 +0,0 @@
-# frozen_string_literal: true
-# == Schema Information
-#
-# Table name: statuses
-#
-# id :bigint(8) not null, primary key
-# uri :string
-# text :text default(""), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# in_reply_to_id :bigint(8)
-# reblog_of_id :bigint(8)
-# url :string
-# sensitive :boolean default(FALSE), not null
-# visibility :integer default("public"), not null
-# spoiler_text :text default(""), not null
-# reply :boolean default(FALSE), not null
-# language :string
-# conversation_id :bigint(8)
-# local :boolean
-# account_id :bigint(8) not null
-# application_id :bigint(8)
-# in_reply_to_account_id :bigint(8)
-# poll_id :bigint(8)
-# deleted_at :datetime
-#
-
-class Status < ApplicationRecord
- before_destroy :unlink_from_conversations
-
- include Discard::Model
- include Paginable
- include Cacheable
- include StatusThreadingConcern
- include RateLimitable
-
- rate_limit by: :account, family: :statuses
-
- self.discard_column = :deleted_at
-
- # If `override_timestamps` is set at creation time, Snowflake ID creation
- # will be based on current time instead of `created_at`
- attr_accessor :override_timestamps
-
- update_index('statuses#status', :proper)
-
- enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility
-
- belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
-
- belongs_to :account, inverse_of: :statuses
- belongs_to :in_reply_to_account, foreign_key: 'in_reply_to_account_id', class_name: 'Account', optional: true
- belongs_to :conversation, optional: true
- belongs_to :preloadable_poll, class_name: 'Poll', foreign_key: 'poll_id', optional: true
-
- belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
- belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
-
- has_many :favourites, inverse_of: :status, dependent: :destroy
- has_many :bookmarks, inverse_of: :status, dependent: :destroy
- has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
- has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread
- has_many :mentions, dependent: :destroy, inverse_of: :status
- has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status
- has_many :media_attachments, dependent: :nullify
-
- has_and_belongs_to_many :tags
- has_and_belongs_to_many :preview_cards
-
- has_one :notification, as: :activity, dependent: :destroy
- has_one :status_stat, inverse_of: :status
- has_one :poll, inverse_of: :status, dependent: :destroy
-
- validates :uri, uniqueness: true, presence: true, unless: :local?
- validates :text, presence: true, unless: -> { with_media? || reblog? }
- validates_with StatusLengthValidator
- validates_with DisallowedHashtagsValidator
- validates :reblog, uniqueness: { scope: :account }, if: :reblog?
- validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
-
- accepts_nested_attributes_for :poll
-
- default_scope { recent.kept }
-
- scope :recent, -> { reorder(id: :desc) }
- scope :remote, -> { where(local: false).where.not(uri: nil) }
- scope :local, -> { where(local: true).or(where(uri: nil)) }
-
-<<<<<<< HEAD
- #scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
- scope :without_replies, -> { where('statuses.reply = FALSE') }
- #scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
- scope :without_reblogs, -> { joins('INNER JOIN statuses ori ON (statuses.reblog_of_id is NULL AND ori.id = statuses.id) OR ori.id = statuses.reblog_of_id')
- .where('ori.account_id = statuses.account_id') }
-=======
- scope :with_accounts, ->(ids) { where(id: ids).includes(:account) }
- 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') }
->>>>>>> master
- scope :with_public_visibility, -> { where(visibility: :public) }
- scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
- scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced_at: nil }) }
- scope :including_silenced_accounts, -> { left_outer_joins(:account).where.not(accounts: { silenced_at: nil }) }
- scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
- scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) }
- scope :tagged_with_all, ->(tags) {
- Array(tags).map(&:id).map(&:to_i).reduce(self) do |result, id|
- result.joins("INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}")
- end
- }
- scope :tagged_with_none, ->(tags) {
- Array(tags).map(&:id).map(&:to_i).reduce(self) do |result, id|
- result.joins("LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}")
- .where("t#{id}.tag_id IS NULL")
- end
- }
-
- cache_associated :application,
- :media_attachments,
- :conversation,
- :status_stat,
- :tags,
- :preview_cards,
- :preloadable_poll,
- account: :account_stat,
- active_mentions: { account: :account_stat },
- reblog: [
- :application,
- :tags,
- :preview_cards,
- :media_attachments,
- :conversation,
- :status_stat,
- :preloadable_poll,
- account: :account_stat,
- active_mentions: { account: :account_stat },
- ],
- thread: { account: :account_stat }
-
- delegate :domain, to: :account, prefix: true
-
- REAL_TIME_WINDOW = 6.hours
-
- def searchable_by(preloaded = nil)
- ids = []
-
- ids << account_id if local?
-
- if preloaded.nil?
- ids += mentions.where(account: Account.local, silent: false).pluck(:account_id)
- ids += favourites.where(account: Account.local).pluck(:account_id)
- ids += reblogs.where(account: Account.local).pluck(:account_id)
- ids += bookmarks.where(account: Account.local).pluck(:account_id)
- else
- ids += preloaded.mentions[id] || []
- ids += preloaded.favourites[id] || []
- ids += preloaded.reblogs[id] || []
- ids += preloaded.bookmarks[id] || []
- end
-
- ids.uniq
- end
-
- def reply?
- !in_reply_to_id.nil? || attributes['reply']
- end
-
- def local?
- attributes['local'] || uri.nil?
- end
-
- def reblog?
- !reblog_of_id.nil?
- end
-
- def within_realtime_window?
- created_at >= REAL_TIME_WINDOW.ago
- end
-
- def verb
- if destroyed?
- :delete
- else
- reblog? ? :share : :post
- end
- end
-
- def object_type
- reply? ? :comment : :note
- end
-
- def proper
- reblog? ? reblog : self
- end
-
- def content
- proper.text
- end
-
- def target
- reblog
- end
-
- def preview_card
- preview_cards.first
- end
-
- def hidden?
- !distributable?
- end
-
- def distributable?
- public_visibility? || unlisted_visibility?
- end
-
- alias sign? distributable?
-
- def with_media?
- media_attachments.any?
- end
-
- def non_sensitive_with_media?
- !sensitive? && with_media?
- end
-
- def reported?
- @reported ||= Report.where(target_account: account).unresolved.where('? = ANY(status_ids)', id).exists?
- end
-
- def emojis
- return @emojis if defined?(@emojis)
-
- fields = [spoiler_text, text]
- fields += preloadable_poll.options unless preloadable_poll.nil?
-
- @emojis = CustomEmoji.from_text(fields.join(' '), account.domain)
- end
-
- def mark_for_mass_destruction!
- @marked_for_mass_destruction = true
- end
-
- def marked_for_mass_destruction?
- @marked_for_mass_destruction
- end
-
- def replies_count
- status_stat&.replies_count || 0
- end
-
- def reblogs_count
- status_stat&.reblogs_count || 0
- end
-
- def favourites_count
- status_stat&.favourites_count || 0
- end
-
- def increment_count!(key)
- update_status_stat!(key => public_send(key) + 1)
- end
-
- def decrement_count!(key)
- update_status_stat!(key => [public_send(key) - 1, 0].max)
- end
-
- after_create_commit :increment_counter_caches
- after_destroy_commit :decrement_counter_caches
-
- after_create_commit :store_uri, if: :local?
- after_create_commit :update_statistics, if: :local?
-
- around_create Mastodon::Snowflake::Callbacks
-
- before_validation :prepare_contents, if: :local?
- before_validation :set_reblog
- before_validation :set_visibility
- before_validation :set_conversation
- before_validation :set_local
-
- after_create :set_poll_id
-
- class << self
- def selectable_visibilities
- visibilities.keys - %w(direct limited)
- end
-
- def in_chosen_languages(account)
- where(language: nil).or where(language: account.chosen_languages)
- end
-
- def as_public_timeline(account = nil, local_only = false)
- query = timeline_scope(local_only).without_replies
-
- apply_timeline_filters(query, account, [:local, true].include?(local_only))
- end
-
- def as_tag_timeline(tag, account = nil, local_only = false)
- query = timeline_scope(local_only).tagged_with(tag)
-
- apply_timeline_filters(query, account, local_only)
- end
-
- def as_outbox_timeline(account)
- where(account: account, visibility: :public)
- end
-
- def favourites_map(status_ids, account_id)
- Favourite.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |f, h| h[f.status_id] = true }
- end
-
- def bookmarks_map(status_ids, account_id)
- Bookmark.select('status_id').where(status_id: status_ids).where(account_id: account_id).map { |f| [f.status_id, true] }.to_h
- end
-
- def reblogs_map(status_ids, account_id)
- unscoped.select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).each_with_object({}) { |s, h| h[s.reblog_of_id] = true }
- end
-
- def mutes_map(conversation_ids, account_id)
- ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).each_with_object({}) { |m, h| h[m.conversation_id] = true }
- end
-
- def pins_map(status_ids, account_id)
- StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
- end
-
- def reload_stale_associations!(cached_items)
- account_ids = []
-
- cached_items.each do |item|
- account_ids << item.account_id
- account_ids << item.reblog.account_id if item.reblog?
- end
-
- account_ids.uniq!
-
- return if account_ids.empty?
-
- accounts = Account.where(id: account_ids).includes(:account_stat).each_with_object({}) { |a, h| h[a.id] = a }
-
- cached_items.each do |item|
- item.account = accounts[item.account_id]
- item.reblog.account = accounts[item.reblog.account_id] if item.reblog?
- end
- end
-
- def permitted_for(target_account, account)
- visibility = [:public, :unlisted]
-
- if account.nil?
- where(visibility: visibility)
- elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
- none
- elsif account.id == target_account.id # author can see own stuff
- all
- else
- # followers can see followers-only stuff, but also things they are mentioned in.
- # non-followers can see everything that isn't private/direct, but can see stuff they are mentioned in.
- visibility.push(:private) if account.following?(target_account)
-
- scope = left_outer_joins(:reblog)
-
- scope.where(visibility: visibility)
- .or(scope.where(id: account.mentions.select(:status_id)))
- .merge(scope.where(reblog_of_id: nil).or(scope.where.not(reblogs_statuses: { account_id: account.excluded_from_timeline_account_ids })))
- end
- end
-
- def from_text(text)
- return [] if text.blank?
-
- text.scan(FetchLinkCardService::URL_PATTERN).map(&:first).uniq.map do |url|
- status = begin
- if TagManager.instance.local_url?(url)
- ActivityPub::TagManager.instance.uri_to_resource(url, Status)
- else
- EntityCache.instance.status(url)
- end
- end
- status&.distributable? ? status : nil
- end.compact
- end
-
- private
-
- def timeline_scope(scope = false)
- starting_scope = case scope
- when :local, true
- Status.local
- when :remote
- Status.remote
- else
- Status
- end
-
- starting_scope
- .with_public_visibility
- .without_reblogs
- end
-
- def apply_timeline_filters(query, account, local_only)
- if account.nil?
- filter_timeline_default(query)
- else
- filter_timeline_for_account(query, account, local_only)
- end
- end
-
- def filter_timeline_for_account(query, account, local_only)
- query = query.not_excluded_by_account(account)
- query = query.not_domain_blocked_by_account(account) unless local_only
- query = query.in_chosen_languages(account) if account.chosen_languages.present?
- query.merge(account_silencing_filter(account))
- end
-
- def filter_timeline_default(query)
- query.excluding_silenced_accounts
- end
-
- def account_silencing_filter(account)
- if account.silenced?
- including_myself = left_outer_joins(:account).where(account_id: account.id).references(:accounts)
- excluding_silenced_accounts.or(including_myself)
- else
- excluding_silenced_accounts
- end
- end
- end
-
- def status_stat
- super || build_status_stat
- end
-
- private
-
- def update_status_stat!(attrs)
- return if marked_for_destruction? || destroyed?
-
- status_stat.update(attrs)
- end
-
- def store_uri
- update_column(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil?
- end
-
- def prepare_contents
- text&.strip!
- spoiler_text&.strip!
- end
-
- def set_reblog
- self.reblog = reblog.reblog if reblog? && reblog.reblog?
- end
-
- def set_poll_id
- update_column(:poll_id, poll.id) unless poll.nil?
- end
-
- def set_visibility
- self.visibility = reblog.visibility if reblog? && visibility.nil?
- self.visibility = (account.locked? ? :private : :public) if visibility.nil?
- self.sensitive = false if sensitive.nil?
- end
-
- def set_conversation
- self.thread = thread.reblog if thread&.reblog?
-
- self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply
-
- if reply? && !thread.nil?
- self.in_reply_to_account_id = carried_over_reply_to_account_id
- self.conversation_id = thread.conversation_id if conversation_id.nil?
- elsif conversation_id.nil?
- self.conversation = Conversation.new
- end
- end
-
- def carried_over_reply_to_account_id
- if thread.account_id == account_id && thread.reply?
- thread.in_reply_to_account_id
- else
- thread.account_id
- end
- end
-
- def set_local
- self.local = account.local?
- end
-
- def update_statistics
- return unless distributable?
-
- ActivityTracker.increment('activity:statuses:local')
- end
-
- def increment_counter_caches
- return if direct_visibility?
-
- account&.increment_count!(:statuses_count)
- reblog&.increment_count!(:reblogs_count) if reblog?
- thread&.increment_count!(:replies_count) if in_reply_to_id.present? && distributable?
- end
-
- def decrement_counter_caches
- return if direct_visibility? || marked_for_mass_destruction?
-
- account&.decrement_count!(:statuses_count)
- reblog&.decrement_count!(:reblogs_count) if reblog?
- thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && distributable?
- end
-
- def unlink_from_conversations
- return unless direct_visibility?
-
- mentioned_accounts = mentions.includes(:account).map(&:account)
- inbox_owners = mentioned_accounts.select(&:local?) + (account.local? ? [account] : [])
-
- inbox_owners.each do |inbox_owner|
- AccountConversation.remove_status(inbox_owner, self)
- end
- end
-end
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index a69f8589c6..a35476ff59 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -22,7 +22,7 @@ class InitialStateSerializer < ActiveModel::Serializer
mascot: instance_presenter.mascot&.file&.url,
profile_directory: Setting.profile_directory,
trends: Setting.trends,
- tree_root: ENV['TREE_ADDRESS'],
+ tree_root: Rails.configuration.x.tree_address,
pinned_info: Setting.site_description,
}
diff --git a/app/services/fetch_link_card_service.rb.orig b/app/services/fetch_link_card_service.rb.orig
deleted file mode 100644
index 466e813150..0000000000
--- a/app/services/fetch_link_card_service.rb.orig
+++ /dev/null
@@ -1,182 +0,0 @@
-# frozen_string_literal: true
-
-class FetchLinkCardService < BaseService
- URL_PATTERN = %r{
- ( # $1 URL
- (https?:\/\/) # $2 Protocol (required)
- (#{Twitter::Regex[:valid_domain]}) # $3 Domain(s)
- (?::(#{Twitter::Regex[:valid_port_number]}))? # $4 Port number (optional)
- (/#{Twitter::Regex[:valid_url_path]}*)? # $5 URL Path and anchor
- (\?#{Twitter::Regex[:valid_url_query_chars]}*#{Twitter::Regex[:valid_url_query_ending_chars]})? # $6 Query String
- )
- }iox
-
- def call(status)
- @status = status
- @url = parse_urls
-
- return if @url.nil? || @status.preview_cards.any?
-
- @url = @url.to_s
-
- RedisLock.acquire(lock_options) do |lock|
- if lock.acquired?
- @card = PreviewCard.find_by(url: @url)
- process_url if @card.nil? || @card.updated_at <= 2.weeks.ago || @card.missing_image?
- else
- raise Mastodon::RaceConditionError
- end
- end
-
- attach_card if @card&.persisted?
- rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
- Rails.logger.debug "Error fetching link #{@url}: #{e}"
- nil
- end
-
- private
-
- def process_url
- @card ||= PreviewCard.new(url: @url)
-
-<<<<<<< HEAD
- Request.new(:get, @url).perform do |res|
- if res.code == 200 && res.mime_type.end_with?('text/html')
-=======
- attempt_oembed || attempt_opengraph
- end
-
- def html
- return @html if defined?(@html)
-
- Request.new(:get, @url).add_headers('Accept' => 'text/html', 'User-Agent' => Mastodon::Version.user_agent + ' Bot').perform do |res|
- if res.code == 200 && res.mime_type == 'text/html'
->>>>>>> master
- @html = res.body_with_limit
- @html_charset = res.charset
- else
- @html = nil
- @html_charset = nil
- end
- end
- end
-
- def attach_card
- @status.preview_cards << @card
- Rails.cache.delete(@status)
- end
-
- def parse_urls
- if @status.local?
- urls = @status.text.scan(URL_PATTERN).map { |array| Addressable::URI.parse(array[0]).normalize }
- else
- html = Nokogiri::HTML(@status.text)
- links = html.css('a')
- urls = links.map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.compact.map(&:normalize).compact
- end
-
- urls.reject { |uri| bad_url?(uri) }.first
- end
-
- def bad_url?(uri)
- # Avoid local instance URLs and invalid URLs
- uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme)
- end
-
- def mention_link?(a)
- @status.mentions.any? do |mention|
- a['href'] == ActivityPub::TagManager.instance.url_for(mention.account)
- end
- end
-
- def skip_link?(a)
- # Avoid links for hashtags and mentions (microformats)
- a['rel']&.include?('tag') || a['class']&.match?(/u-url|h-card/) || mention_link?(a)
- end
-
- def attempt_oembed
- service = FetchOEmbedService.new
- url_domain = Addressable::URI.parse(@url).normalized_host
- cached_endpoint = Rails.cache.read("oembed_endpoint:#{url_domain}")
-
- embed = service.call(@url, cached_endpoint: cached_endpoint) unless cached_endpoint.nil?
- embed ||= service.call(@url, html: html) unless html.nil?
-
- return false if embed.nil?
-
- url = Addressable::URI.parse(service.endpoint_url)
-
- @card.type = embed[:type]
- @card.title = embed[:title] || ''
- @card.author_name = embed[:author_name] || ''
- @card.author_url = embed[:author_url].present? ? (url + embed[:author_url]).to_s : ''
- @card.provider_name = embed[:provider_name] || ''
- @card.provider_url = embed[:provider_url].present? ? (url + embed[:provider_url]).to_s : ''
- @card.width = 0
- @card.height = 0
-
- case @card.type
- when 'link'
- @card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
- when 'photo'
- return false if embed[:url].blank?
-
- @card.embed_url = (url + embed[:url]).to_s
- @card.image_remote_url = (url + embed[:url]).to_s
- @card.width = embed[:width].presence || 0
- @card.height = embed[:height].presence || 0
- when 'video'
- @card.width = embed[:width].presence || 0
- @card.height = embed[:height].presence || 0
- @card.html = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED)
- @card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
- when 'rich'
- # Most providers rely on