diff --git a/.travis.yml b/.travis.yml index fe4549edd0..b1b0c2bcda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ env: - LOCAL_DOMAIN=cb6e6126.ngrok.io - LOCAL_HTTPS=true - RAILS_ENV=test - + - CXX=g++-4.8 addons: postgresql: 9.4 @@ -23,6 +23,10 @@ services: bundler_args: --without development production --retry=3 --jobs=3 +before_install: + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - sudo apt-get -qq update + - sudo apt-get -qq install g++-4.8 install: - nvm install $TRAVIS_NODE_VERSION - npm install -g npm@3 diff --git a/Gemfile b/Gemfile index 6fd86ec488..423560bb6d 100644 --- a/Gemfile +++ b/Gemfile @@ -50,7 +50,6 @@ gem 'pg_search' gem 'simple-navigation' gem 'statsd-instrument' gem 'ruby-oembed', require: 'oembed' -gem 'fcm' gem 'react-rails' gem 'browserify-rails' diff --git a/Gemfile.lock b/Gemfile.lock index dd3105b38a..4f54a621ca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -147,9 +147,6 @@ GEM execjs (2.7.0) fabrication (2.15.2) fast_blank (1.0.0) - fcm (0.0.2) - httparty - json font-awesome-rails (4.6.3.1) railties (>= 3.2, < 5.1) fuubar (2.1.1) @@ -183,8 +180,6 @@ GEM domain_name (~> 0.5) http-form_data (1.0.1) http_parser.rb (0.6.0) - httparty (0.14.0) - multi_xml (>= 0.5.2) httplog (0.3.2) colorize i18n (0.7.0) @@ -232,7 +227,6 @@ GEM mini_portile2 (2.1.0) minitest (5.10.1) multi_json (1.12.1) - multi_xml (0.6.0) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (4.0.1) @@ -470,7 +464,6 @@ DEPENDENCIES dotenv-rails fabrication fast_blank - fcm font-awesome-rails fuubar goldfinger diff --git a/app/assets/javascripts/components/actions/blocks.jsx b/app/assets/javascripts/components/actions/blocks.jsx new file mode 100644 index 0000000000..79e316497f --- /dev/null +++ b/app/assets/javascripts/components/actions/blocks.jsx @@ -0,0 +1,82 @@ +import api, { getLinks } from '../api' +import { fetchRelationships } from './accounts'; + +export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST'; +export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS'; +export const BLOCKS_FETCH_FAIL = 'BLOCKS_FETCH_FAIL'; + +export const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST'; +export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS'; +export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL'; + +export function fetchBlocks() { + return (dispatch, getState) => { + dispatch(fetchBlocksRequest()); + + api(getState).get('/api/v1/blocks').then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(fetchBlocksFail(error))); + }; +}; + +export function fetchBlocksRequest() { + return { + type: BLOCKS_FETCH_REQUEST + }; +}; + +export function fetchBlocksSuccess(accounts, next) { + return { + type: BLOCKS_FETCH_SUCCESS, + accounts, + next + }; +}; + +export function fetchBlocksFail(error) { + return { + type: BLOCKS_FETCH_FAIL, + error + }; +}; + +export function expandBlocks() { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'blocks', 'next']); + + if (url === null) { + return; + } + + dispatch(expandBlocksRequest()); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(expandBlocksSuccess(response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandBlocksFail(error))); + }; +}; + +export function expandBlocksRequest() { + return { + type: BLOCKS_EXPAND_REQUEST + }; +}; + +export function expandBlocksSuccess(accounts, next) { + return { + type: BLOCKS_EXPAND_SUCCESS, + accounts, + next + }; +}; + +export function expandBlocksFail(error) { + return { + type: BLOCKS_EXPAND_FAIL, + error + }; +}; diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 46a01b2005..3b36ce3ef5 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -33,6 +33,7 @@ import Notifications from '../features/notifications'; import FollowRequests from '../features/follow_requests'; import GenericNotFound from '../features/generic_not_found'; import FavouritedStatuses from '../features/favourited_statuses'; +import Blocks from '../features/blocks'; import { IntlProvider, addLocaleData } from 'react-intl'; import en from 'react-intl/locale-data/en'; import de from 'react-intl/locale-data/de'; @@ -124,6 +125,8 @@ const Mastodon = React.createClass({ + + diff --git a/app/assets/javascripts/components/features/blocks/index.jsx b/app/assets/javascripts/components/features/blocks/index.jsx new file mode 100644 index 0000000000..9bc226d616 --- /dev/null +++ b/app/assets/javascripts/components/features/blocks/index.jsx @@ -0,0 +1,68 @@ +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 ColumnBackButtonSlim from '../../components/column_back_button_slim'; +import AccountContainer from '../../containers/account_container'; +import { fetchBlocks, expandBlocks } from '../../actions/blocks'; +import { defineMessages, injectIntl } from 'react-intl'; + +const messages = defineMessages({ + heading: { id: 'column.blocks', defaultMessage: 'Blocked' } +}); + +const mapStateToProps = state => ({ + accountIds: state.getIn(['user_lists', 'blocks', 'items']) +}); + +const Blocks = 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(fetchBlocks()); + }, + + handleScroll (e) { + const { scrollTop, scrollHeight, clientHeight } = e.target; + + if (scrollTop === scrollHeight - clientHeight) { + this.props.dispatch(expandBlocks()); + } + }, + + render () { + const { intl, accountIds } = this.props; + + if (!accountIds) { + return ( + + + + ); + } + + return ( + + + +
+ {accountIds.map(id => + + )} +
+
+
+ ); + } +}); + +export default connect(mapStateToProps)(injectIntl(Blocks)); diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx index 409dfd663d..f3938cee1b 100644 --- a/app/assets/javascripts/components/reducers/accounts.jsx +++ b/app/assets/javascripts/components/reducers/accounts.jsx @@ -7,9 +7,14 @@ import { ACCOUNT_TIMELINE_FETCH_SUCCESS, ACCOUNT_TIMELINE_EXPAND_SUCCESS, FOLLOW_REQUESTS_FETCH_SUCCESS, + FOLLOW_REQUESTS_EXPAND_SUCCESS, ACCOUNT_FOLLOW_SUCCESS, ACCOUNT_UNFOLLOW_SUCCESS } from '../actions/accounts'; +import { + BLOCKS_FETCH_SUCCESS, + BLOCKS_EXPAND_SUCCESS +} from '../actions/blocks'; import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose'; import { REBLOG_SUCCESS, @@ -87,6 +92,9 @@ export default function accounts(state = initialState, action) { case COMPOSE_SUGGESTIONS_READY: case SEARCH_SUGGESTIONS_READY: case FOLLOW_REQUESTS_FETCH_SUCCESS: + case FOLLOW_REQUESTS_EXPAND_SUCCESS: + case BLOCKS_FETCH_SUCCESS: + case BLOCKS_EXPAND_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 72922f509f..8c9a3d3aab 100644 --- a/app/assets/javascripts/components/reducers/user_lists.jsx +++ b/app/assets/javascripts/components/reducers/user_lists.jsx @@ -4,6 +4,7 @@ import { FOLLOWING_FETCH_SUCCESS, FOLLOWING_EXPAND_SUCCESS, FOLLOW_REQUESTS_FETCH_SUCCESS, + FOLLOW_REQUESTS_EXPAND_SUCCESS, FOLLOW_REQUEST_AUTHORIZE_SUCCESS, FOLLOW_REQUEST_REJECT_SUCCESS } from '../actions/accounts'; @@ -11,6 +12,10 @@ import { REBLOGS_FETCH_SUCCESS, FAVOURITES_FETCH_SUCCESS } from '../actions/interactions'; +import { + BLOCKS_FETCH_SUCCESS, + BLOCKS_EXPAND_SUCCESS +} from '../actions/blocks'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -18,7 +23,8 @@ const initialState = Immutable.Map({ following: Immutable.Map(), reblogged_by: Immutable.Map(), favourited_by: Immutable.Map(), - follow_requests: Immutable.Map() + follow_requests: Immutable.Map(), + blocks: Immutable.Map() }); const normalizeList = (state, type, id, accounts, next) => { @@ -50,9 +56,15 @@ export default function userLists(state = initialState, action) { 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_REQUESTS_EXPAND_SUCCESS: + return state.updateIn(['follow_requests', 'items'], list => list.push(...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)); + case BLOCKS_FETCH_SUCCESS: + return state.setIn(['blocks', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); + case BLOCKS_EXPAND_SUCCESS: + return state.updateIn(['blocks', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); default: return state; } diff --git a/app/models/device.rb b/app/models/device.rb deleted file mode 100644 index 2782a7f383..0000000000 --- a/app/models/device.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class Device < ApplicationRecord - belongs_to :account - - validates :account, :registration_id, presence: true -end diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 0cc3cd6182..942cd9d21c 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -10,7 +10,6 @@ class NotifyService < BaseService create_notification send_email if email_enabled? - send_push_notification rescue ActiveRecord::RecordInvalid return end @@ -58,10 +57,6 @@ class NotifyService < BaseService NotificationMailer.send(@notification.type, @recipient, @notification).deliver_later end - def send_push_notification - PushNotificationWorker.perform_async(@notification.id) - end - def email_enabled? @recipient.user.settings.notification_emails[@notification.type] end diff --git a/app/services/send_push_notification_service.rb b/app/services/send_push_notification_service.rb deleted file mode 100644 index 526ae20cbc..0000000000 --- a/app/services/send_push_notification_service.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -class SendPushNotificationService < BaseService - def call(notification) - return if ENV['FCM_API_KEY'].blank? - - devices = Device.where(account: notification.account).pluck(:registration_id) - fcm = FCM.new(ENV['FCM_API_KEY']) - - response = fcm.send(devices, data: { notification_id: notification.id }, collapse_key: :notifications, priority: :high) - handle_response(response) - end - - private - - def handle_response(response) - update_canonical_ids(response[:canonical_ids]) if response[:canonical_ids] - remove_bad_ids(response[:not_registered_ids]) if response[:not_registered_ids] - end - - def update_canonical_ids(ids) - ids.each { |pair| Device.find_by(registration_id: pair[:old]).update(registration_id: pair[:new]) } - end - - def remove_bad_ids(bad_ids) - Device.where(registration_id: bad_ids).delete_all unless bad_ids.empty? - end -end diff --git a/db/migrate/20170205175257_remove_devices.rb b/db/migrate/20170205175257_remove_devices.rb new file mode 100644 index 0000000000..e96ffed4da --- /dev/null +++ b/db/migrate/20170205175257_remove_devices.rb @@ -0,0 +1,5 @@ +class RemoveDevices < ActiveRecord::Migration[5.0] + def change + drop_table :devices + end +end diff --git a/db/schema.rb b/db/schema.rb index 448a7b8610..28a578aa22 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170129000348) do +ActiveRecord::Schema.define(version: 20170205175257) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -54,15 +54,6 @@ ActiveRecord::Schema.define(version: 20170129000348) do t.index ["account_id", "target_account_id"], name: "index_blocks_on_account_id_and_target_account_id", unique: true, using: :btree end - create_table "devices", force: :cascade do |t| - t.integer "account_id", null: false - t.string "registration_id", default: "", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["account_id"], name: "index_devices_on_account_id", using: :btree - t.index ["registration_id"], name: "index_devices_on_registration_id", using: :btree - end - create_table "domain_blocks", force: :cascade do |t| t.string "domain", default: "", null: false t.datetime "created_at", null: false diff --git a/spec/fabricators/device_fabricator.rb b/spec/fabricators/device_fabricator.rb deleted file mode 100644 index 02b24e8b35..0000000000 --- a/spec/fabricators/device_fabricator.rb +++ /dev/null @@ -1,3 +0,0 @@ -Fabricator(:device) do - registration_id "12345678" -end diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index 6ec28f5d88..0db1634e94 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Formatter do end it 'contains a link' do - expect(subject).to match('google.com') + expect(subject).to match('google.com') end end diff --git a/spec/models/device_spec.rb b/spec/models/device_spec.rb deleted file mode 100644 index f56fbf9785..0000000000 --- a/spec/models/device_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe Device, type: :model do - -end