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) {
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) {
dispatch(uploadComposeFail(error));
});
};
-}
+};
export function uploadComposeRequest() {
return {
type: COMPOSE_UPLOAD_REQUEST
};
-}
+};
export function uploadComposeProgress(loaded, total) {
return {
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
};
-}
+};
type: FOLLOW_CHANGE,
text: text
};
-}
+};
export function submitFollow() {
return function (dispatch, getState) {
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
};
-}
+};
dispatch(reblogFail(status, error));
});
};
-}
+};
export function reblogRequest(status) {
return {
type: REBLOG_REQUEST,
status: status
};
-}
+};
export function reblogSuccess(status, response) {
return {
status: status,
response: response
};
-}
+};
export function reblogFail(status, error) {
return {
status: status,
error: error
};
-}
+};
export function favourite(status) {
return function (dispatch, getState) {
dispatch(favouriteFail(status, error));
});
};
-}
+};
export function favouriteRequest(status) {
return {
type: FAVOURITE_REQUEST,
status: status
};
-}
+};
export function favouriteSuccess(status, response) {
return {
status: status,
response: response
};
-}
+};
export function favouriteFail(status, error) {
return {
status: status,
error: error
};
-}
+};
type: ACCESS_TOKEN_SET,
token: token
};
-}
+};
--- /dev/null
+export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS';
+
+export function dismissNotification(notification) {
+ return {
+ type: NOTIFICATION_DISMISS,
+ notification: notification
+ };
+};
timeline: timeline,
statuses: statuses
};
-}
+};
export function updateTimeline(timeline, status) {
return {
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) {
dispatch(refreshTimelineFail(timeline, error));
});
};
-}
+};
export function refreshTimelineSuccess(timeline, statuses) {
return function (dispatch) {
dispatch(setTimeline(timeline, statuses));
};
-}
+};
export function refreshTimelineFail(timeline, error) {
return {
timeline: timeline,
error: error
};
-}
+};
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({
<StatusListContainer type='mentions' />
</Column>
</ColumnsArea>
+
+ <NotificationsContainer />
</div>
);
}
--- /dev/null
+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);
default:
return state;
}
-}
+};
default:
return state;
}
-}
+};
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
});
default:
return state;
}
-}
+};
--- /dev/null
+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;
+ }
+};
default:
return state;
}
-}
+};
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
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)
"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",