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.

259 lines
7.9 KiB

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