diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 5acbf749..0fa6d072 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,7 +2,7 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject, :verify, :unverify, :add_donor_badge, :remove_donor_badge, :add_investor_badge, :remove_investor_badge] + before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject, :verify, :unverify, :add_donor_badge, :remove_donor_badge, :add_investor_badge, :remove_investor_badge, :edit_pro, :save_pro] before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] @@ -162,6 +162,17 @@ module Admin redirect_to admin_account_path(@account.id) end + def edit_pro + authorize @account, :edit_pro? + end + + def save_pro + authorize @account, :edit_pro? + + @account.update!(pro_params) + redirect_to edit_pro_admin_account_path(@account.id) + end + private def set_account @@ -196,5 +207,9 @@ module Admin :staff ) end + + def pro_params + params.require(:account).permit(:is_pro, :pro_expires_at) + end end end diff --git a/app/javascript/gabsocial/components/icon_with_badge.js b/app/javascript/gabsocial/components/icon_with_badge.js index aa87f425..c42b9337 100644 --- a/app/javascript/gabsocial/components/icon_with_badge.js +++ b/app/javascript/gabsocial/components/icon_with_badge.js @@ -1,14 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; import Icon from 'gabsocial/components/icon'; +import { shortNumberFormat } from 'gabsocial/utils/numbers'; -const formatNumber = num => num > 40 ? '40+' : num; +const IconWithBadge = ({ id, count, className }) => { + if (count < 1) return null; -const IconWithBadge = ({ id, count, className }) => ( - - {count > 0 && {formatNumber(count)}} - -); + return ( + + {count > 0 && {shortNumberFormat(count)}} + + ) +}; IconWithBadge.propTypes = { id: PropTypes.string.isRequired, @@ -16,4 +19,4 @@ IconWithBadge.propTypes = { className: PropTypes.string, }; -export default IconWithBadge; \ No newline at end of file +export default IconWithBadge; diff --git a/app/javascript/gabsocial/components/status.js b/app/javascript/gabsocial/components/status.js index f8d588a4..74c75fe7 100644 --- a/app/javascript/gabsocial/components/status.js +++ b/app/javascript/gabsocial/components/status.js @@ -179,7 +179,11 @@ class Status extends ImmutablePureComponent { } const { status } = this.props; - this.context.router.history.push(`/${status.getIn(['account', 'acct'])}/posts/${status.getIn(['reblog', 'id'], status.get('id'))}`); + + const isReblog = !!status.getIn(['reblog', 'id']); + const originalPosterUsername = isReblog ? status.getIn(['reblog', 'account', 'acct']) : status.getIn(['account', 'acct']); + + this.context.router.history.push(`/${originalPosterUsername}/posts/${status.getIn(['reblog', 'id'], status.get('id'))}`); } } diff --git a/app/javascript/gabsocial/features/reblogs/index.js b/app/javascript/gabsocial/features/reblogs/index.js index 34976469..bebd2812 100644 --- a/app/javascript/gabsocial/features/reblogs/index.js +++ b/app/javascript/gabsocial/features/reblogs/index.js @@ -4,16 +4,28 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; +import MissingIndicator from '../../components/missing_indicator'; import { fetchReblogs } from '../../actions/interactions'; +import { fetchStatus } from '../../actions/statuses'; import { FormattedMessage } from 'react-intl'; import AccountContainer from '../../containers/account_container'; import Column from '../ui/components/column'; import ColumnBackButton from '../../components/column_back_button'; import ScrollableList from '../../components/scrollable_list'; +import { makeGetStatus } from '../../selectors'; -const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), -}); +const mapStateToProps = (state, props) => { + const getStatus = makeGetStatus(); + const status = getStatus(state, { + id: props.params.statusId, + username: props.params.username + }); + + return { + status, + accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), + } +}; export default @connect(mapStateToProps) class Reblogs extends ImmutablePureComponent { @@ -22,20 +34,23 @@ class Reblogs extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + status: ImmutablePropTypes.map, }; componentWillMount () { this.props.dispatch(fetchReblogs(this.props.params.statusId)); + this.props.dispatch(fetchStatus(this.props.params.statusId)); } componentWillReceiveProps(nextProps) { if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { this.props.dispatch(fetchReblogs(nextProps.params.statusId)); + this.props.dispatch(fetchStatus(nextProps.params.statusId)); } } render () { - const { accountIds } = this.props; + const { accountIds, status } = this.props; if (!accountIds) { return ( @@ -45,6 +60,14 @@ class Reblogs extends ImmutablePureComponent { ); } + if (!status) { + return ( + + + + ); + } + const emptyMessage = ; return ( diff --git a/app/javascript/gabsocial/features/status/index.js b/app/javascript/gabsocial/features/status/index.js index 59244854..0e8c3e00 100644 --- a/app/javascript/gabsocial/features/status/index.js +++ b/app/javascript/gabsocial/features/status/index.js @@ -63,7 +63,11 @@ const makeMapStateToProps = () => { const getStatus = makeGetStatus(); const mapStateToProps = (state, props) => { - const status = getStatus(state, { id: props.params.statusId }); + const status = getStatus(state, { + id: props.params.statusId, + username: props.params.username + }); + let ancestorsIds = Immutable.List(); let descendantsIds = Immutable.List(); diff --git a/app/javascript/gabsocial/selectors/index.js b/app/javascript/gabsocial/selectors/index.js index 70f08a8e..5ebd1e9c 100644 --- a/app/javascript/gabsocial/selectors/index.js +++ b/app/javascript/gabsocial/selectors/index.js @@ -70,14 +70,21 @@ export const makeGetStatus = () => { (state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]), (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]), (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]), + (state, { username }) => username, getFilters, ], - (statusBase, statusReblog, accountBase, accountReblog, filters) => { + (statusBase, statusReblog, accountBase, accountReblog, username, filters) => { if (!statusBase) { return null; } + const accountUsername = accountBase.get('acct'); + //Must be owner of status if username exists + if (accountUsername !== username && username !== undefined) { + return null; + } + if (statusReblog) { statusReblog = statusReblog.set('account', accountReblog); } else { diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index d838f16e..7e95367f 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -61,6 +61,10 @@ class AccountPolicy < ApplicationPolicy staff? end + def edit_pro? + staff? + end + def update_badges? staff? end diff --git a/app/views/admin/accounts/_edit_pro_fields.html.haml b/app/views/admin/accounts/_edit_pro_fields.html.haml new file mode 100644 index 00000000..a48a5980 --- /dev/null +++ b/app/views/admin/accounts/_edit_pro_fields.html.haml @@ -0,0 +1,7 @@ +.fields-row + .fields-row__column.fields-row__column-6.fields-group + %label{for: "is_pro"} + PRO + = f.check_box :is_pro, wrapper: :with_label, hint: false, id: "is_pro" + .fields-row__column.fields-row__column-6.fields-group + = f.input :pro_expires_at, as: :string, wrapper: :with_label, hint: false \ No newline at end of file diff --git a/app/views/admin/accounts/edit_pro.html.haml b/app/views/admin/accounts/edit_pro.html.haml new file mode 100644 index 00000000..66a0f17d --- /dev/null +++ b/app/views/admin/accounts/edit_pro.html.haml @@ -0,0 +1,8 @@ +- content_for :page_title do + = 'Edit PRO status of @' + @account.acct + += simple_form_for @account, url: save_pro_admin_account_path(@account.id), method: :put do |f| + = render 'edit_pro_fields', f: f + + .actions + = f.button :button, t('generic.save_changes'), type: :submit \ No newline at end of file diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index a45f7a6a..4e709352 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -134,6 +134,9 @@ - if @account.is_pro? =fa_icon 'check' %time.formatted{ datetime: @account.pro_expires_at.iso8601, title: l(@account.pro_expires_at) }= l @account.pro_expires_at + %td + - if @account.local? + = table_link_to '', t('admin.accounts.edit_pro'), edit_pro_admin_account_path(@account.id), class: 'button' if can?(:verify, @account) %tr %th= t('admin.accounts.is_verified') diff --git a/config/routes.rb b/config/routes.rb index 5966cc72..b1a24b99 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -201,6 +201,8 @@ Rails.application.routes.draw do post :remove_donor_badge post :add_investor_badge post :remove_investor_badge + get :edit_pro + put :save_pro end resource :change_email, only: [:show, :update]