Adding a notification stack for error messages

This commit is contained in:
Eugen Rochko 2016-09-12 19:20:55 +02:00
parent 05b0c985b4
commit d6a64f45fd
17 changed files with 115 additions and 45 deletions

View file

@ -19,20 +19,20 @@ export function changeCompose(text) {
type: COMPOSE_CHANGE, type: COMPOSE_CHANGE,
text: text text: text
}; };
} };
export function replyCompose(status) { export function replyCompose(status) {
return { return {
type: COMPOSE_REPLY, type: COMPOSE_REPLY,
status: status status: status
}; };
} };
export function cancelReplyCompose() { export function cancelReplyCompose() {
return { return {
type: COMPOSE_REPLY_CANCEL type: COMPOSE_REPLY_CANCEL
}; };
} };
export function submitCompose() { export function submitCompose() {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -48,27 +48,27 @@ export function submitCompose() {
dispatch(submitComposeFail(error)); dispatch(submitComposeFail(error));
}); });
}; };
} };
export function submitComposeRequest() { export function submitComposeRequest() {
return { return {
type: COMPOSE_SUBMIT_REQUEST type: COMPOSE_SUBMIT_REQUEST
}; };
} };
export function submitComposeSuccess(status) { export function submitComposeSuccess(status) {
return { return {
type: COMPOSE_SUBMIT_SUCCESS, type: COMPOSE_SUBMIT_SUCCESS,
status: status status: status
}; };
} };
export function submitComposeFail(error) { export function submitComposeFail(error) {
return { return {
type: COMPOSE_SUBMIT_FAIL, type: COMPOSE_SUBMIT_FAIL,
error: error error: error
}; };
} };
export function uploadCompose(files) { export function uploadCompose(files) {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -87,13 +87,13 @@ export function uploadCompose(files) {
dispatch(uploadComposeFail(error)); dispatch(uploadComposeFail(error));
}); });
}; };
} };
export function uploadComposeRequest() { export function uploadComposeRequest() {
return { return {
type: COMPOSE_UPLOAD_REQUEST type: COMPOSE_UPLOAD_REQUEST
}; };
} };
export function uploadComposeProgress(loaded, total) { export function uploadComposeProgress(loaded, total) {
return { return {
@ -101,25 +101,25 @@ export function uploadComposeProgress(loaded, total) {
loaded: loaded, loaded: loaded,
total: total total: total
}; };
} };
export function uploadComposeSuccess(media) { export function uploadComposeSuccess(media) {
return { return {
type: COMPOSE_UPLOAD_SUCCESS, type: COMPOSE_UPLOAD_SUCCESS,
media: media media: media
}; };
} };
export function uploadComposeFail(error) { export function uploadComposeFail(error) {
return { return {
type: COMPOSE_UPLOAD_FAIL, type: COMPOSE_UPLOAD_FAIL,
error: error error: error
}; };
} };
export function undoUploadCompose(media_id) { export function undoUploadCompose(media_id) {
return { return {
type: COMPOSE_UPLOAD_UNDO, type: COMPOSE_UPLOAD_UNDO,
media_id: media_id media_id: media_id
}; };
} };

View file

@ -11,7 +11,7 @@ export function changeFollow(text) {
type: FOLLOW_CHANGE, type: FOLLOW_CHANGE,
text: text text: text
}; };
} };
export function submitFollow() { export function submitFollow() {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -25,24 +25,24 @@ export function submitFollow() {
dispatch(submitFollowFail(error)); dispatch(submitFollowFail(error));
}); });
}; };
} };
export function submitFollowRequest() { export function submitFollowRequest() {
return { return {
type: FOLLOW_SUBMIT_REQUEST type: FOLLOW_SUBMIT_REQUEST
}; };
} };
export function submitFollowSuccess(account) { export function submitFollowSuccess(account) {
return { return {
type: FOLLOW_SUBMIT_SUCCESS, type: FOLLOW_SUBMIT_SUCCESS,
account: account account: account
}; };
} };
export function submitFollowFail(error) { export function submitFollowFail(error) {
return { return {
type: FOLLOW_SUBMIT_FAIL, type: FOLLOW_SUBMIT_FAIL,
error: error error: error
}; };
} };

View file

@ -22,14 +22,14 @@ export function reblog(status) {
dispatch(reblogFail(status, error)); dispatch(reblogFail(status, error));
}); });
}; };
} };
export function reblogRequest(status) { export function reblogRequest(status) {
return { return {
type: REBLOG_REQUEST, type: REBLOG_REQUEST,
status: status status: status
}; };
} };
export function reblogSuccess(status, response) { export function reblogSuccess(status, response) {
return { return {
@ -37,7 +37,7 @@ export function reblogSuccess(status, response) {
status: status, status: status,
response: response response: response
}; };
} };
export function reblogFail(status, error) { export function reblogFail(status, error) {
return { return {
@ -45,7 +45,7 @@ export function reblogFail(status, error) {
status: status, status: status,
error: error error: error
}; };
} };
export function favourite(status) { export function favourite(status) {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -57,14 +57,14 @@ export function favourite(status) {
dispatch(favouriteFail(status, error)); dispatch(favouriteFail(status, error));
}); });
}; };
} };
export function favouriteRequest(status) { export function favouriteRequest(status) {
return { return {
type: FAVOURITE_REQUEST, type: FAVOURITE_REQUEST,
status: status status: status
}; };
} };
export function favouriteSuccess(status, response) { export function favouriteSuccess(status, response) {
return { return {
@ -72,7 +72,7 @@ export function favouriteSuccess(status, response) {
status: status, status: status,
response: response response: response
}; };
} };
export function favouriteFail(status, error) { export function favouriteFail(status, error) {
return { return {
@ -80,4 +80,4 @@ export function favouriteFail(status, error) {
status: status, status: status,
error: error error: error
}; };
} };

View file

@ -5,4 +5,4 @@ export function setAccessToken(token) {
type: ACCESS_TOKEN_SET, type: ACCESS_TOKEN_SET,
token: token token: token
}; };
} };

