about summary refs log tree commit diff
diff options
context:
space:
mode:
authormultiple creatures <dev@multiple-creature.party>2019-04-27 23:31:25 -0500
committermultiple creatures <dev@multiple-creature.party>2019-05-21 03:16:22 -0500
commit1ab60fea4831855657e666db6ef00ec7efe0f970 (patch)
tree16b53036d1ddb955e254e75ff2ca1b4532c5ee53
parented9c8f67c4ea7985e2a29e65e352694b9d52ecdc (diff)
Split boosts/repeats off to their own tab and add to the API.
-rw-r--r--app/controllers/accounts_controller.rb34
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb12
-rw-r--r--app/javascript/flavours/glitch/actions/timelines.js2
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/header.js1
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js21
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.js1
-rw-r--r--app/javascript/mastodon/locales/en.json1
-rw-r--r--app/views/accounts/show.html.haml1
-rw-r--r--config/locales/en.yml4
-rw-r--r--config/routes.rb1
10 files changed, 44 insertions, 34 deletions
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 58e176364..c4cf4a77b 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -59,14 +59,20 @@ class AccountsController < ApplicationController
   private
 
   def show_pinned_statuses?
-    [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
+    [reblogs_requested?, replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
   end
 
   def filtered_statuses
-    default_statuses.tap do |statuses|
-      statuses.merge!(hashtag_scope)    if tag_requested?
-      statuses.merge!(only_media_scope) if media_requested?
-      statuses.merge!(no_replies_scope) unless @account.replies && replies_requested?
+    if reblogs_requested?
+      default_statuses.reblogs
+    elsif replies_requested?
+      @account.replies ? default_statuses : default_statuses.without_replies
+    elsif media_requested?
+      default_statuses.where(id: account_media_status_ids)
+    elsif tag_requested?
+      default_statuses.hashtag_scope
+    else
+      default_statuses.without_replies.without_reblogs
     end
   end
 
@@ -74,18 +80,6 @@ class AccountsController < ApplicationController
     @account.statuses.not_local_only.where(visibility: [:public, :unlisted])
   end
 
-  def only_media_scope
-    Status.where(id: account_media_status_ids)
-  end
-
-  def account_media_status_ids
-    @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
-  end
-
-  def no_replies_scope
-    Status.without_replies
-  end
-
   def hashtag_scope
     tag = Tag.find_normalized(params[:tag])
 
@@ -115,6 +109,8 @@ class AccountsController < ApplicationController
       short_account_media_url(@account, max_id: max_id, min_id: min_id)
     elsif replies_requested?
       short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
+    elsif reblogs_requested?
+      short_account_reblogs_url(@account, max_id: max_id, min_id: min_id)
     else
       short_account_url(@account, max_id: max_id, min_id: min_id)
     end
@@ -128,6 +124,10 @@ class AccountsController < ApplicationController
     request.path.ends_with?('/with_replies')
   end
 
+  def reblogs_requested?
+    request.path.ends_with?('/reblogs')
+  end
+
   def tag_requested?
     request.path.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
   end
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index 8cd8f8e79..8a216ce0d 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -28,14 +28,14 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 
   def account_statuses
     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
-    statuses = statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
 
-    statuses.merge!(only_media_scope) if truthy_param?(:only_media)
-    statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
-    statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
-    statuses.merge!(hashtag_scope)    if params[:tagged].present?
+    statuses = statuses.without_replies if !@account.replies || truthy_param?(:exclude_replies)
+    statuses = statuses.without_reblogs if truthy_param?(:exclude_reblogs)
+    statuses = statuses.reblogs if truthy_param?(:reblogs) && !truthy_param?(:exclude_reblogs)
+    statuses = statuses.where(id: account_media_status_ids) if truthy_param?(:only_media)
+    statuses = statuses.hashtag_scope if params[:tagged].present?
 
-    statuses
+    statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def permitted_account_statuses
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index cca571583..07d50d163 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -95,7 +95,7 @@ export const expandHomeTimeline            = ({ maxId } = {}, done = noOp) => ex
 export const expandPublicTimeline          = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
 export const expandCommunityTimeline       = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
 export const expandDirectTimeline          = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
-export const expandAccountTimeline         = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
+export const expandAccountTimeline         = (accountId, { maxId, withReplies, reblogs } = {}) => expandTimeline(`account:${accountId}${reblogs ? ':reblogs' : withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, reblogs: reblogs, max_id: maxId });
 export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
 export const expandAccountMediaTimeline    = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
 export const expandListTimeline            = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/header.js b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
index 0faa8a424..299a8c9e5 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
@@ -120,6 +120,7 @@ export default class Header extends ImmutablePureComponent {
           <div className='account__section-headline'>
             <NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
             <NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>
+            <NavLink exact to={`/accounts/${account.get('id')}/reblogs`}><FormattedMessage id='account.reblogs' defaultMessage='Boosts' /></NavLink>
             <NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
           </div>
         )}
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index 93d8fc9ec..2e3bd5ce5 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -15,13 +15,13 @@ import { FormattedMessage } from 'react-intl';
 import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
 import MissingIndicator from 'flavours/glitch/components/missing_indicator';
 
-const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
-  const path = withReplies ? `${accountId}:with_replies` : accountId;
+const mapStateToProps = (state, { params: { accountId }, withReplies = false, reblogs = false }) => {
+  const path = reblogs ? `${accountId}:reblogs` : withReplies ? `${accountId}:with_replies` : accountId;
 
   return {
     isAccount: !!state.getIn(['accounts', accountId]),
     statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
-    featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
+    featuredStatusIds: (withReplies || reblogs) ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
     isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
     hasMore:   state.getIn(['timelines', `account:${path}`, 'hasMore']),
   };
@@ -38,28 +38,29 @@ export default class AccountTimeline extends ImmutablePureComponent {
     isLoading: PropTypes.bool,
     hasMore: PropTypes.bool,
     withReplies: PropTypes.bool,
+    reblogs: PropTypes.bool,
     isAccount: PropTypes.bool,
   };
 
   componentWillMount () {
-    const { params: { accountId }, withReplies } = this.props;
+    const { params: { accountId }, withReplies, reblogs } = this.props;
 
     this.props.dispatch(fetchAccount(accountId));
     this.props.dispatch(fetchAccountIdentityProofs(accountId));
-    if (!withReplies) {
+    if (!withReplies && !reblogs) {
       this.props.dispatch(expandAccountFeaturedTimeline(accountId));
     }
-    this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
+    this.props.dispatch(expandAccountTimeline(accountId, { withReplies: withReplies, reblogs: reblogs }));
   }
 
   componentWillReceiveProps (nextProps) {
-    if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
+    if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies || nextProps.reblogs !== this.props.reblogs) {
       this.props.dispatch(fetchAccount(nextProps.params.accountId));
       this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
-      if (!nextProps.withReplies) {
+      if (!nextProps.withReplies && !nextProps.reblogs) {
         this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
       }
-      this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
+      this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies, reblogs: nextProps.params.reblogs }));
     }
   }
 
@@ -68,7 +69,7 @@ export default class AccountTimeline extends ImmutablePureComponent {
   }
 
   handleLoadMore = maxId => {
-    this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies }));
+    this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies, reblogs: this.props.reblogs }));
   }
 
   setRef = c => {
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js
index 13c71337a..1d8d906b0 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.js
@@ -500,6 +500,7 @@ export default class UI extends React.Component {
               <WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
 
               <WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
+              <WrappedRoute path='/accounts/:accountId/reblogs' component={AccountTimeline} content={children} componentParams={{ reblogs: true }} />
               <WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
               <WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
               <WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index a21f0049f..9417a745d 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -24,6 +24,7 @@
   "account.mute_notifications": "Mute notifications from @{name}",
   "account.muted": "Muted",
   "account.posts": "Roars",
+  "account.reblogs": "Repeats",
   "account.posts_with_replies": "Roars & growls",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval. Click to cancel join request",
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index fc9eb04e6..aa7903304 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -32,6 +32,7 @@
         = active_link_to t('accounts.posts_tab_heading'), short_account_url(@account)
         - if @account.replies
           = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
+        = active_link_to t('accounts.reblogs_tab_heading'), short_account_reblogs_url(@account)
         = active_link_to t('accounts.media'), short_account_media_url(@account)
 
       - if user_signed_in? && @account.blocking?(current_account)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 72c9379c3..e7d596885 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -61,7 +61,11 @@ en:
     posts:
       one: Roar
       other: Roars
+    reblogs:
+      one: Repeat
+      other: Repeats
     posts_tab_heading: Roars
+    reblogs_tab_heading: Repeats
     posts_with_replies: Roars & growls
     reserved_username: The creaturename is reserved
     roles:
diff --git a/config/routes.rb b/config/routes.rb
index 5614a7cdc..83ef69f15 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -77,6 +77,7 @@ Rails.application.routes.draw do
   get '/@:username', to: 'accounts#show', as: :short_account
   get '/@:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
   get '/@:username/media', to: 'accounts#show', as: :short_account_media
+  get '/@:username/reblogs', to: 'accounts#show', as: :short_account_reblogs
   get '/@:username/tagged/:tag', to: 'accounts#show', as: :short_account_tag
   get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status
   get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status