You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
8.2 KiB

6 years ago
  1. import api, { getLinks } from 'flavours/glitch/util/api';
  2. import IntlMessageFormat from 'intl-messageformat';
  3. import { fetchRelationships } from './accounts';
  4. import { defineMessages } from 'react-intl';
  5. import { List as ImmutableList } from 'immutable';
  6. import { unescapeHTML } from 'flavours/glitch/util/html';
  7. import { getFilters, regexFromFilters } from 'flavours/glitch/selectors';
  8. export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
  9. // tracking the notif cleaning request
  10. export const NOTIFICATIONS_DELETE_MARKED_REQUEST = 'NOTIFICATIONS_DELETE_MARKED_REQUEST';
  11. export const NOTIFICATIONS_DELETE_MARKED_SUCCESS = 'NOTIFICATIONS_DELETE_MARKED_SUCCESS';
  12. export const NOTIFICATIONS_DELETE_MARKED_FAIL = 'NOTIFICATIONS_DELETE_MARKED_FAIL';
  13. export const NOTIFICATIONS_MARK_ALL_FOR_DELETE = 'NOTIFICATIONS_MARK_ALL_FOR_DELETE';
  14. export const NOTIFICATIONS_ENTER_CLEARING_MODE = 'NOTIFICATIONS_ENTER_CLEARING_MODE'; // arg: yes
  15. // Unmark notifications (when the cleaning mode is left)
  16. export const NOTIFICATIONS_UNMARK_ALL_FOR_DELETE = 'NOTIFICATIONS_UNMARK_ALL_FOR_DELETE';
  17. // Mark one for delete
  18. export const NOTIFICATION_MARK_FOR_DELETE = 'NOTIFICATION_MARK_FOR_DELETE';
  19. export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
  20. export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
  21. export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
  22. export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
  23. export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
  24. export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
  25. export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT';
  26. export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
  27. export const NOTIFICATIONS_SET_VISIBILITY = 'NOTIFICATIONS_SET_VISIBILITY';
  28. defineMessages({
  29. mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
  30. });
  31. const fetchRelatedRelationships = (dispatch, notifications) => {
  32. const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
  33. if (accountIds > 0) {
  34. dispatch(fetchRelationships(accountIds));
  35. }
  36. };
  37. export function updateNotifications(notification, intlMessages, intlLocale) {
  38. return (dispatch, getState) => {
  39. const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
  40. const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
  41. const filters = getFilters(getState(), { contextType: 'notifications' });
  42. let filtered = false;
  43. if (notification.type === 'mention') {
  44. const regex = regexFromFilters(filters);
  45. const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
  46. filtered = regex && regex.test(searchIndex);
  47. }
  48. dispatch({
  49. type: NOTIFICATIONS_UPDATE,
  50. notification,
  51. account: notification.account,
  52. status: notification.status,
  53. meta: (playSound && !filtered) ? { sound: 'boop' } : undefined,
  54. });
  55. fetchRelatedRelationships(dispatch, [notification]);
  56. // Desktop notifications
  57. if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
  58. const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
  59. const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
  60. const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
  61. notify.addEventListener('click', () => {
  62. window.focus();
  63. notify.close();
  64. });
  65. }
  66. };
  67. };
  68. const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
  69. const excludeTypesFromFilter = filter => {
  70. const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention']);
  71. return allTypes.filterNot(item => item === filter).toJS();
  72. };
  73. const noOp = () => {};
  74. export function expandNotifications({ maxId } = {}, done = noOp) {
  75. return (dispatch, getState) => {
  76. const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
  77. const notifications = getState().get('notifications');
  78. const isLoadingMore = !!maxId;
  79. if (notifications.get('isLoading')) {
  80. done();
  81. return;
  82. }
  83. const params = {
  84. max_id: maxId,
  85. exclude_types: activeFilter === 'all'
  86. ? excludeTypesFromSettings(getState())
  87. : excludeTypesFromFilter(activeFilter),
  88. };
  89. if (!maxId && notifications.get('items').size > 0) {
  90. params.since_id = notifications.getIn(['items', 0, 'id']);
  91. }
  92. dispatch(expandNotificationsRequest(isLoadingMore));
  93. api(getState).get('/api/v1/notifications', { params }).then(response => {
  94. const next = getLinks(response).refs.find(link => link.rel === 'next');
  95. dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
  96. fetchRelatedRelationships(dispatch, response.data);
  97. done();
  98. }).catch(error => {
  99. dispatch(expandNotificationsFail(error, isLoadingMore));
  100. done();
  101. });
  102. };
  103. };
  104. export function expandNotificationsRequest(isLoadingMore) {
  105. return {
  106. type: NOTIFICATIONS_EXPAND_REQUEST,
  107. skipLoading: !isLoadingMore,
  108. };
  109. };
  110. export function expandNotificationsSuccess(notifications, next, isLoadingMore) {
  111. return {
  112. type: NOTIFICATIONS_EXPAND_SUCCESS,
  113. notifications,
  114. accounts: notifications.map(item => item.account),
  115. statuses: notifications.map(item => item.status).filter(status => !!status),
  116. next,
  117. skipLoading: !isLoadingMore,
  118. };
  119. };
  120. export function expandNotificationsFail(error, isLoadingMore) {
  121. return {
  122. type: NOTIFICATIONS_EXPAND_FAIL,
  123. error,
  124. skipLoading: !isLoadingMore,
  125. };
  126. };
  127. export function clearNotifications() {
  128. return (dispatch, getState) => {
  129. dispatch({
  130. type: NOTIFICATIONS_CLEAR,
  131. });
  132. api(getState).post('/api/v1/notifications/clear');
  133. };
  134. };
  135. export function scrollTopNotifications(top) {
  136. return {
  137. type: NOTIFICATIONS_SCROLL_TOP,
  138. top,
  139. };
  140. };
  141. export function deleteMarkedNotifications() {
  142. return (dispatch, getState) => {
  143. dispatch(deleteMarkedNotificationsRequest());
  144. let ids = [];
  145. getState().getIn(['notifications', 'items']).forEach((n) => {
  146. if (n.get('markedForDelete')) {
  147. ids.push(n.get('id'));
  148. }
  149. });
  150. if (ids.length === 0) {
  151. return;
  152. }
  153. api(getState).delete(`/api/v1/notifications/destroy_multiple?ids[]=${ids.join('&ids[]=')}`).then(() => {
  154. dispatch(deleteMarkedNotificationsSuccess());
  155. }).catch(error => {
  156. console.error(error);
  157. dispatch(deleteMarkedNotificationsFail(error));
  158. });
  159. };
  160. };
  161. export function enterNotificationClearingMode(yes) {
  162. return {
  163. type: NOTIFICATIONS_ENTER_CLEARING_MODE,
  164. yes: yes,
  165. };
  166. };
  167. export function markAllNotifications(yes) {
  168. return {
  169. type: NOTIFICATIONS_MARK_ALL_FOR_DELETE,
  170. yes: yes, // true, false or null. null = invert
  171. };
  172. };
  173. export function deleteMarkedNotificationsRequest() {
  174. return {
  175. type: NOTIFICATIONS_DELETE_MARKED_REQUEST,
  176. };
  177. };
  178. export function deleteMarkedNotificationsFail() {
  179. return {
  180. type: NOTIFICATIONS_DELETE_MARKED_FAIL,
  181. };
  182. };
  183. export function markNotificationForDelete(id, yes) {
  184. return {
  185. type: NOTIFICATION_MARK_FOR_DELETE,
  186. id: id,
  187. yes: yes,
  188. };
  189. };
  190. export function deleteMarkedNotificationsSuccess() {
  191. return {
  192. type: NOTIFICATIONS_DELETE_MARKED_SUCCESS,
  193. };
  194. };
  195. export function mountNotifications() {
  196. return {
  197. type: NOTIFICATIONS_MOUNT,
  198. };
  199. };
  200. export function unmountNotifications() {
  201. return {
  202. type: NOTIFICATIONS_UNMOUNT,
  203. };
  204. };
  205. export function notificationsSetVisibility(visibility) {
  206. return {
  207. type: NOTIFICATIONS_SET_VISIBILITY,
  208. visibility: visibility,
  209. };
  210. };
  211. export function setFilter (filterType) {
  212. return dispatch => {
  213. dispatch({
  214. type: NOTIFICATIONS_FILTER_SET,
  215. path: ['notifications', 'quickFilter', 'active'],
  216. value: filterType,
  217. });
  218. dispatch(expandNotifications());
  219. };
  220. };