mirror of https://github.com/mastodon/mastodon
commit
de154dbd5d
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 244 KiB |
@ -0,0 +1,61 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Permalink from '../../../components/permalink';
|
||||
import Avatar from '../../../components/avatar';
|
||||
import DisplayName from '../../../components/display_name';
|
||||
import emojify from '../../../emoji';
|
||||
import IconButton from '../../../components/icon_button';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
||||
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
|
||||
});
|
||||
|
||||
const outerStyle = {
|
||||
padding: '14px 10px'
|
||||
};
|
||||
|
||||
const panelStyle = {
|
||||
background: '#2f3441',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderTop: '1px solid #363c4b',
|
||||
borderBottom: '1px solid #363c4b',
|
||||
padding: '10px 0'
|
||||
};
|
||||
|
||||
const btnStyle = {
|
||||
flex: '1 1 auto',
|
||||
textAlign: 'center'
|
||||
};
|
||||
|
||||
const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
|
||||
const content = { __html: emojify(account.get('note')) };
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={outerStyle}>
|
||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}>
|
||||
<div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={48} /></div>
|
||||
<DisplayName account={account} />
|
||||
</Permalink>
|
||||
|
||||
<div style={{ color: '#616b86', fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} />
|
||||
</div>
|
||||
|
||||
<div style={panelStyle}>
|
||||
<div style={btnStyle}><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
|
||||
<div style={btnStyle}><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
AccountAuthorize.propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
onAuthorize: React.PropTypes.func.isRequired,
|
||||
onReject: React.PropTypes.func.isRequired,
|
||||
intl: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(AccountAuthorize);
|
@ -0,0 +1,26 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { makeGetAccount } from '../../../selectors';
|
||||
import AccountAuthorize from '../components/account_authorize';
|
||||
import { authorizeFollowRequest, rejectFollowRequest } from '../../../actions/accounts';
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
account: getAccount(state, props.id)
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { id }) => ({
|
||||
onAuthorize (account) {
|
||||
dispatch(authorizeFollowRequest(id));
|
||||
},
|
||||
|
||||
onReject (account) {
|
||||
dispatch(rejectFollowRequest(id));
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(AccountAuthorize);
|
@ -0,0 +1,66 @@
|
||||
import { connect } from 'react-redux';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import { ScrollContainer } from 'react-router-scroll';
|
||||
import Column from '../ui/components/column';
|
||||
import AccountAuthorizeContainer from './containers/account_authorize_container';
|
||||
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' }
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'follow_requests', 'items'])
|
||||
});
|
||||
|
||||
const FollowRequests = React.createClass({
|
||||
propTypes: {
|
||||
params: React.PropTypes.object.isRequired,
|
||||
dispatch: React.PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
intl: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
componentWillMount () {
|
||||
this.props.dispatch(fetchFollowRequests());
|
||||
},
|
||||
|
||||
handleScroll (e) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
|
||||
if (scrollTop === scrollHeight - clientHeight) {
|
||||
this.props.dispatch(expandFollowRequests());
|
||||
}
|
||||
},
|
||||
|
||||
render () {
|
||||
const { intl, accountIds } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Column icon='users' heading={intl.formatMessage(messages.heading)}>
|
||||
<ScrollContainer scrollKey='follow_requests'>
|
||||
<div className='scrollable' onScroll={this.handleScroll}>
|
||||
{accountIds.map(id =>
|
||||
<AccountAuthorizeContainer key={id} id={id} />
|
||||
)}
|
||||
</div>
|
||||
</ScrollContainer>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(FollowRequests));
|
@ -0,0 +1,150 @@
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Toggle from 'react-toggle';
|
||||
import { Motion, spring } from 'react-motion';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const outerStyle = {
|
||||
background: '#373b4a',
|
||||
padding: '15px'
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
fontSize: '16px',
|
||||
padding: '15px',
|
||||
position: 'absolute',
|
||||
right: '0',
|
||||
top: '-48px',
|
||||
cursor: 'pointer'
|
||||
};
|
||||
|
||||
const labelStyle = {
|
||||
display: 'block',
|
||||
lineHeight: '24px',
|
||||
verticalAlign: 'middle'
|
||||
};
|
||||
|
||||
const labelSpanStyle = {
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
marginBottom: '14px',
|
||||
marginLeft: '8px',
|
||||
color: '#9baec8'
|
||||
};
|
||||
|
||||
const sectionStyle = {
|
||||
cursor: 'default',
|
||||
display: 'block',
|
||||
fontWeight: '500',
|
||||
color: '#9baec8',
|
||||
marginBottom: '10px'
|
||||
};
|
||||
|
||||
const rowStyle = {
|
||||
|
||||
};
|
||||
|
||||
const ColumnSettings = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getInitialState () {
|
||||
return {
|
||||
collapsed: true
|
||||
};
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
handleToggleCollapsed () {
|
||||
this.setState({ collapsed: !this.state.collapsed });
|
||||
},
|
||||
|
||||
handleChange (key, e) {
|
||||
this.props.onChange(key, e.target.checked);
|
||||
},
|
||||
|
||||
render () {
|
||||
const { settings } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
||||
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div style={{...iconStyle, color: collapsed ? '#9baec8' : '#fff', background: collapsed ? '#2f3441' : '#373b4a' }} onClick={this.handleToggleCollapsed}><i className='fa fa-sliders' /></div>
|
||||
|
||||
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : 458) }}>
|
||||
{({ opacity, height }) =>
|
||||
<div style={{ overflow: 'hidden', height: `${height}px`, opacity: opacity / 100 }}>
|
||||
<div style={outerStyle}>
|
||||
<span style={sectionStyle}><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
||||
|
||||
<div style={rowStyle}>
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['alerts', 'follow'])} onChange={this.handleChange.bind(this, ['alerts', 'follow'])} />
|
||||
<span style={labelSpanStyle}>{alertStr}</span>
|
||||
</label>
|
||||
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['shows', 'follow'])} onChange={this.handleChange.bind(this, ['shows', 'follow'])} />
|
||||
<span style={labelSpanStyle}>{showStr}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<span style={sectionStyle}><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
|
||||
|
||||
<div style={rowStyle}>
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['alerts', 'favourite'])} onChange={this.handleChange.bind(this, ['alerts', 'favourite'])} />
|
||||
<span style={labelSpanStyle}>{alertStr}</span>
|
||||
</label>
|
||||
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['shows', 'favourite'])} onChange={this.handleChange.bind(this, ['shows', 'favourite'])} />
|
||||
<span style={labelSpanStyle}>{showStr}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<span style={sectionStyle}><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
|
||||
|
||||
<div style={rowStyle}>
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['alerts', 'mention'])} onChange={this.handleChange.bind(this, ['alerts', 'mention'])} />
|
||||
<span style={labelSpanStyle}>{alertStr}</span>
|
||||
</label>
|
||||
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['shows', 'mention'])} onChange={this.handleChange.bind(this, ['shows', 'mention'])} />
|
||||
<span style={labelSpanStyle}>{showStr}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<span style={sectionStyle}><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
|
||||
|
||||
<div style={rowStyle}>
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['alerts', 'reblog'])} onChange={this.handleChange.bind(this, ['alerts', 'reblog'])} />
|
||||
<span style={labelSpanStyle}>{alertStr}</span>
|
||||
</label>
|
||||
|
||||
<label style={labelStyle}>
|
||||
<Toggle checked={settings.getIn(['shows', 'reblog'])} onChange={this.handleChange.bind(this, ['shows', 'reblog'])} />
|
||||
<span style={labelSpanStyle}>{showStr}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Motion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default ColumnSettings;
|
@ -0,0 +1,17 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ColumnSettings from '../components/column_settings';
|
||||
import { changeNotificationsSetting } from '../../../actions/notifications';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
settings: state.getIn(['notifications', 'settings'])
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onChange (key, checked) {
|
||||
dispatch(changeNotificationsSetting(key, checked));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
@ -0,0 +1,46 @@
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const outerStyle = {
|
||||
position: 'absolute',
|
||||
right: '0',
|
||||
top: '-48px',
|
||||
padding: '15px',
|
||||
fontSize: '16px',
|
||||
background: '#2f3441',
|
||||
flex: '0 0 auto',
|
||||
cursor: 'pointer',
|
||||
color: '#2b90d9'
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
display: 'inline-block',
|
||||
marginRight: '5px'
|
||||
};
|
||||
|
||||
const ColumnBackButton = React.createClass({
|
||||
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
handleClick () {
|
||||
this.context.router.push('/');
|
||||
},
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div style={outerStyle} onClick={this.handleClick} className='column-back-button'>
|
||||
<i className='fa fa-fw fa-chevron-left' style={iconStyle} />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default ColumnBackButton;
|
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::BlocksController < ApiController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
results = Block.where(account: current_account).paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |f| accounts[f.target_account_id] }
|
||||
|
||||
set_account_counters_maps(@accounts)
|
||||
|
||||
next_path = api_v1_blocks_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT
|
||||
prev_path = api_v1_blocks_url(since_id: results.first.id) unless results.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
end
|
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FavouritesController < ApiController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
results = Favourite.where(account: current_account).paginate_by_max_id(DEFAULT_STATUSES_LIMIT, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(Status.where(id: results.map(&:status_id)), Status)
|
||||
|
||||
set_maps(@statuses)
|
||||
set_counters_maps(@statuses)
|
||||
|
||||
next_path = api_v1_favourites_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT
|
||||
prev_path = api_v1_favourites_url(since_id: results.first.id) unless results.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
end
|
@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowRequestsController < ApiController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
|
||||
def index
|
||||
results = FollowRequest.where(target_account: current_account).paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |f| accounts[f.account_id] }
|
||||
|
||||
set_account_counters_maps(@accounts)
|
||||
|
||||
next_path = api_v1_follow_requests_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT
|
||||
prev_path = api_v1_follow_requests_url(since_id: results.first.id) unless results.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def authorize
|
||||
FollowRequest.find_by!(account_id: params[:id], target_account: current_account).authorize!
|
||||
render_empty
|
||||
end
|
||||
|
||||
def reject
|
||||
FollowRequest.find_by!(account_id: params[:id], target_account: current_account).reject!
|
||||
render_empty
|
||||
end
|
||||
end
|
@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AuthorizeFollowController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
def new
|
||||
uri = Addressable::URI.parse(acct_param)
|
||||
|
||||
if uri.path && %w(http https).include?(uri.scheme)
|
||||
set_account_from_url
|
||||
else
|
||||
set_account_from_acct
|
||||
end
|
||||
|
||||
render :error if @account.nil?
|
||||
end
|
||||
|
||||
def create
|
||||
@account = FollowService.new.call(current_account, acct_param).try(:target_account)
|
||||
|
||||
if @account.nil?
|
||||
render :error
|
||||
else
|
||||
redirect_to web_url("accounts/#{@account.id}")
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermitted
|
||||
render :error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account_from_url
|
||||
@account = FetchRemoteAccountService.new.call(acct_param)
|
||||
end
|
||||
|
||||
def set_account_from_acct
|
||||
@account = FollowRemoteAccountService.new.call(acct_param)
|
||||
end
|
||||
|
||||
def acct_param
|
||||
params[:acct].gsub(/\Aacct:/, '')
|
||||
end
|
||||
end
|
@ -1,28 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FollowRequestsController < ApplicationController
|
||||
layout 'auth'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_follow_request, except: :index
|
||||
|
||||
def index
|
||||
@follow_requests = FollowRequest.where(target_account: current_account)
|
||||
end
|
||||
|
||||
def authorize
|
||||
@follow_request.authorize!
|
||||
redirect_to follow_requests_path
|
||||
end
|
||||
|
||||
def reject
|
||||
@follow_request.reject!
|
||||
redirect_to follow_requests_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_follow_request
|
||||
@follow_request = FollowRequest.find(params[:id])
|
||||
end
|
||||
end
|
@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemoteFollowController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :set_account
|
||||
before_action :check_account_suspension
|
||||
|
||||
def new
|
||||
@remote_follow = RemoteFollow.new
|
||||
end
|
||||
|
||||
def create
|
||||
@remote_follow = RemoteFollow.new(resource_params)
|
||||
|
||||
if @remote_follow.valid?
|
||||
resource = Goldfinger.finger("acct:#{@remote_follow.acct}")
|
||||
redirect_url_link = resource&.link('http://ostatus.org/schema/1.0/subscribe')
|
||||
|
||||
if redirect_url_link.nil? || redirect_url_link.template.nil?
|
||||
@remote_follow.errors.add(:acct, I18n.t('remote_follow.missing_resource'))
|
||||
render(:new) && return
|
||||
end
|
||||
|
||||
redirect_to Addressable::Template.new(redirect_url_link.template).expand(uri: "#{@account.username}@#{Rails.configuration.x.local_domain}").to_s
|
||||
else
|
||||
render :new
|
||||
end
|
||||
rescue Goldfinger::Error
|
||||
@remote_follow.errors.add(:acct, I18n.t('remote_follow.missing_resource'))
|
||||
render :new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:remote_follow).permit(:acct)
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
head 410 if @account.suspended?
|
||||
end
|
||||
end
|
@ -1,2 +0,0 @@
|
||||
module Api::OembedHelper
|
||||
end
|
@ -0,0 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AuthorizeFollowHelper
|
||||
end
|
@ -1,2 +0,0 @@
|
||||
module FollowRequestsHelper
|
||||
end
|
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemoteFollow
|
||||
include ActiveModel::Validations
|
||||
|
||||
attr_accessor :acct
|
||||
|
||||
validates :acct, presence: true
|
||||
|
||||
def initialize(attrs = {})
|
||||
@acct = attrs[:acct]
|
||||
end
|
||||
end
|
@ -0,0 +1,2 @@
|
||||
collection @accounts
|
||||
extends 'api/v1/accounts/show'
|
@ -0,0 +1,2 @@
|
||||
collection @statuses
|
||||
extends 'api/v1/statuses/show'
|
@ -0,0 +1,2 @@
|
||||
collection @accounts
|
||||
extends 'api/v1/accounts/show'
|
@ -0,0 +1,11 @@
|
||||
.account-card
|
||||
.detailed-status__display-name
|
||||
%div
|
||||
= image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar'
|
||||
|
||||
%span.display-name
|
||||
%strong= display_name(account)
|
||||
%span= "@#{account.acct}"
|
||||
|
||||
- unless account.note.blank?
|
||||
.account__header__content= Formatter.instance.simplified_format(account)
|
@ -0,0 +1,3 @@
|
||||
.form-container
|
||||
.flash-message#error_explanation
|
||||
= t('authorize_follow.error')
|
@ -0,0 +1,12 @@
|
||||
- content_for :page_title do
|
||||
= t('authorize_follow.title', acct: @account.acct)
|
||||
|
||||
.form-container
|
||||
.follow-prompt
|
||||
%h2= t('authorize_follow.prompt_html', self: current_account.username)
|
||||
|
||||
= render partial: 'card', locals: { account: @account }
|
||||
|
||||
= form_tag authorize_follow_path, method: :post, class: 'simple_form' do
|
||||
= hidden_field_tag :acct, @account.acct
|
||||
= button_tag t('authorize_follow.follow'), type: :submit
|
@ -1,16 +0,0 @@
|
||||
- content_for :page_title do
|
||||
= t('follow_requests.title')
|
||||
|
||||
- if @follow_requests.empty?
|
||||
%p.nothing-here= t('accounts.nothing_here')
|
||||
- else
|
||||
%table.table
|
||||
%tbody
|
||||
- @follow_requests.each do |follow_request|
|
||||
%tr
|
||||
%td= link_to follow_request.account.acct, web_path("accounts/#{follow_request.account.id}")
|
||||
%td{ style: 'text-align: right' }
|
||||
= table_link_to 'check-circle', t('follow_requests.authorize'), authorize_follow_request_path(follow_request), method: :post
|
||||
= table_link_to 'times-circle', t('follow_requests.reject'), reject_follow_request_path(follow_request), method: :post
|
||||
|
||||
.form-footer= render "settings/shared/links"
|
@ -0,0 +1,5 @@
|
||||
<%= display_name(@me) %>,
|
||||
|
||||
<%= t('notification_mailer.follow_request.body', name: @account.acct) %>
|
||||
|
||||
<%= web_url("follow_requests") %>
|
@ -1,2 +1,3 @@
|
||||
.flash-message#error_explanation
|
||||
= @pre_auth.error_response.body[:error_description]
|
||||
.form-container
|
||||
.flash-message#error_explanation
|
||||
= @pre_auth.error_response.body[:error_description]
|
||||
|
@ -1,25 +1,26 @@
|
||||
- content_for :page_title do
|
||||
= t('doorkeeper.authorizations.new.title')
|
||||
|
||||
.oauth-prompt
|
||||
%h2= t('doorkeeper.authorizations.new.prompt', client_name: @pre_auth.client.name)
|
||||
.form-container
|
||||
.oauth-prompt
|
||||
%h2= t('doorkeeper.authorizations.new.prompt', client_name: @pre_auth.client.name)
|
||||
|
||||
%p
|
||||
= t('doorkeeper.authorizations.new.able_to')
|
||||
= @pre_auth.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.map { |s| "<strong>#{s}</strong>"}.to_sentence.html_safe
|
||||
%p
|
||||
= t('doorkeeper.authorizations.new.able_to')
|
||||
= @pre_auth.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.map { |s| "<strong>#{s}</strong>"}.to_sentence.html_safe
|
||||
|
||||
= form_tag oauth_authorization_path, method: :post, class: 'simple_form' do
|
||||
= hidden_field_tag :client_id, @pre_auth.client.uid
|
||||
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
|
||||
= hidden_field_tag :state, @pre_auth.state
|
||||
= hidden_field_tag :response_type, @pre_auth.response_type
|
||||
= hidden_field_tag :scope, @pre_auth.scope
|
||||
= button_tag t('doorkeeper.authorizations.buttons.authorize'), type: :submit
|
||||
= form_tag oauth_authorization_path, method: :post, class: 'simple_form' do
|
||||
= hidden_field_tag :client_id, @pre_auth.client.uid
|
||||
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
|
||||
= hidden_field_tag :state, @pre_auth.state
|
||||
= hidden_field_tag :response_type, @pre_auth.response_type
|
||||
= hidden_field_tag :scope, @pre_auth.scope
|
||||
= button_tag t('doorkeeper.authorizations.buttons.authorize'), type: :submit
|
||||
|
||||
= form_tag oauth_authorization_path, method: :delete, class: 'simple_form' do
|
||||
= hidden_field_tag :client_id, @pre_auth.client.uid
|
||||
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
|
||||
= hidden_field_tag :state, @pre_auth.state
|
||||
= hidden_field_tag :response_type, @pre_auth.response_type
|
||||
= hidden_field_tag :scope, @pre_auth.scope
|
||||
= button_tag t('doorkeeper.authorizations.buttons.deny'), type: :submit, class: 'negative'
|
||||
= form_tag oauth_authorization_path, method: :delete, class: 'simple_form' do
|
||||
= hidden_field_tag :client_id, @pre_auth.client.uid
|
||||
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
|
||||
= hidden_field_tag :state, @pre_auth.state
|
||||
= hidden_field_tag :response_type, @pre_auth.response_type
|
||||
= hidden_field_tag :scope, @pre_auth.scope
|
||||
= button_tag t('doorkeeper.authorizations.buttons.deny'), type: :submit, class: 'negative'
|
||||
|
@ -1,2 +1,3 @@
|
||||
.flash-message
|
||||
%code= params[:code]
|
||||
.form-container
|
||||
.flash-message
|
||||
%code= params[:code]
|
||||
|
@ -0,0 +1,13 @@
|
||||
.form-container
|
||||
.follow-prompt
|
||||
%h2= t('remote_follow.prompt')
|
||||
|
||||
= render partial: 'authorize_follow/card', locals: { account: @account }
|
||||
|
||||
= simple_form_for @remote_follow, as: :remote_follow, url: account_remote_follow_path(@account) do |f|
|
||||
= render 'shared/error_messages', object: @remote_follow
|
||||
|
||||
= f.input :acct, placeholder: t('remote_follow.acct')
|
||||
|
||||
.actions
|
||||
= f.button :button, t('remote_follow.proceed'), type: :submit
|
@ -0,0 +1,19 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::BlocksController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { double acceptable?: true, resource_owner_id: user.id }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::FavouritesController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { double acceptable?: true, resource_owner_id: user.id }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,52 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::FollowRequestsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice', locked: true)) }
|
||||
let(:token) { double acceptable?: true, resource_owner_id: user.id }
|
||||
let(:follower) { Fabricate(:account, username: 'bob') }
|
||||
|
||||
before do
|
||||
FollowService.new.call(follower, user.account.acct)
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
get :index
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #authorize' do
|
||||
before do
|
||||
post :authorize, params: { id: follower.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'allows follower to follow' do
|
||||
expect(follower.following?(user.account)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #reject' do
|
||||
before do
|
||||
post :reject, params: { id: follower.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'removes follow request' do
|
||||
expect(FollowRequest.where(target_account: user.account, account: follower).count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AuthorizeFollowController, type: :controller do
|
||||
describe 'GET #new'
|
||||
describe 'POST #create'
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FollowRequestsController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue