about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2016-09-12 19:20:55 +0200
committerEugen Rochko <eugen@zeonfederated.com>2016-09-12 19:20:55 +0200
commitd6a64f45fd4530cfee4f7721f0c6e7ca28fe677f (patch)
tree64b6813d49d458c5833f40c9668c4677f56eaf42
parent05b0c985b4d4b313e75ae506fb43b5690afade8d (diff)
Adding a notification stack for error messages
-rw-r--r--app/assets/javascripts/components/actions/compose.jsx26
-rw-r--r--app/assets/javascripts/components/actions/follow.jsx10
-rw-r--r--app/assets/javascripts/components/actions/interactions.jsx16
-rw-r--r--app/assets/javascripts/components/actions/meta.jsx2
-rw-r--r--app/assets/javascripts/components/actions/notifications.jsx8
-rw-r--r--app/assets/javascripts/components/actions/timelines.jsx14
-rw-r--r--app/assets/javascripts/components/components/frontend.jsx13
-rw-r--r--app/assets/javascripts/components/containers/notifications_container.jsx25
-rw-r--r--app/assets/javascripts/components/reducers/compose.jsx2
-rw-r--r--app/assets/javascripts/components/reducers/follow.jsx2
-rw-r--r--app/assets/javascripts/components/reducers/index.jsx4
-rw-r--r--app/assets/javascripts/components/reducers/meta.jsx2
-rw-r--r--app/assets/javascripts/components/reducers/notifications.jsx27
-rw-r--r--app/assets/javascripts/components/reducers/timelines.jsx2
-rw-r--r--app/controllers/api/follows_controller.rb4
-rw-r--r--app/services/remove_status_service.rb2
-rw-r--r--package.json1
17 files changed, 115 insertions, 45 deletions
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({
             <StatusListContainer type='mentions' />
           </Column>
         </ColumnsArea>
+
+        <NotificationsContainer />
       </div>
     );
   }
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",