From 7376af90f79b1de0c4cdd294f3f4d1481eedf0d7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 26 Dec 2016 19:13:56 +0100 Subject: Don't show statuses to blocked users --- spec/controllers/api/v1/statuses_controller_spec.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb index ab918fe50..d9c73f952 100644 --- a/spec/controllers/api/v1/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/statuses_controller_spec.rb @@ -7,7 +7,6 @@ RSpec.describe Api::V1::StatusesController, type: :controller do let(:token) { double acceptable?: true, resource_owner_id: user.id } before do - stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {}) allow(controller).to receive(:doorkeeper_token) { token } end -- cgit From 004382e4d09f90e5ca824996c4b20e99599bf98f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 26 Dec 2016 19:30:45 +0100 Subject: Adding follow requests API --- .../api/v1/follow_requests_controller.rb | 29 ++++++++++++ app/helpers/api/v1/follow_requests_helper.rb | 2 + app/models/follow_request.rb | 2 + app/views/api/v1/follow_requests/index.rabl | 2 + config/routes.rb | 7 +++ .../api/v1/follow_requests_controller_spec.rb | 52 ++++++++++++++++++++++ spec/helpers/api/oembed_helper_spec.rb | 12 +---- 7 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 app/controllers/api/v1/follow_requests_controller.rb create mode 100644 app/helpers/api/v1/follow_requests_helper.rb create mode 100644 app/views/api/v1/follow_requests/index.rabl create mode 100644 spec/controllers/api/v1/follow_requests_controller_spec.rb (limited to 'spec') diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb new file mode 100644 index 000000000..a30e97e71 --- /dev/null +++ b/app/controllers/api/v1/follow_requests_controller.rb @@ -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 diff --git a/app/helpers/api/v1/follow_requests_helper.rb b/app/helpers/api/v1/follow_requests_helper.rb new file mode 100644 index 000000000..b36faf2a3 --- /dev/null +++ b/app/helpers/api/v1/follow_requests_helper.rb @@ -0,0 +1,2 @@ +module Api::V1::FollowRequestsHelper +end diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index 132316fb4..b46065d53 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class FollowRequest < ApplicationRecord + include Paginable + belongs_to :account belongs_to :target_account, class_name: 'Account' diff --git a/app/views/api/v1/follow_requests/index.rabl b/app/views/api/v1/follow_requests/index.rabl new file mode 100644 index 000000000..9f3b13a53 --- /dev/null +++ b/app/views/api/v1/follow_requests/index.rabl @@ -0,0 +1,2 @@ +collection @accounts +extends 'api/v1/accounts/show' diff --git a/config/routes.rb b/config/routes.rb index e8c8f619d..2e9b2a87c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -104,6 +104,13 @@ Rails.application.routes.draw do resources :media, only: [:create] resources :apps, only: [:create] + resources :follow_requests, only: [:index] do + member do + post :authorize + post :reject + end + end + resources :notifications, only: [:index] resources :accounts, only: [:show] do diff --git a/spec/controllers/api/v1/follow_requests_controller_spec.rb b/spec/controllers/api/v1/follow_requests_controller_spec.rb new file mode 100644 index 000000000..a90d2d290 --- /dev/null +++ b/spec/controllers/api/v1/follow_requests_controller_spec.rb @@ -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 diff --git a/spec/helpers/api/oembed_helper_spec.rb b/spec/helpers/api/oembed_helper_spec.rb index 4f64cb84f..a671e2d65 100644 --- a/spec/helpers/api/oembed_helper_spec.rb +++ b/spec/helpers/api/oembed_helper_spec.rb @@ -1,15 +1,5 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the Api::OembedHelper. For example: -# -# describe Api::OembedHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe Api::OembedHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + end -- cgit From 3689c119f0ad8d523ab8deb3c2c8ed0a9c84db6e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 26 Dec 2016 21:33:51 +0100 Subject: Replacing follow requests in the settings area with in-UI column --- .../javascripts/components/actions/accounts.jsx | 153 +++++++++++++++++++++ .../components/components/status_list.jsx | 6 +- .../javascripts/components/containers/mastodon.jsx | 3 + .../components/account_authorize.jsx | 61 ++++++++ .../containers/account_authorize_container.jsx | 26 ++++ .../components/features/follow_requests/index.jsx | 66 +++++++++ .../components/features/getting_started/index.jsx | 9 +- .../javascripts/components/reducers/accounts.jsx | 4 +- .../javascripts/components/reducers/user_lists.jsx | 13 +- app/controllers/follow_requests_controller.rb | 28 ---- app/helpers/follow_requests_helper.rb | 2 - app/views/follow_requests/index.html.haml | 16 --- app/views/settings/shared/_links.html.haml | 2 - config/locales/en.yml | 4 - config/routes.rb | 7 - .../controllers/follow_requests_controller_spec.rb | 16 --- spec/helpers/follow_requests_helper_spec.rb | 5 - 17 files changed, 334 insertions(+), 87 deletions(-) create mode 100644 app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx create mode 100644 app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx create mode 100644 app/assets/javascripts/components/features/follow_requests/index.jsx delete mode 100644 app/controllers/follow_requests_controller.rb delete mode 100644 app/helpers/follow_requests_helper.rb delete mode 100644 app/views/follow_requests/index.html.haml delete mode 100644 spec/controllers/follow_requests_controller_spec.rb delete mode 100644 spec/helpers/follow_requests_helper_spec.rb (limited to 'spec') diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 759435afe..8d28b051f 100644 --- a/app/assets/javascripts/components/actions/accounts.jsx +++ b/app/assets/javascripts/components/actions/accounts.jsx @@ -51,6 +51,22 @@ export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST'; export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'; export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL'; +export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST'; +export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS'; +export const FOLLOW_REQUESTS_FETCH_FAIL = 'FOLLOW_REQUESTS_FETCH_FAIL'; + +export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST'; +export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS'; +export const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL'; + +export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST'; +export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS'; +export const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL'; + +export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; +export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; +export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL'; + export function setAccountSelf(account) { return { type: ACCOUNT_SET_SELF, @@ -509,3 +525,140 @@ export function fetchRelationshipsFail(error) { error }; }; + +export function fetchFollowRequests() { + return (dispatch, getState) => { + dispatch(fetchFollowRequestsRequest()); + + api(getState).get('/api/v1/follow_requests').then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)) + }).catch(error => dispatch(fetchFollowRequestsFail(error))); + }; +}; + +export function fetchFollowRequestsRequest() { + return { + type: FOLLOW_REQUESTS_FETCH_REQUEST + }; +}; + +export function fetchFollowRequestsSuccess(accounts, next) { + return { + type: FOLLOW_REQUESTS_FETCH_SUCCESS, + accounts, + next + }; +}; + +export function fetchFollowRequestsFail(error) { + return { + type: FOLLOW_REQUESTS_FETCH_FAIL, + error + }; +}; + +export function expandFollowRequests() { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'follow_requests', 'next']); + + if (url === null) { + return; + } + + dispatch(expandFollowRequestsRequest()); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)) + }).catch(error => dispatch(expandFollowRequestsFail(error))); + }; +}; + +export function expandFollowRequestsRequest() { + return { + type: FOLLOW_REQUESTS_EXPAND_REQUEST + }; +}; + +export function expandFollowRequestsSuccess(accounts, next) { + return { + type: FOLLOW_REQUESTS_EXPAND_SUCCESS, + accounts, + next + }; +}; + +export function expandFollowRequestsFail(error) { + return { + type: FOLLOW_REQUESTS_EXPAND_FAIL, + error + }; +}; + +export function authorizeFollowRequest(id) { + return (dispatch, getState) => { + dispatch(authorizeFollowRequestRequest(id)); + + api(getState) + .post(`/api/v1/follow_requests/${id}/authorize`) + .then(response => dispatch(authorizeFollowRequestSuccess(id))) + .catch(error => dispatch(authorizeFollowRequestFail(id, error))); + }; +}; + +export function authorizeFollowRequestRequest(id) { + return { + type: FOLLOW_REQUEST_AUTHORIZE_REQUEST, + id + }; +}; + +export function authorizeFollowRequestSuccess(id) { + return { + type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, + id + }; +}; + +export function authorizeFollowRequestFail(id, error) { + return { + type: FOLLOW_REQUEST_AUTHORIZE_FAIL, + id, + error + }; +}; + + +export function rejectFollowRequest(id) { + return (dispatch, getState) => { + dispatch(rejectFollowRequestRequest(id)); + + api(getState) + .post(`/api/v1/follow_requests/${id}/reject`) + .then(response => dispatch(rejectFollowRequestSuccess(id))) + .catch(error => dispatch(rejectFollowRequestFail(id, error))); + }; +}; + +export function rejectFollowRequestRequest(id) { + return { + type: FOLLOW_REQUEST_REJECT_REQUEST, + id + }; +}; + +export function rejectFollowRequestSuccess(id) { + return { + type: FOLLOW_REQUEST_REJECT_SUCCESS, + id + }; +}; + +export function rejectFollowRequestFail(id, error) { + return { + type: FOLLOW_REQUEST_REJECT_FAIL, + id, + error + }; +}; diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx index b48d94405..e0a73435f 100644 --- a/app/assets/javascripts/components/components/status_list.jsx +++ b/app/assets/javascripts/components/components/status_list.jsx @@ -27,11 +27,11 @@ const StatusList = React.createClass({ this._oldScrollPosition = scrollHeight - scrollTop; - if (scrollTop === scrollHeight - clientHeight) { + if (scrollTop === scrollHeight - clientHeight && this.props.onScrollToBottom) { this.props.onScrollToBottom(); - } else if (scrollTop < 100) { + } else if (scrollTop < 100 && this.props.onScrollToTop) { this.props.onScrollToTop(); - } else { + } else if (this.props.onScroll) { this.props.onScroll(); } }, diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 69fe2d07f..670455376 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -34,6 +34,7 @@ import Reblogs from '../features/reblogs'; import Favourites from '../features/favourites'; import HashtagTimeline from '../features/hashtag_timeline'; import Notifications from '../features/notifications'; +import FollowRequests from '../features/follow_requests'; import { IntlProvider, addLocaleData } from 'react-intl'; import en from 'react-intl/locale-data/en'; import de from 'react-intl/locale-data/de'; @@ -125,6 +126,8 @@ const Mastodon = React.createClass({ + + diff --git a/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx b/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx new file mode 100644 index 000000000..c16488aea --- /dev/null +++ b/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx @@ -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 ( +
+
+ +
+ +
+ +
+
+ +
+
+
+
+
+ ) +}; + +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); diff --git a/app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx b/app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx new file mode 100644 index 000000000..da1e5eaa1 --- /dev/null +++ b/app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx @@ -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); diff --git a/app/assets/javascripts/components/features/follow_requests/index.jsx b/app/assets/javascripts/components/features/follow_requests/index.jsx new file mode 100644 index 000000000..461370999 --- /dev/null +++ b/app/assets/javascripts/components/features/follow_requests/index.jsx @@ -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 ( + + + + ); + } + + return ( + + +
+ {accountIds.map(id => + + )} +
+
+
+ ); + } +}); + +export default connect(mapStateToProps)(injectIntl(FollowRequests)); diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index bff75f86f..7c2491954 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -7,7 +7,8 @@ import { connect } from 'react-redux'; const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, - settings: { id: 'navigation_bar.settings', defaultMessage: 'Settings' } + settings: { id: 'navigation_bar.settings', defaultMessage: 'Settings' }, + follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' } }); const mapStateToProps = state => ({ @@ -32,6 +33,7 @@ const GettingStarted = ({ intl, me }) => {
+
@@ -43,4 +45,9 @@ const GettingStarted = ({ intl, me }) => { ); }; +GettingStarted.propTypes = { + intl: React.PropTypes.object.isRequired, + me: React.PropTypes.number +}; + export default connect(mapStateToProps)(injectIntl(GettingStarted)); diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx index 982e63073..7f2f89d0a 100644 --- a/app/assets/javascripts/components/reducers/accounts.jsx +++ b/app/assets/javascripts/components/reducers/accounts.jsx @@ -6,7 +6,8 @@ import { FOLLOWING_FETCH_SUCCESS, FOLLOWING_EXPAND_SUCCESS, ACCOUNT_TIMELINE_FETCH_SUCCESS, - ACCOUNT_TIMELINE_EXPAND_SUCCESS + ACCOUNT_TIMELINE_EXPAND_SUCCESS, + FOLLOW_REQUESTS_FETCH_SUCCESS } from '../actions/accounts'; import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose'; import { @@ -78,6 +79,7 @@ export default function accounts(state = initialState, action) { case FAVOURITES_FETCH_SUCCESS: case COMPOSE_SUGGESTIONS_READY: case SEARCH_SUGGESTIONS_READY: + case FOLLOW_REQUESTS_FETCH_SUCCESS: return normalizeAccounts(state, action.accounts); case NOTIFICATIONS_REFRESH_SUCCESS: case NOTIFICATIONS_EXPAND_SUCCESS: diff --git a/app/assets/javascripts/components/reducers/user_lists.jsx b/app/assets/javascripts/components/reducers/user_lists.jsx index 3608e4209..36093663f 100644 --- a/app/assets/javascripts/components/reducers/user_lists.jsx +++ b/app/assets/javascripts/components/reducers/user_lists.jsx @@ -2,7 +2,10 @@ import { FOLLOWERS_FETCH_SUCCESS, FOLLOWERS_EXPAND_SUCCESS, FOLLOWING_FETCH_SUCCESS, - FOLLOWING_EXPAND_SUCCESS + FOLLOWING_EXPAND_SUCCESS, + FOLLOW_REQUESTS_FETCH_SUCCESS, + FOLLOW_REQUEST_AUTHORIZE_SUCCESS, + FOLLOW_REQUEST_REJECT_SUCCESS } from '../actions/accounts'; import { REBLOGS_FETCH_SUCCESS, @@ -14,7 +17,8 @@ const initialState = Immutable.Map({ followers: Immutable.Map(), following: Immutable.Map(), reblogged_by: Immutable.Map(), - favourited_by: Immutable.Map() + favourited_by: Immutable.Map(), + follow_requests: Immutable.Map() }); const normalizeList = (state, type, id, accounts, next) => { @@ -44,6 +48,11 @@ export default function userLists(state = initialState, action) { return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id))); + case FOLLOW_REQUESTS_FETCH_SUCCESS: + return state.setIn(['follow_requests', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); + case FOLLOW_REQUEST_AUTHORIZE_SUCCESS: + case FOLLOW_REQUEST_REJECT_SUCCESS: + return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id)); default: return state; } diff --git a/app/controllers/follow_requests_controller.rb b/app/controllers/follow_requests_controller.rb deleted file mode 100644 index d4368f773..000000000 --- a/app/controllers/follow_requests_controller.rb +++ /dev/null @@ -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 diff --git a/app/helpers/follow_requests_helper.rb b/app/helpers/follow_requests_helper.rb deleted file mode 100644 index cfd350e53..000000000 --- a/app/helpers/follow_requests_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module FollowRequestsHelper -end diff --git a/app/views/follow_requests/index.html.haml b/app/views/follow_requests/index.html.haml deleted file mode 100644 index 8c83488de..000000000 --- a/app/views/follow_requests/index.html.haml +++ /dev/null @@ -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" diff --git a/app/views/settings/shared/_links.html.haml b/app/views/settings/shared/_links.html.haml index b6a0b1fc1..44f097950 100644 --- a/app/views/settings/shared/_links.html.haml +++ b/app/views/settings/shared/_links.html.haml @@ -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' diff --git a/config/locales/en.yml b/config/locales/en.yml index 4cf958517..e5916dd76 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -40,10 +40,6 @@ en: x_minutes: "%{count}m" x_months: "%{count}mo" x_seconds: "%{count}s" - follow_requests: - authorize: Authorize - reject: Reject - title: Follow requests generic: changes_saved_msg: Changes successfully saved! powered_by: powered by %{link} diff --git a/config/routes.rb b/config/routes.rb index 2e9b2a87c..985d6583d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,13 +48,6 @@ Rails.application.routes.draw do resources :media, only: [:show] resources :tags, only: [:show] - resources :follow_requests do - member do - post :authorize - post :reject - end - end - namespace :admin do resources :pubsubhubbub, only: [:index] resources :domain_blocks, only: [:index, :create] diff --git a/spec/controllers/follow_requests_controller_spec.rb b/spec/controllers/follow_requests_controller_spec.rb deleted file mode 100644 index 72f5fd9b9..000000000 --- a/spec/controllers/follow_requests_controller_spec.rb +++ /dev/null @@ -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 diff --git a/spec/helpers/follow_requests_helper_spec.rb b/spec/helpers/follow_requests_helper_spec.rb deleted file mode 100644 index e031cf402..000000000 --- a/spec/helpers/follow_requests_helper_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe FollowRequestsHelper, type: :helper do - -end -- cgit From d7dc84439c60069a0cb9eeca81dc61c297a8667b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 29 Dec 2016 16:54:54 +0100 Subject: Add ability to use remote follow function on other sites --- app/assets/stylesheets/accounts.scss | 58 ++++++++++++++++++++++ app/assets/stylesheets/application.scss | 11 ++-- app/assets/stylesheets/forms.scss | 2 +- app/controllers/authorize_follow_controller.rb | 24 +++++++++ app/helpers/authorize_follow_helper.rb | 2 + app/views/accounts/_grid_card.html.haml | 2 +- app/views/authorize_follow/error.html.haml | 3 ++ app/views/authorize_follow/new.html.haml | 21 ++++++++ app/views/oauth/authorizations/error.html.haml | 5 +- app/views/oauth/authorizations/new.html.haml | 39 ++++++++------- app/views/oauth/authorizations/show.html.haml | 5 +- app/views/xrd/webfinger.json.rabl | 3 +- app/views/xrd/webfinger.xml.ruby | 1 + config/application.rb | 2 +- config/locales/en.yml | 5 ++ config/routes.rb | 4 ++ .../authorize_follow_controller_spec.rb | 6 +++ spec/helpers/authorize_follow_helper_spec.rb | 5 ++ 18 files changed, 166 insertions(+), 32 deletions(-) create mode 100644 app/controllers/authorize_follow_controller.rb create mode 100644 app/helpers/authorize_follow_helper.rb create mode 100644 app/views/authorize_follow/error.html.haml create mode 100644 app/views/authorize_follow/new.html.haml create mode 100644 spec/controllers/authorize_follow_controller_spec.rb create mode 100644 spec/helpers/authorize_follow_helper_spec.rb (limited to 'spec') diff --git a/app/assets/stylesheets/accounts.scss b/app/assets/stylesheets/accounts.scss index 7f33f178d..5d0963307 100644 --- a/app/assets/stylesheets/accounts.scss +++ b/app/assets/stylesheets/accounts.scss @@ -324,3 +324,61 @@ padding-bottom: 25px; cursor: default; } + +.account-card { + padding: 14px 10px; + background: #fff; + border-radius: 4px; + text-align: left; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); + + .detailed-status__display-name { + display: block; + overflow: hidden; + margin-bottom: 15px; + + & > div { + float: left; + margin-right: 10px; + width: 48px; + height: 48px; + } + + .avatar { + display: block; + border-radius: 4px; + } + + .display-name { + display: block; + max-width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + cursor: default; + + strong { + font-weight: 500; + color: #282c37; + } + + span { + font-size: 14px; + color: #9baec8; + } + } + + &:hover { + .display-name { + strong { + text-decoration: none; + } + } + } + } + + .account__header__content { + font-size: 14px; + color: #282c37; + } +} diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index d05ca3795..e4c550b81 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -214,11 +214,13 @@ body { .footer { text-align: center; margin-top: 30px; + font-size: 12px; + color: darken(#d9e1e8, 25%); .domain { - font-size: 12px; - font-weight: 400; - font-family: 'Roboto Mono', monospace; + //font-size: 12px; + font-weight: 500; + //font-family: 'Roboto Mono', monospace; a { color: inherit; @@ -227,13 +229,12 @@ body { } .powered-by { - font-size: 12px; font-weight: 400; - color: darken(#d9e1e8, 25%); a { color: inherit; text-decoration: underline; + font-weight: 500; &:hover { text-decoration: none; diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index cf9b4fba6..e6d2e85a2 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -185,7 +185,7 @@ code { } } -.oauth-prompt { +.oauth-prompt, .follow-prompt { margin-bottom: 30px; text-align: center; color: #9baec8; diff --git a/app/controllers/authorize_follow_controller.rb b/app/controllers/authorize_follow_controller.rb new file mode 100644 index 000000000..a276250a4 --- /dev/null +++ b/app/controllers/authorize_follow_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class AuthorizeFollowController < ApplicationController + layout 'public' + + before_action :authenticate_user! + + def new + @account = FollowRemoteAccountService.new.call(params[:acct]) + render :error if @account.nil? + end + + def create + @account = FollowService.new.call(current_account, params[:acct]).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 +end diff --git a/app/helpers/authorize_follow_helper.rb b/app/helpers/authorize_follow_helper.rb new file mode 100644 index 000000000..43659ccfa --- /dev/null +++ b/app/helpers/authorize_follow_helper.rb @@ -0,0 +1,2 @@ +module AuthorizeFollowHelper +end diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml index dfd7a9f5e..dfdb23161 100644 --- a/app/views/accounts/_grid_card.html.haml +++ b/app/views/accounts/_grid_card.html.haml @@ -1,6 +1,6 @@ .account-grid-card .account-grid-card__header - .avatar= image_tag account.avatar.url( :original) + .avatar= image_tag account.avatar.url(:original) .name = link_to TagManager.instance.url_for(account) do %span.display_name= display_name(account) diff --git a/app/views/authorize_follow/error.html.haml b/app/views/authorize_follow/error.html.haml new file mode 100644 index 000000000..88d33b68d --- /dev/null +++ b/app/views/authorize_follow/error.html.haml @@ -0,0 +1,3 @@ +.form-container + .flash-message#error_explanation + = t('authorize_follow.error') diff --git a/app/views/authorize_follow/new.html.haml b/app/views/authorize_follow/new.html.haml new file mode 100644 index 000000000..7368b834a --- /dev/null +++ b/app/views/authorize_follow/new.html.haml @@ -0,0 +1,21 @@ +- 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) + + .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}" + + .account__header__content= Formatter.instance.simplified_format(@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 diff --git a/app/views/oauth/authorizations/error.html.haml b/app/views/oauth/authorizations/error.html.haml index ee72d9740..408ca2b86 100644 --- a/app/views/oauth/authorizations/error.html.haml +++ b/app/views/oauth/authorizations/error.html.haml @@ -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] diff --git a/app/views/oauth/authorizations/new.html.haml b/app/views/oauth/authorizations/new.html.haml index f058e2cce..1f951c272 100644 --- a/app/views/oauth/authorizations/new.html.haml +++ b/app/views/oauth/authorizations/new.html.haml @@ -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| "#{s}"}.to_sentence.html_safe + %p + = t('doorkeeper.authorizations.new.able_to') + = @pre_auth.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.map { |s| "#{s}"}.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' diff --git a/app/views/oauth/authorizations/show.html.haml b/app/views/oauth/authorizations/show.html.haml index 897a15cee..b56667f35 100644 --- a/app/views/oauth/authorizations/show.html.haml +++ b/app/views/oauth/authorizations/show.html.haml @@ -1,2 +1,3 @@ -.flash-message - %code= params[:code] +.form-container + .flash-message + %code= params[:code] diff --git a/app/views/xrd/webfinger.json.rabl b/app/views/xrd/webfinger.json.rabl index 0de17ac19..e637ed9d3 100644 --- a/app/views/xrd/webfinger.json.rabl +++ b/app/views/xrd/webfinger.json.rabl @@ -11,6 +11,7 @@ node(:links) do { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account) }, { rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }, { rel: 'salmon', href: api_salmon_url(@account.id) }, - { rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}" } + { rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}" }, + { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_follow_url}?acct={uri}" }, ] end diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby index ee5b5fc9d..80ac71d27 100644 --- a/app/views/xrd/webfinger.xml.ruby +++ b/app/views/xrd/webfinger.xml.ruby @@ -6,5 +6,6 @@ Nokogiri::XML::Builder.new do |xml| xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom')) xml.Link(rel: 'salmon', href: api_salmon_url(@account.id)) xml.Link(rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}") + xml.Link(rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_follow_url}?acct={uri}") end end.to_xml diff --git a/config/application.rb b/config/application.rb index 091f9c535..79ace8521 100644 --- a/config/application.rb +++ b/config/application.rb @@ -45,7 +45,7 @@ module Mastodon config.browserify_rails.commandline_options = '--transform [ babelify --presets [ es2015 react ] ] --extension=".jsx"' config.to_prepare do - Doorkeeper::AuthorizationsController.layout 'auth' + Doorkeeper::AuthorizationsController.layout 'public' end config.action_dispatch.default_headers = { diff --git a/config/locales/en.yml b/config/locales/en.yml index 760078862..f57c72026 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -26,6 +26,11 @@ en: resend_confirmation: Resend confirmation instructions reset_password: Reset password set_new_password: Set new password + authorize_follow: + error: Unfortunately, there was an error looking up the remote account + follow: Follow + prompt_html: 'You (%{self}) have requested to follow:' + title: Follow %{acct} datetime: distance_in_words: about_x_hours: "%{count}h" diff --git a/config/routes.rb b/config/routes.rb index 985d6583d..1468d426b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,6 +48,10 @@ Rails.application.routes.draw do resources :media, only: [:show] resources :tags, only: [:show] + # Remote follow + get :authorize_follow, to: 'authorize_follow#new' + post :authorize_follow, to: 'authorize_follow#create' + namespace :admin do resources :pubsubhubbub, only: [:index] resources :domain_blocks, only: [:index, :create] diff --git a/spec/controllers/authorize_follow_controller_spec.rb b/spec/controllers/authorize_follow_controller_spec.rb new file mode 100644 index 000000000..954efd53e --- /dev/null +++ b/spec/controllers/authorize_follow_controller_spec.rb @@ -0,0 +1,6 @@ +require 'rails_helper' + +RSpec.describe AuthorizeFollowController, type: :controller do + describe 'GET #new' + describe 'POST #create' +end diff --git a/spec/helpers/authorize_follow_helper_spec.rb b/spec/helpers/authorize_follow_helper_spec.rb new file mode 100644 index 000000000..ba5b0a70b --- /dev/null +++ b/spec/helpers/authorize_follow_helper_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AuthorizeFollowHelper, type: :helper do + +end -- cgit From 49834a6e7f06301a5bcc17b8771ed4513622b6a8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 29 Dec 2016 20:12:32 +0100 Subject: Add API for retrieving blocked accounts --- .rubocop.yml | 1 + app/controllers/api/v1/blocks_controller.rb | 21 +++++++++++++++++++++ app/helpers/api/oembed_helper.rb | 2 -- app/helpers/api/v1/follow_requests_helper.rb | 2 -- app/helpers/authorize_follow_helper.rb | 2 ++ app/helpers/stream_entries_helper.rb | 2 +- app/models/block.rb | 1 + app/views/api/v1/blocks/index.rabl | 2 ++ app/workers/processing_worker.rb | 2 +- app/workers/salmon_worker.rb | 2 +- config/routes.rb | 1 + spec/controllers/api/v1/accounts_controller_spec.rb | 1 - spec/controllers/api/v1/blocks_controller_spec.rb | 19 +++++++++++++++++++ .../controllers/api/v1/timelines_controller_spec.rb | 1 - spec/helpers/api/oembed_helper_spec.rb | 5 ----- 15 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 app/controllers/api/v1/blocks_controller.rb delete mode 100644 app/helpers/api/oembed_helper.rb delete mode 100644 app/helpers/api/v1/follow_requests_helper.rb create mode 100644 app/views/api/v1/blocks/index.rabl create mode 100644 spec/controllers/api/v1/blocks_controller_spec.rb delete mode 100644 spec/helpers/api/oembed_helper_spec.rb (limited to 'spec') diff --git a/.rubocop.yml b/.rubocop.yml index b973f01cd..28c735913 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -86,3 +86,4 @@ AllCops: - 'config/**/*' - 'bin/*' - 'Rakefile' + - 'node_modules/**/*' diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb new file mode 100644 index 000000000..8629242ab --- /dev/null +++ b/app/controllers/api/v1/blocks_controller.rb @@ -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 diff --git a/app/helpers/api/oembed_helper.rb b/app/helpers/api/oembed_helper.rb deleted file mode 100644 index 05d5ca216..000000000 --- a/app/helpers/api/oembed_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module Api::OembedHelper -end diff --git a/app/helpers/api/v1/follow_requests_helper.rb b/app/helpers/api/v1/follow_requests_helper.rb deleted file mode 100644 index b36faf2a3..000000000 --- a/app/helpers/api/v1/follow_requests_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module Api::V1::FollowRequestsHelper -end diff --git a/app/helpers/authorize_follow_helper.rb b/app/helpers/authorize_follow_helper.rb index 43659ccfa..99ee03c2f 100644 --- a/app/helpers/authorize_follow_helper.rb +++ b/app/helpers/authorize_follow_helper.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + module AuthorizeFollowHelper end diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 5cd65008e..ae2f575b5 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -10,7 +10,7 @@ module StreamEntriesHelper end def avatar_for_status_url(status) - status.reblog? ? status.reblog.account.avatar.url( :original) : status.account.avatar.url( :original) + status.reblog? ? status.reblog.account.avatar.url(:original) : status.account.avatar.url(:original) end def entry_classes(status, is_predecessor, is_successor, include_threads) diff --git a/app/models/block.rb b/app/models/block.rb index ad225d180..c2067c5b8 100644 --- a/app/models/block.rb +++ b/app/models/block.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Block < ApplicationRecord + include Paginable include Streamable belongs_to :account diff --git a/app/views/api/v1/blocks/index.rabl b/app/views/api/v1/blocks/index.rabl new file mode 100644 index 000000000..9f3b13a53 --- /dev/null +++ b/app/views/api/v1/blocks/index.rabl @@ -0,0 +1,2 @@ +collection @accounts +extends 'api/v1/accounts/show' diff --git a/app/workers/processing_worker.rb b/app/workers/processing_worker.rb index b31cd0aaf..5df404bcc 100644 --- a/app/workers/processing_worker.rb +++ b/app/workers/processing_worker.rb @@ -2,7 +2,7 @@ class ProcessingWorker include Sidekiq::Worker - + sidekiq_options backtrace: true def perform(account_id, body) diff --git a/app/workers/salmon_worker.rb b/app/workers/salmon_worker.rb index 0903ca487..fc95ce47f 100644 --- a/app/workers/salmon_worker.rb +++ b/app/workers/salmon_worker.rb @@ -2,7 +2,7 @@ class SalmonWorker include Sidekiq::Worker - + sidekiq_options backtrace: true def perform(account_id, body) diff --git a/config/routes.rb b/config/routes.rb index 1468d426b..7a1d38ad2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -100,6 +100,7 @@ Rails.application.routes.draw do resources :follows, only: [:create] resources :media, only: [:create] resources :apps, only: [:create] + resources :blocks, only: [:index] resources :follow_requests, only: [:index] do member do diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb index e4532305b..98b284f7a 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts_controller_spec.rb @@ -7,7 +7,6 @@ RSpec.describe Api::V1::AccountsController, type: :controller do let(:token) { double acceptable?: true, resource_owner_id: user.id } before do - stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {}) allow(controller).to receive(:doorkeeper_token) { token } end diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb new file mode 100644 index 000000000..ca20a2d17 --- /dev/null +++ b/spec/controllers/api/v1/blocks_controller_spec.rb @@ -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 diff --git a/spec/controllers/api/v1/timelines_controller_spec.rb b/spec/controllers/api/v1/timelines_controller_spec.rb index c94519ac5..5e9954baf 100644 --- a/spec/controllers/api/v1/timelines_controller_spec.rb +++ b/spec/controllers/api/v1/timelines_controller_spec.rb @@ -6,7 +6,6 @@ RSpec.describe Api::V1::TimelinesController, type: :controller do let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } before do - stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {}) allow(controller).to receive(:doorkeeper_token) { token } end diff --git a/spec/helpers/api/oembed_helper_spec.rb b/spec/helpers/api/oembed_helper_spec.rb deleted file mode 100644 index a671e2d65..000000000 --- a/spec/helpers/api/oembed_helper_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe Api::OembedHelper, type: :helper do - -end -- cgit From a302e56f9a06b67cdf2184a278d99f9e65a67fca Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 29 Dec 2016 20:33:26 +0100 Subject: Add API for retrieving favourites --- app/controllers/api/v1/favourites_controller.rb | 21 +++++++++++++++++++++ app/services/favourite_service.rb | 4 +++- app/views/api/v1/favourites/index.rabl | 2 ++ config/routes.rb | 1 + .../api/v1/favourites_controller_spec.rb | 19 +++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/v1/favourites_controller.rb create mode 100644 app/views/api/v1/favourites/index.rabl create mode 100644 spec/controllers/api/v1/favourites_controller_spec.rb (limited to 'spec') diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb new file mode 100644 index 000000000..a71592acd --- /dev/null +++ b/app/controllers/api/v1/favourites_controller.rb @@ -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 diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index 5c04cfee4..d5fbd29e9 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -6,12 +6,14 @@ class FavouriteService < BaseService # @param [Status] status # @return [Favourite] def call(account, status) + raise Mastodon::NotPermitted unless status.permitted?(account) + favourite = Favourite.create!(account: account, status: status) Pubsubhubbub::DistributionWorker.perform_async(favourite.stream_entry.id) if status.local? - NotifyService.new.call(status.account, favourite) + NotifyService.new.call(favourite.status.account, favourite) else NotificationWorker.perform_async(favourite.stream_entry.id, status.account_id) end diff --git a/app/views/api/v1/favourites/index.rabl b/app/views/api/v1/favourites/index.rabl new file mode 100644 index 000000000..44d29d91b --- /dev/null +++ b/app/views/api/v1/favourites/index.rabl @@ -0,0 +1,2 @@ +collection @statuses +extends 'api/v1/statuses/show' diff --git a/config/routes.rb b/config/routes.rb index 7a1d38ad2..842fbe71e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -110,6 +110,7 @@ Rails.application.routes.draw do end resources :notifications, only: [:index] + resources :favourites, only: [:index] resources :accounts, only: [:show] do collection do diff --git a/spec/controllers/api/v1/favourites_controller_spec.rb b/spec/controllers/api/v1/favourites_controller_spec.rb new file mode 100644 index 000000000..a6e9963e5 --- /dev/null +++ b/spec/controllers/api/v1/favourites_controller_spec.rb @@ -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 -- cgit