c8e8618f64
updated streaming functionality to load notifications into a queue (if currently on notitications page) and to display TimelineQueueButtonHeader with outstanding notification count. (if not on notifications page, it behaves as normal, adding/updating notification state). Max 40 are saved to queuedNotifications state and all are tallied into the totalQueuedNotificationsCount state. On click of TimelineQueueButtonHeader it dequeues the queuedNotifications and loads on page if <= max, otherwise it refreshes the page and shows latest 20 (default count) and clears/resets the state for queuedNotifications and totalQueuedNotificationsCount.
245 lines
7.7 KiB
JavaScript
245 lines
7.7 KiB
JavaScript
import api, { getLinks } from '../api';
|
|
import IntlMessageFormat from 'intl-messageformat';
|
|
import { fetchRelationships } from './accounts';
|
|
import {
|
|
importFetchedAccount,
|
|
importFetchedAccounts,
|
|
importFetchedStatus,
|
|
importFetchedStatuses,
|
|
} from './importer';
|
|
import { saveSettings } from './settings';
|
|
import { defineMessages } from 'react-intl';
|
|
import { List as ImmutableList } from 'immutable';
|
|
import { unescapeHTML } from '../utils/html';
|
|
import { getFilters, regexFromFilters } from '../selectors';
|
|
import { me } from 'gabsocial/initial_state';
|
|
|
|
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
|
|
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
|
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
|
|
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
|
|
|
|
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
|
|
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
|
|
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
|
|
|
|
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
|
|
|
|
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
|
|
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
|
|
|
export const MAX_QUEUED_NOTIFICATIONS = 40;
|
|
|
|
defineMessages({
|
|
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
|
|
group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
|
|
});
|
|
|
|
const fetchRelatedRelationships = (dispatch, notifications) => {
|
|
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
|
|
|
|
if (accountIds.length > 0) {
|
|
dispatch(fetchRelationships(accountIds));
|
|
}
|
|
};
|
|
|
|
export function updateNotifications(notification, intlMessages, intlLocale) {
|
|
return (dispatch, getState) => {
|
|
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
|
|
|
|
if (showInColumn) {
|
|
dispatch(importFetchedAccount(notification.account));
|
|
|
|
if (notification.status) {
|
|
dispatch(importFetchedStatus(notification.status));
|
|
}
|
|
|
|
dispatch({
|
|
type: NOTIFICATIONS_UPDATE,
|
|
notification,
|
|
});
|
|
|
|
fetchRelatedRelationships(dispatch, [notification]);
|
|
}
|
|
};
|
|
};
|
|
|
|
export function updateNotificationsQueue(notification, intlMessages, intlLocale, curPath) {
|
|
return (dispatch, getState) => {
|
|
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
|
|
const filters = getFilters(getState(), { contextType: 'notifications' });
|
|
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
|
|
|
|
let filtered = false;
|
|
|
|
const isOnNotificationsPage = curPath === '/notifications';
|
|
|
|
if (notification.type === 'mention') {
|
|
const regex = regexFromFilters(filters);
|
|
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
|
|
filtered = regex && regex.test(searchIndex);
|
|
}
|
|
|
|
// Desktop notifications
|
|
if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
|
|
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
|
|
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
|
|
|
|
const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
|
|
|
|
notify.addEventListener('click', () => {
|
|
window.focus();
|
|
notify.close();
|
|
});
|
|
}
|
|
|
|
if (playSound && !filtered) {
|
|
dispatch({
|
|
type: NOTIFICATIONS_UPDATE_NOOP,
|
|
meta: { sound: 'ribbit' },
|
|
});
|
|
}
|
|
|
|
if (isOnNotificationsPage) {
|
|
dispatch({
|
|
type: NOTIFICATIONS_UPDATE_QUEUE,
|
|
notification,
|
|
intlMessages,
|
|
intlLocale,
|
|
});
|
|
}
|
|
else {
|
|
dispatch(updateNotifications(notification, intlMessages, intlLocale));
|
|
}
|
|
}
|
|
};
|
|
|
|
export function dequeueNotifications() {
|
|
return (dispatch, getState) => {
|
|
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList());
|
|
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0);
|
|
|
|
if (totalQueuedNotificationsCount == 0) {
|
|
return;
|
|
}
|
|
else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
|
|
queuedNotifications.forEach(block => {
|
|
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale));
|
|
});
|
|
}
|
|
else {
|
|
dispatch(expandNotifications());
|
|
}
|
|
|
|
dispatch({
|
|
type: NOTIFICATIONS_DEQUEUE,
|
|
});
|
|
}
|
|
};
|
|
|
|
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
|
|
|
|
const excludeTypesFromFilter = filter => {
|
|
const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention', 'poll']);
|
|
return allTypes.filterNot(item => item === filter).toJS();
|
|
};
|
|
|
|
const noOp = () => {};
|
|
|
|
export function expandNotifications({ maxId } = {}, done = noOp) {
|
|
return (dispatch, getState) => {
|
|
if (!me) return;
|
|
|
|
const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
|
|
const notifications = getState().get('notifications');
|
|
const isLoadingMore = !!maxId;
|
|
|
|
if (notifications.get('isLoading')) {
|
|
done();
|
|
return;
|
|
}
|
|
|
|
const params = {
|
|
max_id: maxId,
|
|
exclude_types: activeFilter === 'all'
|
|
? excludeTypesFromSettings(getState())
|
|
: excludeTypesFromFilter(activeFilter),
|
|
};
|
|
|
|
if (!maxId && notifications.get('items').size > 0) {
|
|
params.since_id = notifications.getIn(['items', 0, 'id']);
|
|
}
|
|
|
|
dispatch(expandNotificationsRequest(isLoadingMore));
|
|
|
|
api(getState).get('/api/v1/notifications', { params }).then(response => {
|
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
|
|
|
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
|
|
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
|
|
|
|
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
|
|
fetchRelatedRelationships(dispatch, response.data);
|
|
done();
|
|
}).catch(error => {
|
|
dispatch(expandNotificationsFail(error, isLoadingMore));
|
|
done();
|
|
});
|
|
};
|
|
};
|
|
|
|
export function expandNotificationsRequest(isLoadingMore) {
|
|
return {
|
|
type: NOTIFICATIONS_EXPAND_REQUEST,
|
|
skipLoading: !isLoadingMore,
|
|
};
|
|
};
|
|
|
|
export function expandNotificationsSuccess(notifications, next, isLoadingMore) {
|
|
return {
|
|
type: NOTIFICATIONS_EXPAND_SUCCESS,
|
|
notifications,
|
|
next,
|
|
skipLoading: !isLoadingMore,
|
|
};
|
|
};
|
|
|
|
export function expandNotificationsFail(error, isLoadingMore) {
|
|
return {
|
|
type: NOTIFICATIONS_EXPAND_FAIL,
|
|
error,
|
|
skipLoading: !isLoadingMore,
|
|
};
|
|
};
|
|
|
|
export function clearNotifications() {
|
|
return (dispatch, getState) => {
|
|
if (!me) return;
|
|
|
|
dispatch({
|
|
type: NOTIFICATIONS_CLEAR,
|
|
});
|
|
|
|
api(getState).post('/api/v1/notifications/clear');
|
|
};
|
|
};
|
|
|
|
export function scrollTopNotifications(top) {
|
|
return {
|
|
type: NOTIFICATIONS_SCROLL_TOP,
|
|
top,
|
|
};
|
|
};
|
|
|
|
export function setFilter (filterType) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: NOTIFICATIONS_FILTER_SET,
|
|
path: ['notifications', 'quickFilter', 'active'],
|
|
value: filterType,
|
|
});
|
|
dispatch(expandNotifications());
|
|
dispatch(saveSettings());
|
|
};
|
|
};
|