M app/javascript/flavours/glitch/actions/alerts.js => app/javascript/flavours/glitch/actions/alerts.js +17 -16
@@ 25,12 25,10 @@ export function clearAlert() {
};
}
-export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) {
+export function showAlert(alert) {
return {
type: ALERT_SHOW,
- title,
- message,
- message_values,
+ alert,
};
}
@@ 44,20 42,23 @@ export function showAlertForError(error, skipNotFound = false) {
}
if (status === 429 && headers['x-ratelimit-reset']) {
- const reset_date = new Date(headers['x-ratelimit-reset']);
- return showAlert(messages.rateLimitedTitle, messages.rateLimitedMessage, { 'retry_time': reset_date });
+ return showAlert({
+ title: messages.rateLimitedTitle,
+ message: messages.rateLimitedMessage,
+ values: { 'retry_time': new Date(headers['x-ratelimit-reset']) },
+ });
}
- let message = statusText;
- let title = `${status}`;
+ return showAlert({
+ title: `${status}`,
+ message: data.error || statusText,
+ });
+ }
- if (data.error) {
- message = data.error;
- }
+ console.error(error);
- return showAlert(title, message);
- } else {
- console.error(error);
- return showAlert();
- }
+ return showAlert({
+ title: messages.unexpectedTitle,
+ message: messages.unexpectedMessage,
+ });
}
M app/javascript/flavours/glitch/actions/compose.js => app/javascript/flavours/glitch/actions/compose.js +12 -2
@@ 89,6 89,9 @@ export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
const messages = defineMessages({
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
+ open: { id: 'compose.published.open', defaultMessage: 'Open' },
+ published: { id: 'compose.published.body', defaultMessage: 'Post published.' },
+ saved: { id: 'compose.saved.body', defaultMessage: 'Post saved.' },
});
export const ensureComposeIsVisible = (getState, routerHistory) => {
@@ 262,6 265,13 @@ export function submitCompose(routerHistory) {
} else if (statusId === null && response.data.visibility === 'direct') {
insertIfOnline('direct');
}
+
+ dispatch(showAlert({
+ message: statusId === null ? messages.published : messages.saved,
+ action: messages.open,
+ dismissAfter: 10000,
+ onClick: () => routerHistory.push(`/@${response.data.account.username}/${response.data.id}`),
+ }));
}).catch(function (error) {
dispatch(submitComposeFail(error));
});
@@ 304,12 314,12 @@ export function uploadCompose(files) {
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
if (files.length + media.size + pending > uploadLimit) {
- dispatch(showAlert(undefined, messages.uploadErrorLimit));
+ dispatch(showAlert({ message: messages.uploadErrorLimit }));
return;
}
if (getState().getIn(['compose', 'poll'])) {
- dispatch(showAlert(undefined, messages.uploadErrorPoll));
+ dispatch(showAlert({ message: messages.uploadErrorPoll }));
return;
}
M app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js => app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js +2 -2
@@ 33,7 33,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
if (permission === 'granted') {
dispatch(changePushNotifications(path.slice(1), checked));
} else {
- dispatch(showAlert(undefined, messages.permissionDenied));
+ dispatch(showAlert({ message: messages.permissionDenied }));
}
}));
} else {
@@ 48,7 48,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
if (permission === 'granted') {
dispatch(changeSetting(['notifications', ...path], checked));
} else {
- dispatch(showAlert(undefined, messages.permissionDenied));
+ dispatch(showAlert({ message: messages.permissionDenied }));
}
}));
} else {
M app/javascript/flavours/glitch/features/ui/containers/notifications_container.js => app/javascript/flavours/glitch/features/ui/containers/notifications_container.js +21 -17
@@ 7,26 7,30 @@ import { NotificationStack } from 'react-notification';
import { dismissAlert } from 'flavours/glitch/actions/alerts';
import { getAlerts } from 'flavours/glitch/selectors';
-const mapStateToProps = (state, { intl }) => {
- const notifications = getAlerts(state);
-
- notifications.forEach(notification => ['title', 'message'].forEach(key => {
- const value = notification[key];
-
- if (typeof value === 'object') {
- notification[key] = intl.formatMessage(value, notification[`${key}_values`]);
- }
- }));
+const formatIfNeeded = (intl, message, values) => {
+ if (typeof message === 'object') {
+ return intl.formatMessage(message, values);
+ }
- return { notifications };
+ return message;
};
-const mapDispatchToProps = (dispatch) => {
- return {
- onDismiss: alert => {
- dispatch(dismissAlert(alert));
- },
- };
+const mapStateToProps = (state, { intl }) => {
+ console.log(getAlerts(state));
+ return ({
+ notifications: getAlerts(state).map(alert => ({
+ ...alert,
+ action: formatIfNeeded(intl, alert.action, alert.values),
+ title: formatIfNeeded(intl, alert.title, alert.values),
+ message: formatIfNeeded(intl, alert.message, alert.values),
+ })),
+})
};
+const mapDispatchToProps = (dispatch) => ({
+ onDismiss (alert) {
+ dispatch(dismissAlert(alert));
+ },
+});
+
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NotificationStack));
M app/javascript/flavours/glitch/reducers/alerts.js => app/javascript/flavours/glitch/reducers/alerts.js +11 -8
@@ 1,4 1,4 @@
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import { List as ImmutableList } from 'immutable';
import {
ALERT_SHOW,
@@ 8,17 8,20 @@ import {
const initialState = ImmutableList([]);
+let id = 0;
+
+const addAlert = (state, alert) =>
+ state.push({
+ key: id++,
+ ...alert,
+ });
+
export default function alerts(state = initialState, action) {
switch(action.type) {
case ALERT_SHOW:
- return state.push(ImmutableMap({
- key: state.size > 0 ? state.last().get('key') + 1 : 0,
- title: action.title,
- message: action.message,
- message_values: action.message_values,
- }));
+ return addAlert(state, action.alert);
case ALERT_DISMISS:
- return state.filterNot(item => item.get('key') === action.alert.key);
+ return state.filterNot(item => item.key === action.alert.key);
case ALERT_CLEAR:
return state.clear();
default:
M app/javascript/flavours/glitch/selectors/index.js => app/javascript/flavours/glitch/selectors/index.js +9 -19
@@ 84,26 84,16 @@ export const makeGetPictureInPicture = () => {
}));
};
-const getAlertsBase = state => state.get('alerts');
-
-export const getAlerts = createSelector([getAlertsBase], (base) => {
- let arr = [];
-
- base.forEach(item => {
- arr.push({
- message: item.get('message'),
- message_values: item.get('message_values'),
- title: item.get('title'),
- key: item.get('key'),
- dismissAfter: 5000,
- barStyle: {
- zIndex: 200,
- },
- });
- });
+const ALERT_DEFAULTS = {
+ dismissAfter: 5000,
+ style: false,
+};
- return arr;
-});
+export const getAlerts = createSelector(state => state.get('alerts'), alerts =>
+ alerts.map(item => ({
+ ...ALERT_DEFAULTS,
+ ...item,
+ })).toArray());
export const makeGetNotification = () => createSelector([
(_, base) => base,
M app/javascript/flavours/glitch/styles/components/misc.scss => app/javascript/flavours/glitch/styles/components/misc.scss +59 -0
@@ 1614,6 1614,65 @@ button.icon-button.active i.fa-retweet {
min-width: 75%;
}
+.notification-list {
+ position: fixed;
+ bottom: 2rem;
+ inset-inline-start: 0;
+ z-index: 999;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.notification-bar {
+ flex: 0 0 auto;
+ position: relative;
+ inset-inline-start: -100%;
+ width: auto;
+ padding: 15px;
+ margin: 0;
+ color: $white;
+ background: rgba($black, 0.85);
+ backdrop-filter: blur(8px);
+ border: 1px solid rgba(lighten($classic-base-color, 4%), 0.85);
+ border-radius: 8px;
+ box-shadow: 0 10px 15px -3px rgba($base-shadow-color, 0.25),
+ 0 4px 6px -4px rgba($base-shadow-color, 0.25);
+ cursor: default;
+ transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1);
+ transform: translateZ(0);
+ font-size: 15px;
+ line-height: 21px;
+
+ &.notification-bar-active {
+ inset-inline-start: 1rem;
+ }
+}
+
+.notification-bar-title {
+ margin-inline-end: 5px;
+}
+
+.notification-bar-title,
+.notification-bar-action {
+ font-weight: 700;
+}
+
+.notification-bar-action {
+ text-transform: uppercase;
+ margin-inline-start: 10px;
+ cursor: pointer;
+ color: $blurple-300;
+ border-radius: 4px;
+ padding: 0 4px;
+
+ &:hover,
+ &:focus,
+ &:active {
+ background: rgba($ui-base-color, 0.85);
+ }
+}
+
::-webkit-scrollbar-thumb {
border-radius: 0;
}