The code powering m.abunchtell.com https://m.abunchtell.com
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

230 строки
7.6 KiB

  1. import api, { getLinks } from '../api';
  2. import IntlMessageFormat from 'intl-messageformat';
  3. import { fetchRelationships } from './accounts';
  4. import {
  5. importFetchedAccount,
  6. importFetchedAccounts,
  7. importFetchedStatus,
  8. importFetchedStatuses,
  9. } from './importer';
  10. import { saveSettings } from './settings';
  11. import { defineMessages } from 'react-intl';
  12. import { List as ImmutableList } from 'immutable';
  13. import { unescapeHTML } from '../utils/html';
  14. import { getFiltersRegex } from '../selectors';
  15. import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
  16. import compareId from 'mastodon/compare_id';
  17. import { searchTextFromRawStatus } from 'mastodon/actions/importer/normalizer';
  18. export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
  19. export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
  20. export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
  21. export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
  22. export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
  23. export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
  24. export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
  25. export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
  26. export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING';
  27. export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT';
  28. export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
  29. defineMessages({
  30. mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
  31. group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
  32. });
  33. const fetchRelatedRelationships = (dispatch, notifications) => {
  34. const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
  35. if (accountIds.length > 0) {
  36. dispatch(fetchRelationships(accountIds));
  37. }
  38. };
  39. export const loadPending = () => ({
  40. type: NOTIFICATIONS_LOAD_PENDING,
  41. });
  42. export function updateNotifications(notification, intlMessages, intlLocale) {
  43. return (dispatch, getState) => {
  44. const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
  45. const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
  46. const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
  47. const filters = getFiltersRegex(getState(), { contextType: 'notifications' });
  48. let filtered = false;
  49. if (notification.type === 'mention') {
  50. const dropRegex = filters[0];
  51. const regex = filters[1];
  52. const searchIndex = searchTextFromRawStatus(notification.status);
  53. if (dropRegex && dropRegex.test(searchIndex)) {
  54. return;
  55. }
  56. filtered = regex && regex.test(searchIndex);
  57. }
  58. if (showInColumn) {
  59. dispatch(importFetchedAccount(notification.account));
  60. if (notification.status) {
  61. dispatch(importFetchedStatus(notification.status));
  62. }
  63. dispatch({
  64. type: NOTIFICATIONS_UPDATE,
  65. notification,
  66. usePendingItems: preferPendingItems,
  67. meta: (playSound && !filtered) ? { sound: 'boop' } : undefined,
  68. });
  69. fetchRelatedRelationships(dispatch, [notification]);
  70. } else if (playSound && !filtered) {
  71. dispatch({
  72. type: NOTIFICATIONS_UPDATE_NOOP,
  73. meta: { sound: 'boop' },
  74. });
  75. }
  76. // Desktop notifications
  77. if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
  78. const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
  79. const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
  80. const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
  81. notify.addEventListener('click', () => {
  82. window.focus();
  83. notify.close();
  84. });
  85. }
  86. };
  87. };
  88. const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
  89. const excludeTypesFromFilter = filter => {
  90. const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll']);
  91. return allTypes.filterNot(item => item === filter).toJS();
  92. };
  93. const noOp = () => {};
  94. export function expandNotifications({ maxId } = {}, done = noOp) {
  95. return (dispatch, getState) => {
  96. const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
  97. const notifications = getState().get('notifications');
  98. const isLoadingMore = !!maxId;
  99. if (notifications.get('isLoading')) {
  100. done();
  101. return;
  102. }
  103. const params = {
  104. max_id: maxId,
  105. exclude_types: activeFilter === 'all'
  106. ? excludeTypesFromSettings(getState())
  107. : excludeTypesFromFilter(activeFilter),
  108. };
  109. if (!params.max_id && (notifications.get('items', ImmutableList()).size + notifications.get('pendingItems', ImmutableList()).size) > 0) {
  110. const a = notifications.getIn(['pendingItems', 0, 'id']);
  111. const b = notifications.getIn(['items', 0, 'id']);
  112. if (a && b && compareId(a, b) > 0) {
  113. params.since_id = a;
  114. } else {
  115. params.since_id = b || a;
  116. }
  117. }
  118. const isLoadingRecent = !!params.since_id;
  119. dispatch(expandNotificationsRequest(isLoadingMore));
  120. api(getState).get('/api/v1/notifications', { params }).then(response => {
  121. const next = getLinks(response).refs.find(link => link.rel === 'next');
  122. dispatch(importFetchedAccounts(response.data.map(item => item.account)));
  123. dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
  124. dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems));
  125. fetchRelatedRelationships(dispatch, response.data);
  126. done();
  127. }).catch(error => {
  128. dispatch(expandNotificationsFail(error, isLoadingMore));
  129. done();
  130. });
  131. };
  132. };
  133. export function expandNotificationsRequest(isLoadingMore) {
  134. return {
  135. type: NOTIFICATIONS_EXPAND_REQUEST,
  136. skipLoading: !isLoadingMore,
  137. };
  138. };
  139. export function expandNotificationsSuccess(notifications, next, isLoadingMore, isLoadingRecent, usePendingItems) {
  140. return {
  141. type: NOTIFICATIONS_EXPAND_SUCCESS,
  142. notifications,
  143. next,
  144. isLoadingRecent: isLoadingRecent,
  145. usePendingItems,
  146. skipLoading: !isLoadingMore,
  147. };
  148. };
  149. export function expandNotificationsFail(error, isLoadingMore) {
  150. return {
  151. type: NOTIFICATIONS_EXPAND_FAIL,
  152. error,
  153. skipLoading: !isLoadingMore,
  154. };
  155. };
  156. export function clearNotifications() {
  157. return (dispatch, getState) => {
  158. dispatch({
  159. type: NOTIFICATIONS_CLEAR,
  160. });
  161. api(getState).post('/api/v1/notifications/clear');
  162. };
  163. };
  164. export function scrollTopNotifications(top) {
  165. return {
  166. type: NOTIFICATIONS_SCROLL_TOP,
  167. top,
  168. };
  169. };
  170. export function setFilter (filterType) {
  171. return dispatch => {
  172. dispatch({
  173. type: NOTIFICATIONS_FILTER_SET,
  174. path: ['notifications', 'quickFilter', 'active'],
  175. value: filterType,
  176. });
  177. dispatch(expandNotifications());
  178. dispatch(saveSettings());
  179. };
  180. };
  181. export const mountNotifications = () => ({
  182. type: NOTIFICATIONS_MOUNT,
  183. });
  184. export const unmountNotifications = () => ({
  185. type: NOTIFICATIONS_UNMOUNT,
  186. });