View file

@ -0,0 +1,8 @@
export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS';
export function dismissNotification(notification) {
return {
type: NOTIFICATION_DISMISS,
notification: notification
};
};

View file

@ -14,7 +14,7 @@ export function setTimeline(timeline, statuses) {
timeline: timeline, timeline: timeline,
statuses: statuses statuses: statuses
}; };
} };
export function updateTimeline(timeline, status) { export function updateTimeline(timeline, status) {
return { return {
@ -22,21 +22,21 @@ export function updateTimeline(timeline, status) {
timeline: timeline, timeline: timeline,
status: status status: status
}; };
} };
export function deleteFromTimelines(id) { export function deleteFromTimelines(id) {
return { return {
type: TIMELINE_DELETE, type: TIMELINE_DELETE,
id: id id: id
}; };
} };
export function refreshTimelineRequest(timeline) { export function refreshTimelineRequest(timeline) {
return { return {
type: TIMELINE_REFRESH_REQUEST, type: TIMELINE_REFRESH_REQUEST,
timeline: timeline timeline: timeline
}; };
} };
export function refreshTimeline(timeline) { export function refreshTimeline(timeline) {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -48,13 +48,13 @@ export function refreshTimeline(timeline) {
dispatch(refreshTimelineFail(timeline, error)); dispatch(refreshTimelineFail(timeline, error));
}); });
}; };
} };
export function refreshTimelineSuccess(timeline, statuses) { export function refreshTimelineSuccess(timeline, statuses) {
return function (dispatch) { return function (dispatch) {
dispatch(setTimeline(timeline, statuses)); dispatch(setTimeline(timeline, statuses));
}; };
} };
export function refreshTimelineFail(timeline, error) { export function refreshTimelineFail(timeline, error) {
return { return {
@ -62,4 +62,4 @@ export function refreshTimelineFail(timeline, error) {
timeline: timeline, timeline: timeline,
error: error error: error
}; };
} };

View file

@ -5,6 +5,7 @@ import ComposeFormContainer from '../containers/compose_form_container';
import FollowFormContainer from '../containers/follow_form_container'; import FollowFormContainer from '../containers/follow_form_container';
import UploadFormContainer from '../containers/upload_form_container'; import UploadFormContainer from '../containers/upload_form_container';
import StatusListContainer from '../containers/status_list_container'; import StatusListContainer from '../containers/status_list_container';
import NotificationsContainer from '../containers/notifications_container';
import PureRenderMixin from 'react-addons-pure-render-mixin'; import PureRenderMixin from 'react-addons-pure-render-mixin';
const Frontend = React.createClass({ const Frontend = React.createClass({
@ -32,6 +33,8 @@ const Frontend = React.createClass({
<StatusListContainer type='mentions' /> <StatusListContainer type='mentions' />
</Column> </Column>
</ColumnsArea> </ColumnsArea>
<NotificationsContainer />
</div> </div>
); );
} }

View file

@ -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);

View file

@ -58,4 +58,4 @@ export default function compose(state = initialState, action) {
default: default:
return state; return state;
} }
} };

View file

@ -21,4 +21,4 @@ export default function compose(state = initialState, action) {
default: default:
return state; return state;
} }
} };

View file

@ -3,10 +3,12 @@ import timelines from './timelines';
import meta from './meta'; import meta from './meta';
import compose from './compose'; import compose from './compose';
import follow from './follow'; import follow from './follow';
import notifications from './notifications';
export default combineReducers({ export default combineReducers({
timelines, timelines,
meta, meta,
compose, compose,
follow follow,
notifications
}); });

View file

@ -10,4 +10,4 @@ export default function meta(state = initialState, action) {
default: default:
return state; return state;
} }
} };

View file

@ -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;
}
};

View file

@ -66,4 +66,4 @@ export default function timelines(state = initialState, action) {
default: default:
return state; return state;
} }
} };

View file

@ -3,6 +3,10 @@ class Api::FollowsController < ApiController
respond_to :json respond_to :json
def create def create
if params[:uri].blank?
raise ActiveRecord::RecordNotFound
end
@follow = FollowService.new.(current_user.account, params[:uri]) @follow = FollowService.new.(current_user.account, params[:uri])
render action: :show render action: :show
end end

View file

@ -34,7 +34,7 @@ class RemoveStatusService < BaseService
end end
def send_delete_salmon(account, status) def send_delete_salmon(account, status)
SendInteractionService.new.(status.stream_entry, account) NotificationWorker.perform_async(status.stream_entry_id, account.id)
end end
def remove_reblogs(status) def remove_reblogs(status)

View file

@ -19,6 +19,7 @@
"moment": "^2.14.1", "moment": "^2.14.1",
"react-addons-pure-render-mixin": "^15.3.1", "react-addons-pure-render-mixin": "^15.3.1",
"react-immutable-proptypes": "^2.1.0", "react-immutable-proptypes": "^2.1.0",
"react-notification": "^6.1.1",
"react-redux": "^4.4.5", "react-redux": "^4.4.5",
"react-router": "^2.8.0", "react-router": "^2.8.0",
"redux": "^3.5.2", "redux": "^3.5.2",