@ -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 |
@ -1,8 +1,6 @@ | |||
%ul.no-list | |||
- if controller_name != 'profiles' | |||
%li= link_to t('settings.edit_profile'), settings_profile_path | |||
- if controller_name != 'follow_requests' | |||
%li= link_to t('follow_requests.title'), follow_requests_path | |||
- if controller_name != 'preferences' | |||
%li= link_to t('settings.preferences'), settings_preferences_path | |||
- if controller_name != 'registrations' | |||
@ -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 |