From d6a64f45fd4530cfee4f7721f0c6e7ca28fe677f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 12 Sep 2016 19:20:55 +0200 Subject: [PATCH] Adding a notification stack for error messages --- .../components/actions/compose.jsx | 26 +++++++++--------- .../javascripts/components/actions/follow.jsx | 10 +++---- .../components/actions/interactions.jsx | 16 +++++------ .../javascripts/components/actions/meta.jsx | 2 +- .../components/actions/notifications.jsx | 8 ++++++ .../components/actions/timelines.jsx | 14 +++++----- .../components/components/frontend.jsx | 13 +++++---- .../containers/notifications_container.jsx | 25 +++++++++++++++++ .../components/reducers/compose.jsx | 2 +- .../components/reducers/follow.jsx | 2 +- .../javascripts/components/reducers/index.jsx | 4 ++- .../javascripts/components/reducers/meta.jsx | 2 +- .../components/reducers/notifications.jsx | 27 +++++++++++++++++++ .../components/reducers/timelines.jsx | 2 +- app/controllers/api/follows_controller.rb | 4 +++ app/services/remove_status_service.rb | 2 +- package.json | 1 + 17 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 app/assets/javascripts/components/actions/notifications.jsx create mode 100644 app/assets/javascripts/components/containers/notifications_container.jsx create mode 100644 app/assets/javascripts/components/reducers/notifications.jsx diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/assets/javascripts/components/actions/compose.jsx index a107b3352..48c442093 100644 --- a/app/assets/javascripts/components/actions/compose.jsx +++ b/app/assets/javascripts/components/actions/compose.jsx @@ -19,20 +19,20 @@ export function changeCompose(text) { type: COMPOSE_CHANGE, text: text }; -} +}; export function replyCompose(status) { return { type: COMPOSE_REPLY, status: status }; -} +}; export function cancelReplyCompose() { return { type: COMPOSE_REPLY_CANCEL }; -} +}; export function submitCompose() { return function (dispatch, getState) { @@ -48,27 +48,27 @@ export function submitCompose() { dispatch(submitComposeFail(error)); }); }; -} +}; export function submitComposeRequest() { return { type: COMPOSE_SUBMIT_REQUEST }; -} +}; export function submitComposeSuccess(status) { return { type: COMPOSE_SUBMIT_SUCCESS, status: status }; -} +}; export function submitComposeFail(error) { return { type: COMPOSE_SUBMIT_FAIL, error: error }; -} +}; export function uploadCompose(files) { return function (dispatch, getState) { @@ -87,13 +87,13 @@ export function uploadCompose(files) { dispatch(uploadComposeFail(error)); }); }; -} +}; export function uploadComposeRequest() { return { type: COMPOSE_UPLOAD_REQUEST }; -} +}; export function uploadComposeProgress(loaded, total) { return { @@ -101,25 +101,25 @@ export function uploadComposeProgress(loaded, total) { loaded: loaded, total: total }; -} +}; export function uploadComposeSuccess(media) { return { type: COMPOSE_UPLOAD_SUCCESS, media: media }; -} +}; export function uploadComposeFail(error) { return { type: COMPOSE_UPLOAD_FAIL, error: error }; -} +}; export function undoUploadCompose(media_id) { return { type: COMPOSE_UPLOAD_UNDO, media_id: media_id }; -} +}; diff --git a/app/assets/javascripts/components/actions/follow.jsx b/app/assets/javascripts/components/actions/follow.jsx index a09aded83..703ac0297 100644 --- a/app/assets/javascripts/components/actions/follow.jsx +++ b/app/assets/javascripts/components/actions/follow.jsx @@ -11,7 +11,7 @@ export function changeFollow(text) { type: FOLLOW_CHANGE, text: text }; -} +}; export function submitFollow() { return function (dispatch, getState) { @@ -25,24 +25,24 @@ export function submitFollow() { dispatch(submitFollowFail(error)); }); }; -} +}; export function submitFollowRequest() { return { type: FOLLOW_SUBMIT_REQUEST }; -} +}; export function submitFollowSuccess(account) { return { type: FOLLOW_SUBMIT_SUCCESS, account: account }; -} +}; export function submitFollowFail(error) { return { type: FOLLOW_SUBMIT_FAIL, error: error }; -} +}; diff --git a/app/assets/javascripts/components/actions/interactions.jsx b/app/assets/javascripts/components/actions/interactions.jsx index 964655530..84f5c4a4a 100644 --- a/app/assets/javascripts/components/actions/interactions.jsx +++ b/app/assets/javascripts/components/actions/interactions.jsx @@ -22,14 +22,14 @@ export function reblog(status) { dispatch(reblogFail(status, error)); }); }; -} +}; export function reblogRequest(status) { return { type: REBLOG_REQUEST, status: status }; -} +}; export function reblogSuccess(status, response) { return { @@ -37,7 +37,7 @@ export function reblogSuccess(status, response) { status: status, response: response }; -} +}; export function reblogFail(status, error) { return { @@ -45,7 +45,7 @@ export function reblogFail(status, error) { status: status, error: error }; -} +}; export function favourite(status) { return function (dispatch, getState) { @@ -57,14 +57,14 @@ export function favourite(status) { dispatch(favouriteFail(status, error)); }); }; -} +}; export function favouriteRequest(status) { return { type: FAVOURITE_REQUEST, status: status }; -} +}; export function favouriteSuccess(status, response) { return { @@ -72,7 +72,7 @@ export function favouriteSuccess(status, response) { status: status, response: response }; -} +}; export function favouriteFail(status, error) { return { @@ -80,4 +80,4 @@ export function favouriteFail(status, error) { status: status, error: error }; -} +}; diff --git a/app/assets/javascripts/components/actions/meta.jsx b/app/assets/javascripts/components/actions/meta.jsx index e0f127231..d0adbce3f 100644 --- a/app/assets/javascripts/components/actions/meta.jsx +++ b/app/assets/javascripts/components/actions/meta.jsx @@ -5,4 +5,4 @@ export function setAccessToken(token) { type: ACCESS_TOKEN_SET, token: token }; -} +}; diff --git a/app/assets/javascripts/components/actions/notifications.jsx b/app/assets/javascripts/components/actions/notifications.jsx new file mode 100644 index 000000000..cf1e50a69 --- /dev/null +++ b/app/assets/javascripts/components/actions/notifications.jsx @@ -0,0 +1,8 @@ +export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS'; + +export function dismissNotification(notification) { + return { + type: NOTIFICATION_DISMISS, + notification: notification + }; +}; diff --git a/app/assets/javascripts/components/actions/timelines.jsx b/app/assets/javascripts/components/actions/timelines.jsx index 383b727e9..05c12cc6d 100644 --- a/app/assets/javascripts/components/actions/timelines.jsx +++ b/app/assets/javascripts/components/actions/timelines.jsx @@ -14,7 +14,7 @@ export function setTimeline(timeline, statuses) { timeline: timeline, statuses: statuses }; -} +}; export function updateTimeline(timeline, status) { return { @@ -22,21 +22,21 @@ export function updateTimeline(timeline, status) { timeline: timeline, status: status }; -} +}; export function deleteFromTimelines(id) { return { type: TIMELINE_DELETE, id: id }; -} +}; export function refreshTimelineRequest(timeline) { return { type: TIMELINE_REFRESH_REQUEST, timeline: timeline }; -} +}; export function refreshTimeline(timeline) { return function (dispatch, getState) { @@ -48,13 +48,13 @@ export function refreshTimeline(timeline) { dispatch(refreshTimelineFail(timeline, error)); }); }; -} +}; export function refreshTimelineSuccess(timeline, statuses) { return function (dispatch) { dispatch(setTimeline(timeline, statuses)); }; -} +}; export function refreshTimelineFail(timeline, error) { return { @@ -62,4 +62,4 @@ export function refreshTimelineFail(timeline, error) { timeline: timeline, error: error }; -} +}; diff --git a/app/assets/javascripts/components/components/frontend.jsx b/app/assets/javascripts/components/components/frontend.jsx index 8774d2506..9d5166a08 100644 --- a/app/assets/javascripts/components/components/frontend.jsx +++ b/app/assets/javascripts/components/components/frontend.jsx @@ -1,11 +1,12 @@ import ColumnsArea from './columns_area'; import Column from './column'; import Drawer from './drawer'; -import ComposeFormContainer from '../containers/compose_form_container'; -import FollowFormContainer from '../containers/follow_form_container'; -import UploadFormContainer from '../containers/upload_form_container'; -import StatusListContainer from '../containers/status_list_container'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ComposeFormContainer from '../containers/compose_form_container'; +import FollowFormContainer from '../containers/follow_form_container'; +import UploadFormContainer from '../containers/upload_form_container'; +import StatusListContainer from '../containers/status_list_container'; +import NotificationsContainer from '../containers/notifications_container'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; const Frontend = React.createClass({ @@ -32,6 +33,8 @@ const Frontend = React.createClass({ + + ); } diff --git a/app/assets/javascripts/components/containers/notifications_container.jsx b/app/assets/javascripts/components/containers/notifications_container.jsx new file mode 100644 index 000000000..68173b34e --- /dev/null +++ b/app/assets/javascripts/components/containers/notifications_container.jsx @@ -0,0 +1,25 @@ +import { connect } from 'react-redux'; +import { NotificationStack } from 'react-notification'; +import { dismissNotification } from '../actions/notifications'; + +const mapStateToProps = (state, props) => { + return { + notifications: state.get('notifications').map((item, i) => ({ + message: item.get('message'), + title: item.get('title'), + key: i, + action: 'Dismiss', + dismissAfter: 5000 + })).toJS() + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + onDismiss: notifiction => { + dispatch(dismissNotification(notifiction)); + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(NotificationStack); diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx index b4a21b874..c0d9f4ff2 100644 --- a/app/assets/javascripts/components/reducers/compose.jsx +++ b/app/assets/javascripts/components/reducers/compose.jsx @@ -58,4 +58,4 @@ export default function compose(state = initialState, action) { default: return state; } -} +}; diff --git a/app/assets/javascripts/components/reducers/follow.jsx b/app/assets/javascripts/components/reducers/follow.jsx index 348510eaf..838f12259 100644 --- a/app/assets/javascripts/components/reducers/follow.jsx +++ b/app/assets/javascripts/components/reducers/follow.jsx @@ -21,4 +21,4 @@ export default function compose(state = initialState, action) { default: return state; } -} +}; diff --git a/app/assets/javascripts/components/reducers/index.jsx b/app/assets/javascripts/components/reducers/index.jsx index 3f3e1e928..6d275654a 100644 --- a/app/assets/javascripts/components/reducers/index.jsx +++ b/app/assets/javascripts/components/reducers/index.jsx @@ -3,10 +3,12 @@ import timelines from './timelines'; import meta from './meta'; import compose from './compose'; import follow from './follow'; +import notifications from './notifications'; export default combineReducers({ timelines, meta, compose, - follow + follow, + notifications }); diff --git a/app/assets/javascripts/components/reducers/meta.jsx b/app/assets/javascripts/components/reducers/meta.jsx index d65c3c36d..71a14dbd3 100644 --- a/app/assets/javascripts/components/reducers/meta.jsx +++ b/app/assets/javascripts/components/reducers/meta.jsx @@ -10,4 +10,4 @@ export default function meta(state = initialState, action) { default: return state; } -} +}; diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx new file mode 100644 index 000000000..6ba453292 --- /dev/null +++ b/app/assets/javascripts/components/reducers/notifications.jsx @@ -0,0 +1,27 @@ +import { COMPOSE_SUBMIT_FAIL, COMPOSE_UPLOAD_FAIL } from '../actions/compose'; +import { FOLLOW_SUBMIT_FAIL } from '../actions/follow'; +import { REBLOG_FAIL, FAVOURITE_FAIL } from '../actions/interactions'; +import { TIMELINE_REFRESH_FAIL } from '../actions/timelines'; +import { NOTIFICATION_DISMISS } from '../actions/notifications'; +import Immutable from 'immutable'; + +const initialState = Immutable.List(); + +export default function meta(state = initialState, action) { + switch(action.type) { + case COMPOSE_SUBMIT_FAIL: + case COMPOSE_UPLOAD_FAIL: + case FOLLOW_SUBMIT_FAIL: + case REBLOG_FAIL: + case FAVOURITE_FAIL: + case TIMELINE_REFRESH_FAIL: + return state.push(Immutable.fromJS({ + message: action.error.response.statusText, + title: `${action.error.response.status}` + })); + case NOTIFICATION_DISMISS: + return state.clear(); + default: + return state; + } +}; diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index fb990ef54..b6ecdfb1f 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -66,4 +66,4 @@ export default function timelines(state = initialState, action) { default: return state; } -} +}; diff --git a/app/controllers/api/follows_controller.rb b/app/controllers/api/follows_controller.rb index 9ec4f46b8..604e65565 100644 --- a/app/controllers/api/follows_controller.rb +++ b/app/controllers/api/follows_controller.rb @@ -3,6 +3,10 @@ class Api::FollowsController < ApiController respond_to :json def create + if params[:uri].blank? + raise ActiveRecord::RecordNotFound + end + @follow = FollowService.new.(current_user.account, params[:uri]) render action: :show end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 681c6843b..b64f04a14 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -34,7 +34,7 @@ class RemoveStatusService < BaseService end def send_delete_salmon(account, status) - SendInteractionService.new.(status.stream_entry, account) + NotificationWorker.perform_async(status.stream_entry_id, account.id) end def remove_reblogs(status) diff --git a/package.json b/package.json index 18e0cc736..5234a37f0 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "moment": "^2.14.1", "react-addons-pure-render-mixin": "^15.3.1", "react-immutable-proptypes": "^2.1.0", + "react-notification": "^6.1.1", "react-redux": "^4.4.5", "react-router": "^2.8.0", "redux": "^3.5.2",