Convert notification requests actions and reducers to Typescript (#31866)
This commit is contained in:
		
							parent
							
								
									d5cf27e667
								
							
						
					
					
						commit
						c0eda832f3
					
				@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  apiClearNotifications,
 | 
			
		||||
  apiFetchNotifications,
 | 
			
		||||
  apiFetchNotificationGroups,
 | 
			
		||||
} from 'mastodon/api/notifications';
 | 
			
		||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
 | 
			
		||||
import type {
 | 
			
		||||
@ -71,7 +71,7 @@ function dispatchAssociatedRecords(
 | 
			
		||||
export const fetchNotifications = createDataLoadingThunk(
 | 
			
		||||
  'notificationGroups/fetch',
 | 
			
		||||
  async (_params, { getState }) =>
 | 
			
		||||
    apiFetchNotifications({ exclude_types: getExcludedTypes(getState()) }),
 | 
			
		||||
    apiFetchNotificationGroups({ exclude_types: getExcludedTypes(getState()) }),
 | 
			
		||||
  ({ notifications, accounts, statuses }, { dispatch }) => {
 | 
			
		||||
    dispatch(importFetchedAccounts(accounts));
 | 
			
		||||
    dispatch(importFetchedStatuses(statuses));
 | 
			
		||||
@ -92,7 +92,7 @@ export const fetchNotifications = createDataLoadingThunk(
 | 
			
		||||
export const fetchNotificationsGap = createDataLoadingThunk(
 | 
			
		||||
  'notificationGroups/fetchGap',
 | 
			
		||||
  async (params: { gap: NotificationGap }, { getState }) =>
 | 
			
		||||
    apiFetchNotifications({
 | 
			
		||||
    apiFetchNotificationGroups({
 | 
			
		||||
      max_id: params.gap.maxId,
 | 
			
		||||
      exclude_types: getExcludedTypes(getState()),
 | 
			
		||||
    }),
 | 
			
		||||
@ -108,7 +108,7 @@ export const fetchNotificationsGap = createDataLoadingThunk(
 | 
			
		||||
export const pollRecentNotifications = createDataLoadingThunk(
 | 
			
		||||
  'notificationGroups/pollRecentNotifications',
 | 
			
		||||
  async (_params, { getState }) => {
 | 
			
		||||
    return apiFetchNotifications({
 | 
			
		||||
    return apiFetchNotificationGroups({
 | 
			
		||||
      max_id: undefined,
 | 
			
		||||
      exclude_types: getExcludedTypes(getState()),
 | 
			
		||||
      // In slow mode, we don't want to include notifications that duplicate the already-displayed ones
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										234
									
								
								app/javascript/mastodon/actions/notification_requests.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								app/javascript/mastodon/actions/notification_requests.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,234 @@
 | 
			
		||||
import {
 | 
			
		||||
  apiFetchNotificationRequest,
 | 
			
		||||
  apiFetchNotificationRequests,
 | 
			
		||||
  apiFetchNotifications,
 | 
			
		||||
  apiAcceptNotificationRequest,
 | 
			
		||||
  apiDismissNotificationRequest,
 | 
			
		||||
  apiAcceptNotificationRequests,
 | 
			
		||||
  apiDismissNotificationRequests,
 | 
			
		||||
} from 'mastodon/api/notifications';
 | 
			
		||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
 | 
			
		||||
import type {
 | 
			
		||||
  ApiNotificationGroupJSON,
 | 
			
		||||
  ApiNotificationJSON,
 | 
			
		||||
} from 'mastodon/api_types/notifications';
 | 
			
		||||
import type { ApiStatusJSON } from 'mastodon/api_types/statuses';
 | 
			
		||||
import type { AppDispatch, RootState } from 'mastodon/store';
 | 
			
		||||
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
 | 
			
		||||
 | 
			
		||||
import { importFetchedAccounts, importFetchedStatuses } from './importer';
 | 
			
		||||
import { decreasePendingNotificationsCount } from './notification_policies';
 | 
			
		||||
 | 
			
		||||
// TODO: refactor with notification_groups
 | 
			
		||||
function dispatchAssociatedRecords(
 | 
			
		||||
  dispatch: AppDispatch,
 | 
			
		||||
  notifications: ApiNotificationGroupJSON[] | ApiNotificationJSON[],
 | 
			
		||||
) {
 | 
			
		||||
  const fetchedAccounts: ApiAccountJSON[] = [];
 | 
			
		||||
  const fetchedStatuses: ApiStatusJSON[] = [];
 | 
			
		||||
 | 
			
		||||
  notifications.forEach((notification) => {
 | 
			
		||||
    if (notification.type === 'admin.report') {
 | 
			
		||||
      fetchedAccounts.push(notification.report.target_account);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (notification.type === 'moderation_warning') {
 | 
			
		||||
      fetchedAccounts.push(notification.moderation_warning.target_account);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('status' in notification && notification.status) {
 | 
			
		||||
      fetchedStatuses.push(notification.status);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (fetchedAccounts.length > 0)
 | 
			
		||||
    dispatch(importFetchedAccounts(fetchedAccounts));
 | 
			
		||||
 | 
			
		||||
  if (fetchedStatuses.length > 0)
 | 
			
		||||
    dispatch(importFetchedStatuses(fetchedStatuses));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequests = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequests/fetch',
 | 
			
		||||
  async (_params, { getState }) => {
 | 
			
		||||
    let sinceId = undefined;
 | 
			
		||||
 | 
			
		||||
    if (getState().notificationRequests.items.length > 0) {
 | 
			
		||||
      sinceId = getState().notificationRequests.items[0]?.id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return apiFetchNotificationRequests({
 | 
			
		||||
      since_id: sinceId,
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  ({ requests, links }, { dispatch }) => {
 | 
			
		||||
    const next = links.refs.find((link) => link.rel === 'next');
 | 
			
		||||
 | 
			
		||||
    dispatch(importFetchedAccounts(requests.map((request) => request.account)));
 | 
			
		||||
 | 
			
		||||
    return { requests, next: next?.uri };
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    condition: (_params, { getState }) =>
 | 
			
		||||
      !getState().notificationRequests.isLoading,
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequest = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequest/fetch',
 | 
			
		||||
  async ({ id }: { id: string }) => apiFetchNotificationRequest(id),
 | 
			
		||||
  {
 | 
			
		||||
    condition: ({ id }, { getState }) =>
 | 
			
		||||
      !(
 | 
			
		||||
        getState().notificationRequests.current.item?.id === id ||
 | 
			
		||||
        getState().notificationRequests.current.isLoading
 | 
			
		||||
      ),
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const expandNotificationRequests = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequests/expand',
 | 
			
		||||
  async (_, { getState }) => {
 | 
			
		||||
    const nextUrl = getState().notificationRequests.next;
 | 
			
		||||
    if (!nextUrl) throw new Error('missing URL');
 | 
			
		||||
 | 
			
		||||
    return apiFetchNotificationRequests(undefined, nextUrl);
 | 
			
		||||
  },
 | 
			
		||||
  ({ requests, links }, { dispatch }) => {
 | 
			
		||||
    const next = links.refs.find((link) => link.rel === 'next');
 | 
			
		||||
 | 
			
		||||
    dispatch(importFetchedAccounts(requests.map((request) => request.account)));
 | 
			
		||||
 | 
			
		||||
    return { requests, next: next?.uri };
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    condition: (_, { getState }) =>
 | 
			
		||||
      !!getState().notificationRequests.next &&
 | 
			
		||||
      !getState().notificationRequests.isLoading,
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationsForRequest = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequest/fetchNotifications',
 | 
			
		||||
  async ({ accountId }: { accountId: string }, { getState }) => {
 | 
			
		||||
    const sinceId =
 | 
			
		||||
      // @ts-expect-error current.notifications.items is not yet typed
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      getState().notificationRequests.current.notifications.items[0]?.get(
 | 
			
		||||
        'id',
 | 
			
		||||
      ) as string | undefined;
 | 
			
		||||
 | 
			
		||||
    return apiFetchNotifications({
 | 
			
		||||
      since_id: sinceId,
 | 
			
		||||
      account_id: accountId,
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  ({ notifications, links }, { dispatch }) => {
 | 
			
		||||
    const next = links.refs.find((link) => link.rel === 'next');
 | 
			
		||||
 | 
			
		||||
    dispatchAssociatedRecords(dispatch, notifications);
 | 
			
		||||
 | 
			
		||||
    return { notifications, next: next?.uri };
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    condition: ({ accountId }, { getState }) => {
 | 
			
		||||
      const current = getState().notificationRequests.current;
 | 
			
		||||
      return !(
 | 
			
		||||
        current.item?.account_id === accountId &&
 | 
			
		||||
        current.notifications.isLoading
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const expandNotificationsForRequest = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequest/expandNotifications',
 | 
			
		||||
  async (_, { getState }) => {
 | 
			
		||||
    const nextUrl = getState().notificationRequests.current.notifications.next;
 | 
			
		||||
    if (!nextUrl) throw new Error('missing URL');
 | 
			
		||||
 | 
			
		||||
    return apiFetchNotifications(undefined, nextUrl);
 | 
			
		||||
  },
 | 
			
		||||
  ({ notifications, links }, { dispatch }) => {
 | 
			
		||||
    const next = links.refs.find((link) => link.rel === 'next');
 | 
			
		||||
 | 
			
		||||
    dispatchAssociatedRecords(dispatch, notifications);
 | 
			
		||||
 | 
			
		||||
    return { notifications, next: next?.uri };
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    condition: ({ accountId }: { accountId: string }, { getState }) => {
 | 
			
		||||
      const url = getState().notificationRequests.current.notifications.next;
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        !!url &&
 | 
			
		||||
        !getState().notificationRequests.current.notifications.isLoading &&
 | 
			
		||||
        getState().notificationRequests.current.item?.account_id === accountId
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const selectNotificationCountForRequest = (state: RootState, id: string) => {
 | 
			
		||||
  const requests = state.notificationRequests.items;
 | 
			
		||||
  const thisRequest = requests.find((request) => request.id === id);
 | 
			
		||||
  return thisRequest ? thisRequest.notifications_count : 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequest = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequest/accept',
 | 
			
		||||
  ({ id }: { id: string }) => apiAcceptNotificationRequest(id),
 | 
			
		||||
  (_data, { dispatch, getState, discardLoadData, actionArg: { id } }) => {
 | 
			
		||||
    const count = selectNotificationCountForRequest(getState(), id);
 | 
			
		||||
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
 | 
			
		||||
    // The payload is not used in any functions
 | 
			
		||||
    return discardLoadData;
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequest = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequest/dismiss',
 | 
			
		||||
  ({ id }: { id: string }) => apiDismissNotificationRequest(id),
 | 
			
		||||
  (_data, { dispatch, getState, discardLoadData, actionArg: { id } }) => {
 | 
			
		||||
    const count = selectNotificationCountForRequest(getState(), id);
 | 
			
		||||
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
 | 
			
		||||
    // The payload is not used in any functions
 | 
			
		||||
    return discardLoadData;
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequests = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequests/acceptBulk',
 | 
			
		||||
  ({ ids }: { ids: string[] }) => apiAcceptNotificationRequests(ids),
 | 
			
		||||
  (_data, { dispatch, getState, discardLoadData, actionArg: { ids } }) => {
 | 
			
		||||
    const count = ids.reduce(
 | 
			
		||||
      (count, id) => count + selectNotificationCountForRequest(getState(), id),
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
 | 
			
		||||
    // The payload is not used in any functions
 | 
			
		||||
    return discardLoadData;
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequests = createDataLoadingThunk(
 | 
			
		||||
  'notificationRequests/dismissBulk',
 | 
			
		||||
  ({ ids }: { ids: string[] }) => apiDismissNotificationRequests(ids),
 | 
			
		||||
  (_data, { dispatch, getState, discardLoadData, actionArg: { ids } }) => {
 | 
			
		||||
    const count = ids.reduce(
 | 
			
		||||
      (count, id) => count + selectNotificationCountForRequest(getState(), id),
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
 | 
			
		||||
    // The payload is not used in any functions
 | 
			
		||||
    return discardLoadData;
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
@ -18,7 +18,6 @@ import {
 | 
			
		||||
  importFetchedStatuses,
 | 
			
		||||
} from './importer';
 | 
			
		||||
import { submitMarkers } from './markers';
 | 
			
		||||
import { decreasePendingNotificationsCount } from './notification_policies';
 | 
			
		||||
import { notificationsUpdate } from "./notifications_typed";
 | 
			
		||||
import { register as registerPushNotifications } from './push_notifications';
 | 
			
		||||
import { saveSettings } from './settings';
 | 
			
		||||
@ -44,26 +43,6 @@ export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ';
 | 
			
		||||
export const NOTIFICATIONS_SET_BROWSER_SUPPORT    = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
 | 
			
		||||
export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUESTS_FETCH_REQUEST = 'NOTIFICATION_REQUESTS_FETCH_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_FETCH_FAIL    = 'NOTIFICATION_REQUESTS_FETCH_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUESTS_EXPAND_REQUEST = 'NOTIFICATION_REQUESTS_EXPAND_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_EXPAND_SUCCESS = 'NOTIFICATION_REQUESTS_EXPAND_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_EXPAND_FAIL    = 'NOTIFICATION_REQUESTS_EXPAND_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUEST_FETCH_REQUEST = 'NOTIFICATION_REQUEST_FETCH_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUEST_FETCH_SUCCESS = 'NOTIFICATION_REQUEST_FETCH_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUEST_FETCH_FAIL    = 'NOTIFICATION_REQUEST_FETCH_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUEST_ACCEPT_REQUEST = 'NOTIFICATION_REQUEST_ACCEPT_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUEST_ACCEPT_SUCCESS = 'NOTIFICATION_REQUEST_ACCEPT_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUEST_ACCEPT_FAIL    = 'NOTIFICATION_REQUEST_ACCEPT_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUEST_DISMISS_REQUEST = 'NOTIFICATION_REQUEST_DISMISS_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUEST_DISMISS_SUCCESS = 'NOTIFICATION_REQUEST_DISMISS_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUEST_DISMISS_FAIL    = 'NOTIFICATION_REQUEST_DISMISS_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATION_REQUESTS_ACCEPT_REQUEST = 'NOTIFICATION_REQUESTS_ACCEPT_REQUEST';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_ACCEPT_SUCCESS = 'NOTIFICATION_REQUESTS_ACCEPT_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_ACCEPT_FAIL    = 'NOTIFICATION_REQUESTS_ACCEPT_FAIL';
 | 
			
		||||
@ -72,14 +51,6 @@ export const NOTIFICATION_REQUESTS_DISMISS_REQUEST = 'NOTIFICATION_REQUESTS_DISM
 | 
			
		||||
export const NOTIFICATION_REQUESTS_DISMISS_SUCCESS = 'NOTIFICATION_REQUESTS_DISMISS_SUCCESS';
 | 
			
		||||
export const NOTIFICATION_REQUESTS_DISMISS_FAIL    = 'NOTIFICATION_REQUESTS_DISMISS_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_FETCH_REQUEST = 'NOTIFICATIONS_FOR_REQUEST_FETCH_REQUEST';
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_FETCH_SUCCESS = 'NOTIFICATIONS_FOR_REQUEST_FETCH_SUCCESS';
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_FETCH_FAIL    = 'NOTIFICATIONS_FOR_REQUEST_FETCH_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_EXPAND_REQUEST = 'NOTIFICATIONS_FOR_REQUEST_EXPAND_REQUEST';
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_EXPAND_SUCCESS = 'NOTIFICATIONS_FOR_REQUEST_EXPAND_SUCCESS';
 | 
			
		||||
export const NOTIFICATIONS_FOR_REQUEST_EXPAND_FAIL    = 'NOTIFICATIONS_FOR_REQUEST_EXPAND_FAIL';
 | 
			
		||||
 | 
			
		||||
defineMessages({
 | 
			
		||||
  mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
 | 
			
		||||
  group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
 | 
			
		||||
@ -93,12 +64,6 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const selectNotificationCountForRequest = (state, id) => {
 | 
			
		||||
  const requests = state.getIn(['notificationRequests', 'items']);
 | 
			
		||||
  const thisRequest = requests.find(request => request.get('id') === id);
 | 
			
		||||
  return thisRequest ? thisRequest.get('notifications_count') : 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const loadPending = () => ({
 | 
			
		||||
  type: NOTIFICATIONS_LOAD_PENDING,
 | 
			
		||||
});
 | 
			
		||||
@ -343,296 +308,3 @@ export function setBrowserPermission (value) {
 | 
			
		||||
    value,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequests = () => (dispatch, getState) => {
 | 
			
		||||
  const params = {};
 | 
			
		||||
 | 
			
		||||
  if (getState().getIn(['notificationRequests', 'isLoading'])) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (getState().getIn(['notificationRequests', 'items'])?.size > 0) {
 | 
			
		||||
    params.since_id = getState().getIn(['notificationRequests', 'items', 0, 'id']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dispatch(fetchNotificationRequestsRequest());
 | 
			
		||||
 | 
			
		||||
  api().get('/api/v1/notifications/requests', { params }).then(response => {
 | 
			
		||||
    const next = getLinks(response).refs.find(link => link.rel === 'next');
 | 
			
		||||
    dispatch(importFetchedAccounts(response.data.map(x => x.account)));
 | 
			
		||||
    dispatch(fetchNotificationRequestsSuccess(response.data, next ? next.uri : null));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(fetchNotificationRequestsFail(err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestsRequest = () => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_FETCH_REQUEST,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestsSuccess = (requests, next) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_FETCH_SUCCESS,
 | 
			
		||||
  requests,
 | 
			
		||||
  next,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestsFail = error => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_FETCH_FAIL,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationRequests = () => (dispatch, getState) => {
 | 
			
		||||
  const url = getState().getIn(['notificationRequests', 'next']);
 | 
			
		||||
 | 
			
		||||
  if (!url || getState().getIn(['notificationRequests', 'isLoading'])) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dispatch(expandNotificationRequestsRequest());
 | 
			
		||||
 | 
			
		||||
  api().get(url).then(response => {
 | 
			
		||||
    const next = getLinks(response).refs.find(link => link.rel === 'next');
 | 
			
		||||
    dispatch(importFetchedAccounts(response.data.map(x => x.account)));
 | 
			
		||||
    dispatch(expandNotificationRequestsSuccess(response.data, next?.uri));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(expandNotificationRequestsFail(err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const expandNotificationRequestsRequest = () => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_EXPAND_REQUEST,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationRequestsSuccess = (requests, next) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_EXPAND_SUCCESS,
 | 
			
		||||
  requests,
 | 
			
		||||
  next,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationRequestsFail = error => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_EXPAND_FAIL,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequest = id => (dispatch, getState) => {
 | 
			
		||||
  const current = getState().getIn(['notificationRequests', 'current']);
 | 
			
		||||
 | 
			
		||||
  if (current.getIn(['item', 'id']) === id || current.get('isLoading')) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dispatch(fetchNotificationRequestRequest(id));
 | 
			
		||||
 | 
			
		||||
  api().get(`/api/v1/notifications/requests/${id}`).then(({ data }) => {
 | 
			
		||||
    dispatch(fetchNotificationRequestSuccess(data));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(fetchNotificationRequestFail(id, err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestRequest = id => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_FETCH_REQUEST,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestSuccess = request => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_FETCH_SUCCESS,
 | 
			
		||||
  request,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationRequestFail = (id, error) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_FETCH_FAIL,
 | 
			
		||||
  id,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequest = (id) => (dispatch, getState) => {
 | 
			
		||||
  const count = selectNotificationCountForRequest(getState(), id);
 | 
			
		||||
  dispatch(acceptNotificationRequestRequest(id));
 | 
			
		||||
 | 
			
		||||
  api().post(`/api/v1/notifications/requests/${id}/accept`).then(() => {
 | 
			
		||||
    dispatch(acceptNotificationRequestSuccess(id));
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(acceptNotificationRequestFail(id, err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestRequest = id => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_ACCEPT_REQUEST,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestSuccess = id => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_ACCEPT_SUCCESS,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestFail = (id, error) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_ACCEPT_FAIL,
 | 
			
		||||
  id,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequest = (id) => (dispatch, getState) => {
 | 
			
		||||
  const count = selectNotificationCountForRequest(getState(), id);
 | 
			
		||||
  dispatch(dismissNotificationRequestRequest(id));
 | 
			
		||||
 | 
			
		||||
  api().post(`/api/v1/notifications/requests/${id}/dismiss`).then(() =>{
 | 
			
		||||
    dispatch(dismissNotificationRequestSuccess(id));
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(dismissNotificationRequestFail(id, err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestRequest = id => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_DISMISS_REQUEST,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestSuccess = id => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_DISMISS_SUCCESS,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestFail = (id, error) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUEST_DISMISS_FAIL,
 | 
			
		||||
  id,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequests = (ids) => (dispatch, getState) => {
 | 
			
		||||
  const count = ids.reduce((count, id) => count + selectNotificationCountForRequest(getState(), id), 0);
 | 
			
		||||
  dispatch(acceptNotificationRequestsRequest(ids));
 | 
			
		||||
 | 
			
		||||
  api().post(`/api/v1/notifications/requests/accept`, { id: ids }).then(() => {
 | 
			
		||||
    dispatch(acceptNotificationRequestsSuccess(ids));
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(acceptNotificationRequestFail(ids, err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestsRequest = ids => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_ACCEPT_REQUEST,
 | 
			
		||||
  ids,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestsSuccess = ids => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_ACCEPT_SUCCESS,
 | 
			
		||||
  ids,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const acceptNotificationRequestsFail = (ids, error) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_ACCEPT_FAIL,
 | 
			
		||||
  ids,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequests = (ids) => (dispatch, getState) => {
 | 
			
		||||
  const count = ids.reduce((count, id) => count + selectNotificationCountForRequest(getState(), id), 0);
 | 
			
		||||
  dispatch(acceptNotificationRequestsRequest(ids));
 | 
			
		||||
 | 
			
		||||
  api().post(`/api/v1/notifications/requests/dismiss`, { id: ids }).then(() => {
 | 
			
		||||
    dispatch(dismissNotificationRequestsSuccess(ids));
 | 
			
		||||
    dispatch(decreasePendingNotificationsCount(count));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(dismissNotificationRequestFail(ids, err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestsRequest = ids => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_DISMISS_REQUEST,
 | 
			
		||||
  ids,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestsSuccess = ids => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_DISMISS_SUCCESS,
 | 
			
		||||
  ids,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const dismissNotificationRequestsFail = (ids, error) => ({
 | 
			
		||||
  type: NOTIFICATION_REQUESTS_DISMISS_FAIL,
 | 
			
		||||
  ids,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationsForRequest = accountId => (dispatch, getState) => {
 | 
			
		||||
  const current = getState().getIn(['notificationRequests', 'current']);
 | 
			
		||||
  const params = { account_id: accountId };
 | 
			
		||||
 | 
			
		||||
  if (current.getIn(['item', 'account']) === accountId) {
 | 
			
		||||
    if (current.getIn(['notifications', 'isLoading'])) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (current.getIn(['notifications', 'items'])?.size > 0) {
 | 
			
		||||
      params.since_id = current.getIn(['notifications', 'items', 0, 'id']);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dispatch(fetchNotificationsForRequestRequest());
 | 
			
		||||
 | 
			
		||||
  api().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(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account)));
 | 
			
		||||
 | 
			
		||||
    dispatch(fetchNotificationsForRequestSuccess(response.data, next?.uri));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(fetchNotificationsForRequestFail(err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationsForRequestRequest = () => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_FETCH_REQUEST,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationsForRequestSuccess = (notifications, next) => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_FETCH_SUCCESS,
 | 
			
		||||
  notifications,
 | 
			
		||||
  next,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchNotificationsForRequestFail = (error) => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_FETCH_FAIL,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationsForRequest = () => (dispatch, getState) => {
 | 
			
		||||
  const url = getState().getIn(['notificationRequests', 'current', 'notifications', 'next']);
 | 
			
		||||
 | 
			
		||||
  if (!url || getState().getIn(['notificationRequests', 'current', 'notifications', 'isLoading'])) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dispatch(expandNotificationsForRequestRequest());
 | 
			
		||||
 | 
			
		||||
  api().get(url).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(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account)));
 | 
			
		||||
 | 
			
		||||
    dispatch(expandNotificationsForRequestSuccess(response.data, next?.uri));
 | 
			
		||||
  }).catch(err => {
 | 
			
		||||
    dispatch(expandNotificationsForRequestFail(err));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const expandNotificationsForRequestRequest = () => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_EXPAND_REQUEST,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationsForRequestSuccess = (notifications, next) => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_EXPAND_SUCCESS,
 | 
			
		||||
  notifications,
 | 
			
		||||
  next,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const expandNotificationsForRequestFail = (error) => ({
 | 
			
		||||
  type: NOTIFICATIONS_FOR_REQUEST_EXPAND_FAIL,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,36 @@
 | 
			
		||||
import api, { apiRequest, getLinks } from 'mastodon/api';
 | 
			
		||||
import type { ApiNotificationGroupsResultJSON } from 'mastodon/api_types/notifications';
 | 
			
		||||
import api, {
 | 
			
		||||
  apiRequest,
 | 
			
		||||
  getLinks,
 | 
			
		||||
  apiRequestGet,
 | 
			
		||||
  apiRequestPost,
 | 
			
		||||
} from 'mastodon/api';
 | 
			
		||||
import type {
 | 
			
		||||
  ApiNotificationGroupsResultJSON,
 | 
			
		||||
  ApiNotificationRequestJSON,
 | 
			
		||||
  ApiNotificationJSON,
 | 
			
		||||
} from 'mastodon/api_types/notifications';
 | 
			
		||||
 | 
			
		||||
export const apiFetchNotifications = async (params?: {
 | 
			
		||||
export const apiFetchNotifications = async (
 | 
			
		||||
  params?: {
 | 
			
		||||
    account_id?: string;
 | 
			
		||||
    since_id?: string;
 | 
			
		||||
  },
 | 
			
		||||
  url?: string,
 | 
			
		||||
) => {
 | 
			
		||||
  const response = await api().request<ApiNotificationJSON[]>({
 | 
			
		||||
    method: 'GET',
 | 
			
		||||
    url: url ?? '/api/v1/notifications',
 | 
			
		||||
    params,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    notifications: response.data,
 | 
			
		||||
    links: getLinks(response),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiFetchNotificationGroups = async (params?: {
 | 
			
		||||
  url?: string;
 | 
			
		||||
  exclude_types?: string[];
 | 
			
		||||
  max_id?: string;
 | 
			
		||||
  since_id?: string;
 | 
			
		||||
@ -24,3 +53,43 @@ export const apiFetchNotifications = async (params?: {
 | 
			
		||||
 | 
			
		||||
export const apiClearNotifications = () =>
 | 
			
		||||
  apiRequest<undefined>('POST', 'v1/notifications/clear');
 | 
			
		||||
 | 
			
		||||
export const apiFetchNotificationRequests = async (
 | 
			
		||||
  params?: {
 | 
			
		||||
    since_id?: string;
 | 
			
		||||
  },
 | 
			
		||||
  url?: string,
 | 
			
		||||
) => {
 | 
			
		||||
  const response = await api().request<ApiNotificationRequestJSON[]>({
 | 
			
		||||
    method: 'GET',
 | 
			
		||||
    url: url ?? '/api/v1/notifications/requests',
 | 
			
		||||
    params,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    requests: response.data,
 | 
			
		||||
    links: getLinks(response),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiFetchNotificationRequest = async (id: string) => {
 | 
			
		||||
  return apiRequestGet<ApiNotificationRequestJSON>(
 | 
			
		||||
    `v1/notifications/requests/${id}`,
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiAcceptNotificationRequest = async (id: string) => {
 | 
			
		||||
  return apiRequestPost(`v1/notifications/requests/${id}/accept`);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiDismissNotificationRequest = async (id: string) => {
 | 
			
		||||
  return apiRequestPost(`v1/notifications/requests/${id}/dismiss`);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiAcceptNotificationRequests = async (id: string[]) => {
 | 
			
		||||
  return apiRequestPost('v1/notifications/requests/accept', { id });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiDismissNotificationRequests = async (id: string[]) => {
 | 
			
		||||
  return apiRequestPost('v1/notifications/dismiss/dismiss', { id });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -149,3 +149,12 @@ export interface ApiNotificationGroupsResultJSON {
 | 
			
		||||
  statuses: ApiStatusJSON[];
 | 
			
		||||
  notification_groups: ApiNotificationGroupJSON[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ApiNotificationRequestJSON {
 | 
			
		||||
  id: string;
 | 
			
		||||
  created_at: string;
 | 
			
		||||
  updated_at: string;
 | 
			
		||||
  notifications_count: string;
 | 
			
		||||
  account: ApiAccountJSON;
 | 
			
		||||
  last_status?: ApiStatusJSON;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
 | 
			
		||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
 | 
			
		||||
import { initBlockModal } from 'mastodon/actions/blocks';
 | 
			
		||||
import { initMuteModal } from 'mastodon/actions/mutes';
 | 
			
		||||
import { acceptNotificationRequest, dismissNotificationRequest } from 'mastodon/actions/notifications';
 | 
			
		||||
import { acceptNotificationRequest, dismissNotificationRequest } from 'mastodon/actions/notification_requests';
 | 
			
		||||
import { initReport } from 'mastodon/actions/reports';
 | 
			
		||||
import { Avatar } from 'mastodon/components/avatar';
 | 
			
		||||
import { CheckBox } from 'mastodon/components/check_box';
 | 
			
		||||
@ -40,11 +40,11 @@ export const NotificationRequest = ({ id, accountId, notificationsCount, checked
 | 
			
		||||
  const { push: historyPush } = useHistory();
 | 
			
		||||
 | 
			
		||||
  const handleDismiss = useCallback(() => {
 | 
			
		||||
    dispatch(dismissNotificationRequest(id));
 | 
			
		||||
    dispatch(dismissNotificationRequest({ id }));
 | 
			
		||||
  }, [dispatch, id]);
 | 
			
		||||
 | 
			
		||||
  const handleAccept = useCallback(() => {
 | 
			
		||||
    dispatch(acceptNotificationRequest(id));
 | 
			
		||||
    dispatch(acceptNotificationRequest({ id }));
 | 
			
		||||
  }, [dispatch, id]);
 | 
			
		||||
 | 
			
		||||
  const handleMute = useCallback(() => {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,13 @@ import { useSelector, useDispatch } from 'react-redux';
 | 
			
		||||
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
 | 
			
		||||
import DoneIcon from '@/material-icons/400-24px/done.svg?react';
 | 
			
		||||
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
 | 
			
		||||
import { fetchNotificationRequest, fetchNotificationsForRequest, expandNotificationsForRequest, acceptNotificationRequest, dismissNotificationRequest } from 'mastodon/actions/notifications';
 | 
			
		||||
import {
 | 
			
		||||
  fetchNotificationRequest,
 | 
			
		||||
  fetchNotificationsForRequest,
 | 
			
		||||
  expandNotificationsForRequest,
 | 
			
		||||
  acceptNotificationRequest,
 | 
			
		||||
  dismissNotificationRequest,
 | 
			
		||||
} from 'mastodon/actions/notification_requests';
 | 
			
		||||
import Column from 'mastodon/components/column';
 | 
			
		||||
import ColumnHeader from 'mastodon/components/column_header';
 | 
			
		||||
import { IconButton } from 'mastodon/components/icon_button';
 | 
			
		||||
@ -44,28 +50,28 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
 | 
			
		||||
  const columnRef = useRef();
 | 
			
		||||
  const intl = useIntl();
 | 
			
		||||
  const dispatch = useDispatch();
 | 
			
		||||
  const notificationRequest = useSelector(state => state.getIn(['notificationRequests', 'current', 'item', 'id']) === id ? state.getIn(['notificationRequests', 'current', 'item']) : null);
 | 
			
		||||
  const accountId = notificationRequest?.get('account');
 | 
			
		||||
  const notificationRequest = useSelector(state => state.notificationRequests.current.item?.id === id ? state.notificationRequests.current.item : null);
 | 
			
		||||
  const accountId = notificationRequest?.account_id;
 | 
			
		||||
  const account = useSelector(state => state.getIn(['accounts', accountId]));
 | 
			
		||||
  const notifications = useSelector(state => state.getIn(['notificationRequests', 'current', 'notifications', 'items']));
 | 
			
		||||
  const isLoading = useSelector(state => state.getIn(['notificationRequests', 'current', 'notifications', 'isLoading']));
 | 
			
		||||
  const hasMore = useSelector(state => !!state.getIn(['notificationRequests', 'current', 'notifications', 'next']));
 | 
			
		||||
  const removed = useSelector(state => state.getIn(['notificationRequests', 'current', 'removed']));
 | 
			
		||||
  const notifications = useSelector(state => state.notificationRequests.current.notifications.items);
 | 
			
		||||
  const isLoading = useSelector(state => state.notificationRequests.current.notifications.isLoading);
 | 
			
		||||
  const hasMore = useSelector(state => !!state.notificationRequests.current.notifications.next);
 | 
			
		||||
  const removed = useSelector(state => state.notificationRequests.current.removed);
 | 
			
		||||
 | 
			
		||||
  const handleHeaderClick = useCallback(() => {
 | 
			
		||||
    columnRef.current?.scrollTop();
 | 
			
		||||
  }, [columnRef]);
 | 
			
		||||
 | 
			
		||||
  const handleLoadMore = useCallback(() => {
 | 
			
		||||
    dispatch(expandNotificationsForRequest());
 | 
			
		||||
  }, [dispatch]);
 | 
			
		||||
    dispatch(expandNotificationsForRequest({ accountId }));
 | 
			
		||||
  }, [dispatch, accountId]);
 | 
			
		||||
 | 
			
		||||
  const handleDismiss = useCallback(() => {
 | 
			
		||||
    dispatch(dismissNotificationRequest(id));
 | 
			
		||||
    dispatch(dismissNotificationRequest({ id }));
 | 
			
		||||
  }, [dispatch, id]);
 | 
			
		||||
 | 
			
		||||
  const handleAccept = useCallback(() => {
 | 
			
		||||
    dispatch(acceptNotificationRequest(id));
 | 
			
		||||
    dispatch(acceptNotificationRequest({ id }));
 | 
			
		||||
  }, [dispatch, id]);
 | 
			
		||||
 | 
			
		||||
  const handleMoveUp = useCallback(id => {
 | 
			
		||||
@ -79,12 +85,12 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
 | 
			
		||||
  }, [columnRef, notifications]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    dispatch(fetchNotificationRequest(id));
 | 
			
		||||
    dispatch(fetchNotificationRequest({ id }));
 | 
			
		||||
  }, [dispatch, id]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (accountId) {
 | 
			
		||||
      dispatch(fetchNotificationsForRequest(accountId));
 | 
			
		||||
      dispatch(fetchNotificationsForRequest({ accountId }));
 | 
			
		||||
    }
 | 
			
		||||
  }, [dispatch, accountId]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,12 @@ import ArrowDropDownIcon from '@/material-icons/400-24px/arrow_drop_down.svg?rea
 | 
			
		||||
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
 | 
			
		||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
 | 
			
		||||
import { openModal } from 'mastodon/actions/modal';
 | 
			
		||||
import { fetchNotificationRequests, expandNotificationRequests, acceptNotificationRequests, dismissNotificationRequests } from 'mastodon/actions/notifications';
 | 
			
		||||
import {
 | 
			
		||||
  fetchNotificationRequests,
 | 
			
		||||
  expandNotificationRequests,
 | 
			
		||||
  acceptNotificationRequests,
 | 
			
		||||
  dismissNotificationRequests,
 | 
			
		||||
} from 'mastodon/actions/notification_requests';
 | 
			
		||||
import { changeSetting } from 'mastodon/actions/settings';
 | 
			
		||||
import { CheckBox } from 'mastodon/components/check_box';
 | 
			
		||||
import Column from 'mastodon/components/column';
 | 
			
		||||
@ -84,7 +89,7 @@ const SelectRow = ({selectAllChecked, toggleSelectAll, selectedItems, selectionM
 | 
			
		||||
        message: intl.formatMessage(messages.confirmAcceptMultipleMessage, { count: selectedItems.length }),
 | 
			
		||||
        confirm: intl.formatMessage(messages.confirmAcceptMultipleButton, { count: selectedItems.length}),
 | 
			
		||||
        onConfirm: () =>
 | 
			
		||||
          dispatch(acceptNotificationRequests(selectedItems)),
 | 
			
		||||
          dispatch(acceptNotificationRequests({ ids: selectedItems })),
 | 
			
		||||
      },
 | 
			
		||||
    }));
 | 
			
		||||
  }, [dispatch, intl, selectedItems]);
 | 
			
		||||
@ -97,7 +102,7 @@ const SelectRow = ({selectAllChecked, toggleSelectAll, selectedItems, selectionM
 | 
			
		||||
        message: intl.formatMessage(messages.confirmDismissMultipleMessage, { count: selectedItems.length }),
 | 
			
		||||
        confirm: intl.formatMessage(messages.confirmDismissMultipleButton, { count: selectedItems.length}),
 | 
			
		||||
        onConfirm: () =>
 | 
			
		||||
          dispatch(dismissNotificationRequests(selectedItems)),
 | 
			
		||||
          dispatch(dismissNotificationRequests({ ids: selectedItems })),
 | 
			
		||||
      },
 | 
			
		||||
    }));
 | 
			
		||||
  }, [dispatch, intl, selectedItems]);
 | 
			
		||||
@ -161,9 +166,9 @@ export const NotificationRequests = ({ multiColumn }) => {
 | 
			
		||||
  const columnRef = useRef();
 | 
			
		||||
  const intl = useIntl();
 | 
			
		||||
  const dispatch = useDispatch();
 | 
			
		||||
  const isLoading = useSelector(state => state.getIn(['notificationRequests', 'isLoading']));
 | 
			
		||||
  const notificationRequests = useSelector(state => state.getIn(['notificationRequests', 'items']));
 | 
			
		||||
  const hasMore = useSelector(state => !!state.getIn(['notificationRequests', 'next']));
 | 
			
		||||
  const isLoading = useSelector(state => state.notificationRequests.isLoading);
 | 
			
		||||
  const notificationRequests = useSelector(state => state.notificationRequests.items);
 | 
			
		||||
  const hasMore = useSelector(state => !!state.notificationRequests.next);
 | 
			
		||||
 | 
			
		||||
  const [selectionMode, setSelectionMode] = useState(false);
 | 
			
		||||
  const [checkedRequestIds, setCheckedRequestIds] = useState([]);
 | 
			
		||||
@ -182,7 +187,7 @@ export const NotificationRequests = ({ multiColumn }) => {
 | 
			
		||||
      else
 | 
			
		||||
        ids.push(id);
 | 
			
		||||
 | 
			
		||||
      setSelectAllChecked(ids.length === notificationRequests.size);
 | 
			
		||||
      setSelectAllChecked(ids.length === notificationRequests.length);
 | 
			
		||||
 | 
			
		||||
      return [...ids];
 | 
			
		||||
    });
 | 
			
		||||
@ -193,7 +198,7 @@ export const NotificationRequests = ({ multiColumn }) => {
 | 
			
		||||
      if(checked)
 | 
			
		||||
        setCheckedRequestIds([]);
 | 
			
		||||
      else
 | 
			
		||||
        setCheckedRequestIds(notificationRequests.map(request => request.get('id')).toArray());
 | 
			
		||||
        setCheckedRequestIds(notificationRequests.map(request => request.id));
 | 
			
		||||
 | 
			
		||||
      return !checked;
 | 
			
		||||
    });
 | 
			
		||||
@ -217,7 +222,7 @@ export const NotificationRequests = ({ multiColumn }) => {
 | 
			
		||||
        multiColumn={multiColumn}
 | 
			
		||||
        showBackButton
 | 
			
		||||
        appendContent={
 | 
			
		||||
          notificationRequests.size > 0 && (
 | 
			
		||||
          notificationRequests.length > 0 && (
 | 
			
		||||
            <SelectRow selectionMode={selectionMode} setSelectionMode={setSelectionMode} selectAllChecked={selectAllChecked} toggleSelectAll={toggleSelectAll} selectedItems={checkedRequestIds} />
 | 
			
		||||
          )}
 | 
			
		||||
      >
 | 
			
		||||
@ -236,12 +241,12 @@ export const NotificationRequests = ({ multiColumn }) => {
 | 
			
		||||
      >
 | 
			
		||||
        {notificationRequests.map(request => (
 | 
			
		||||
          <NotificationRequest
 | 
			
		||||
            key={request.get('id')}
 | 
			
		||||
            id={request.get('id')}
 | 
			
		||||
            accountId={request.get('account')}
 | 
			
		||||
            notificationsCount={request.get('notifications_count')}
 | 
			
		||||
            key={request.id}
 | 
			
		||||
            id={request.id}
 | 
			
		||||
            accountId={request.account_id}
 | 
			
		||||
            notificationsCount={request.notifications_count}
 | 
			
		||||
            showCheckbox={selectionMode}
 | 
			
		||||
            checked={checkedRequestIds.includes(request.get('id'))}
 | 
			
		||||
            checked={checkedRequestIds.includes(request.id)}
 | 
			
		||||
            toggleCheck={handleCheck}
 | 
			
		||||
          />
 | 
			
		||||
        ))}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								app/javascript/mastodon/models/notification_request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/javascript/mastodon/models/notification_request.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
import type { ApiNotificationRequestJSON } from 'mastodon/api_types/notifications';
 | 
			
		||||
 | 
			
		||||
export interface NotificationRequest
 | 
			
		||||
  extends Omit<ApiNotificationRequestJSON, 'account' | 'notifications_count'> {
 | 
			
		||||
  account_id: string;
 | 
			
		||||
  notifications_count: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function createNotificationRequestFromJSON(
 | 
			
		||||
  requestJSON: ApiNotificationRequestJSON,
 | 
			
		||||
): NotificationRequest {
 | 
			
		||||
  const { account, notifications_count, ...request } = requestJSON;
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    account_id: account.id,
 | 
			
		||||
    notifications_count: +notifications_count,
 | 
			
		||||
    ...request,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -1,114 +0,0 @@
 | 
			
		||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
 | 
			
		||||
 | 
			
		||||
import { blockAccountSuccess, muteAccountSuccess } from 'mastodon/actions/accounts';
 | 
			
		||||
import {
 | 
			
		||||
  NOTIFICATION_REQUESTS_EXPAND_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUESTS_EXPAND_SUCCESS,
 | 
			
		||||
  NOTIFICATION_REQUESTS_EXPAND_FAIL,
 | 
			
		||||
  NOTIFICATION_REQUESTS_FETCH_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUESTS_FETCH_SUCCESS,
 | 
			
		||||
  NOTIFICATION_REQUESTS_FETCH_FAIL,
 | 
			
		||||
  NOTIFICATION_REQUEST_FETCH_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUEST_FETCH_SUCCESS,
 | 
			
		||||
  NOTIFICATION_REQUEST_FETCH_FAIL,
 | 
			
		||||
  NOTIFICATION_REQUEST_ACCEPT_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUEST_DISMISS_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUESTS_ACCEPT_REQUEST,
 | 
			
		||||
  NOTIFICATION_REQUESTS_DISMISS_REQUEST,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_FETCH_REQUEST,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_FETCH_SUCCESS,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_FETCH_FAIL,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_EXPAND_REQUEST,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_EXPAND_SUCCESS,
 | 
			
		||||
  NOTIFICATIONS_FOR_REQUEST_EXPAND_FAIL,
 | 
			
		||||
} from 'mastodon/actions/notifications';
 | 
			
		||||
 | 
			
		||||
import { notificationToMap } from './notifications';
 | 
			
		||||
 | 
			
		||||
const initialState = ImmutableMap({
 | 
			
		||||
  items: ImmutableList(),
 | 
			
		||||
  isLoading: false,
 | 
			
		||||
  next: null,
 | 
			
		||||
  current: ImmutableMap({
 | 
			
		||||
    isLoading: false,
 | 
			
		||||
    item: null,
 | 
			
		||||
    removed: false,
 | 
			
		||||
    notifications: ImmutableMap({
 | 
			
		||||
      items: ImmutableList(),
 | 
			
		||||
      isLoading: false,
 | 
			
		||||
      next: null,
 | 
			
		||||
    }),
 | 
			
		||||
  }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const normalizeRequest = request => fromJS({
 | 
			
		||||
  ...request,
 | 
			
		||||
  account: request.account.id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const removeRequest = (state, id) => {
 | 
			
		||||
  if (state.getIn(['current', 'item', 'id']) === id) {
 | 
			
		||||
    state = state.setIn(['current', 'removed'], true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return state.update('items', list => list.filterNot(item => item.get('id') === id));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const removeRequestByAccount = (state, account_id) => {
 | 
			
		||||
  if (state.getIn(['current', 'item', 'account']) === account_id) {
 | 
			
		||||
    state = state.setIn(['current', 'removed'], true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return state.update('items', list => list.filterNot(item => item.get('account') === account_id));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const notificationRequestsReducer = (state = initialState, action) => {
 | 
			
		||||
  switch(action.type) {
 | 
			
		||||
  case NOTIFICATION_REQUESTS_FETCH_SUCCESS:
 | 
			
		||||
    return state.withMutations(map => {
 | 
			
		||||
      map.update('items', list => ImmutableList(action.requests.map(normalizeRequest)).concat(list));
 | 
			
		||||
      map.set('isLoading', false);
 | 
			
		||||
      map.update('next', next => next ?? action.next);
 | 
			
		||||
    });
 | 
			
		||||
  case NOTIFICATION_REQUESTS_EXPAND_SUCCESS:
 | 
			
		||||
    return state.withMutations(map => {
 | 
			
		||||
      map.update('items', list => list.concat(ImmutableList(action.requests.map(normalizeRequest))));
 | 
			
		||||
      map.set('isLoading', false);
 | 
			
		||||
      map.set('next', action.next);
 | 
			
		||||
    });
 | 
			
		||||
  case NOTIFICATION_REQUESTS_EXPAND_REQUEST:
 | 
			
		||||
  case NOTIFICATION_REQUESTS_FETCH_REQUEST:
 | 
			
		||||
    return state.set('isLoading', true);
 | 
			
		||||
  case NOTIFICATION_REQUESTS_EXPAND_FAIL:
 | 
			
		||||
  case NOTIFICATION_REQUESTS_FETCH_FAIL:
 | 
			
		||||
    return state.set('isLoading', false);
 | 
			
		||||
  case NOTIFICATION_REQUEST_ACCEPT_REQUEST:
 | 
			
		||||
  case NOTIFICATION_REQUEST_DISMISS_REQUEST:
 | 
			
		||||
    return removeRequest(state, action.id);
 | 
			
		||||
  case NOTIFICATION_REQUESTS_ACCEPT_REQUEST:
 | 
			
		||||
  case NOTIFICATION_REQUESTS_DISMISS_REQUEST:
 | 
			
		||||
    return action.ids.reduce((state, id) => removeRequest(state, id), state);
 | 
			
		||||
  case blockAccountSuccess.type:
 | 
			
		||||
    return removeRequestByAccount(state, action.payload.relationship.id);
 | 
			
		||||
  case muteAccountSuccess.type:
 | 
			
		||||
    return action.payload.relationship.muting_notifications ? removeRequestByAccount(state, action.payload.relationship.id) : state;
 | 
			
		||||
  case NOTIFICATION_REQUEST_FETCH_REQUEST:
 | 
			
		||||
    return state.set('current', initialState.get('current').set('isLoading', true));
 | 
			
		||||
  case NOTIFICATION_REQUEST_FETCH_SUCCESS:
 | 
			
		||||
    return state.update('current', map => map.set('isLoading', false).set('item', normalizeRequest(action.request)));
 | 
			
		||||
  case NOTIFICATION_REQUEST_FETCH_FAIL:
 | 
			
		||||
    return state.update('current', map => map.set('isLoading', false));
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_FETCH_REQUEST:
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_EXPAND_REQUEST:
 | 
			
		||||
    return state.setIn(['current', 'notifications', 'isLoading'], true);
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_FETCH_SUCCESS:
 | 
			
		||||
    return state.updateIn(['current', 'notifications'], map => map.set('isLoading', false).update('items', list => ImmutableList(action.notifications.map(notificationToMap)).concat(list)).update('next', next => next ?? action.next));
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_EXPAND_SUCCESS:
 | 
			
		||||
    return state.updateIn(['current', 'notifications'], map => map.set('isLoading', false).update('items', list => list.concat(ImmutableList(action.notifications.map(notificationToMap)))).set('next', action.next));
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_FETCH_FAIL:
 | 
			
		||||
  case NOTIFICATIONS_FOR_REQUEST_EXPAND_FAIL:
 | 
			
		||||
    return state.setIn(['current', 'notifications', 'isLoading'], false);
 | 
			
		||||
  default:
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										182
									
								
								app/javascript/mastodon/reducers/notification_requests.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								app/javascript/mastodon/reducers/notification_requests.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,182 @@
 | 
			
		||||
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  blockAccountSuccess,
 | 
			
		||||
  muteAccountSuccess,
 | 
			
		||||
} from 'mastodon/actions/accounts';
 | 
			
		||||
import {
 | 
			
		||||
  fetchNotificationRequests,
 | 
			
		||||
  expandNotificationRequests,
 | 
			
		||||
  fetchNotificationRequest,
 | 
			
		||||
  fetchNotificationsForRequest,
 | 
			
		||||
  expandNotificationsForRequest,
 | 
			
		||||
  acceptNotificationRequest,
 | 
			
		||||
  dismissNotificationRequest,
 | 
			
		||||
  acceptNotificationRequests,
 | 
			
		||||
  dismissNotificationRequests,
 | 
			
		||||
} from 'mastodon/actions/notification_requests';
 | 
			
		||||
import type { NotificationRequest } from 'mastodon/models/notification_request';
 | 
			
		||||
import { createNotificationRequestFromJSON } from 'mastodon/models/notification_request';
 | 
			
		||||
 | 
			
		||||
import { notificationToMap } from './notifications';
 | 
			
		||||
 | 
			
		||||
interface NotificationsListState {
 | 
			
		||||
  items: unknown[]; // TODO
 | 
			
		||||
  isLoading: boolean;
 | 
			
		||||
  next: string | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface CurrentNotificationRequestState {
 | 
			
		||||
  item: NotificationRequest | null;
 | 
			
		||||
  isLoading: boolean;
 | 
			
		||||
  removed: boolean;
 | 
			
		||||
  notifications: NotificationsListState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface NotificationRequestsState {
 | 
			
		||||
  items: NotificationRequest[];
 | 
			
		||||
  isLoading: boolean;
 | 
			
		||||
  next: string | null;
 | 
			
		||||
  current: CurrentNotificationRequestState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const initialState: NotificationRequestsState = {
 | 
			
		||||
  items: [],
 | 
			
		||||
  isLoading: false,
 | 
			
		||||
  next: null,
 | 
			
		||||
  current: {
 | 
			
		||||
    item: null,
 | 
			
		||||
    isLoading: false,
 | 
			
		||||
    removed: false,
 | 
			
		||||
    notifications: {
 | 
			
		||||
      isLoading: false,
 | 
			
		||||
      items: [],
 | 
			
		||||
      next: null,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const removeRequest = (state: NotificationRequestsState, id: string) => {
 | 
			
		||||
  if (state.current.item?.id === id) {
 | 
			
		||||
    state.current.removed = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state.items = state.items.filter((item) => item.id !== id);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const removeRequestByAccount = (
 | 
			
		||||
  state: NotificationRequestsState,
 | 
			
		||||
  account_id: string,
 | 
			
		||||
) => {
 | 
			
		||||
  if (state.current.item?.account_id === account_id) {
 | 
			
		||||
    state.current.removed = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state.items = state.items.filter((item) => item.account_id !== account_id);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const notificationRequestsReducer =
 | 
			
		||||
  createReducer<NotificationRequestsState>(initialState, (builder) => {
 | 
			
		||||
    builder
 | 
			
		||||
      .addCase(fetchNotificationRequests.fulfilled, (state, action) => {
 | 
			
		||||
        state.items = action.payload.requests
 | 
			
		||||
          .map(createNotificationRequestFromJSON)
 | 
			
		||||
          .concat(state.items);
 | 
			
		||||
        state.isLoading = false;
 | 
			
		||||
        state.next ??= action.payload.next ?? null;
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(expandNotificationRequests.fulfilled, (state, action) => {
 | 
			
		||||
        state.items = state.items.concat(
 | 
			
		||||
          action.payload.requests.map(createNotificationRequestFromJSON),
 | 
			
		||||
        );
 | 
			
		||||
        state.isLoading = false;
 | 
			
		||||
        state.next = action.payload.next ?? null;
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(blockAccountSuccess, (state, action) => {
 | 
			
		||||
        removeRequestByAccount(state, action.payload.relationship.id);
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(muteAccountSuccess, (state, action) => {
 | 
			
		||||
        if (action.payload.relationship.muting_notifications)
 | 
			
		||||
          removeRequestByAccount(state, action.payload.relationship.id);
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(fetchNotificationRequest.pending, (state) => {
 | 
			
		||||
        state.current = { ...initialState.current, isLoading: true };
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(fetchNotificationRequest.rejected, (state) => {
 | 
			
		||||
        state.current.isLoading = false;
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(fetchNotificationRequest.fulfilled, (state, action) => {
 | 
			
		||||
        state.current.isLoading = false;
 | 
			
		||||
        state.current.item = createNotificationRequestFromJSON(action.payload);
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(fetchNotificationsForRequest.fulfilled, (state, action) => {
 | 
			
		||||
        state.current.notifications.isLoading = false;
 | 
			
		||||
        state.current.notifications.items.unshift(
 | 
			
		||||
          ...action.payload.notifications.map(notificationToMap),
 | 
			
		||||
        );
 | 
			
		||||
        state.current.notifications.next ??= action.payload.next ?? null;
 | 
			
		||||
      })
 | 
			
		||||
      .addCase(expandNotificationsForRequest.fulfilled, (state, action) => {
 | 
			
		||||
        state.current.notifications.isLoading = false;
 | 
			
		||||
        state.current.notifications.items.push(
 | 
			
		||||
          ...action.payload.notifications.map(notificationToMap),
 | 
			
		||||
        );
 | 
			
		||||
        state.current.notifications.next = action.payload.next ?? null;
 | 
			
		||||
      })
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          fetchNotificationRequests.pending,
 | 
			
		||||
          expandNotificationRequests.pending,
 | 
			
		||||
        ),
 | 
			
		||||
        (state) => {
 | 
			
		||||
          state.isLoading = true;
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          fetchNotificationRequests.rejected,
 | 
			
		||||
          expandNotificationRequests.rejected,
 | 
			
		||||
        ),
 | 
			
		||||
        (state) => {
 | 
			
		||||
          state.isLoading = false;
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          acceptNotificationRequest.pending,
 | 
			
		||||
          dismissNotificationRequest.pending,
 | 
			
		||||
        ),
 | 
			
		||||
        (state, action) => {
 | 
			
		||||
          removeRequest(state, action.meta.arg.id);
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          acceptNotificationRequests.pending,
 | 
			
		||||
          dismissNotificationRequests.pending,
 | 
			
		||||
        ),
 | 
			
		||||
        (state, action) => {
 | 
			
		||||
          action.meta.arg.ids.forEach((id) => {
 | 
			
		||||
            removeRequest(state, id);
 | 
			
		||||
          });
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          fetchNotificationsForRequest.pending,
 | 
			
		||||
          expandNotificationsForRequest.pending,
 | 
			
		||||
        ),
 | 
			
		||||
        (state) => {
 | 
			
		||||
          state.current.notifications.isLoading = true;
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
      .addMatcher(
 | 
			
		||||
        isAnyOf(
 | 
			
		||||
          fetchNotificationsForRequest.rejected,
 | 
			
		||||
          expandNotificationsForRequest.rejected,
 | 
			
		||||
        ),
 | 
			
		||||
        (state) => {
 | 
			
		||||
          state.current.notifications.isLoading = false;
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
  });
 | 
			
		||||
@ -33,8 +33,12 @@ interface AppThunkConfig {
 | 
			
		||||
}
 | 
			
		||||
type AppThunkApi = Pick<GetThunkAPI<AppThunkConfig>, 'getState' | 'dispatch'>;
 | 
			
		||||
 | 
			
		||||
interface AppThunkOptions {
 | 
			
		||||
interface AppThunkOptions<Arg> {
 | 
			
		||||
  useLoadingBar?: boolean;
 | 
			
		||||
  condition?: (
 | 
			
		||||
    arg: Arg,
 | 
			
		||||
    { getState }: { getState: AppThunkApi['getState'] },
 | 
			
		||||
  ) => boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const createBaseAsyncThunk = createAsyncThunk.withTypes<AppThunkConfig>();
 | 
			
		||||
@ -42,7 +46,7 @@ const createBaseAsyncThunk = createAsyncThunk.withTypes<AppThunkConfig>();
 | 
			
		||||
export function createThunk<Arg = void, Returned = void>(
 | 
			
		||||
  name: string,
 | 
			
		||||
  creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
 | 
			
		||||
  options: AppThunkOptions = {},
 | 
			
		||||
  options: AppThunkOptions<Arg> = {},
 | 
			
		||||
) {
 | 
			
		||||
  return createBaseAsyncThunk(
 | 
			
		||||
    name,
 | 
			
		||||
@ -70,6 +74,7 @@ export function createThunk<Arg = void, Returned = void>(
 | 
			
		||||
        if (options.useLoadingBar) return { useLoadingBar: true };
 | 
			
		||||
        return {};
 | 
			
		||||
      },
 | 
			
		||||
      condition: options.condition,
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -96,7 +101,7 @@ type ArgsType = Record<string, unknown> | undefined;
 | 
			
		||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
 | 
			
		||||
  name: string,
 | 
			
		||||
  loadData: (args: Args) => Promise<LoadDataResult>,
 | 
			
		||||
  thunkOptions?: AppThunkOptions,
 | 
			
		||||
  thunkOptions?: AppThunkOptions<Args>,
 | 
			
		||||
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
 | 
			
		||||
 | 
			
		||||
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
 | 
			
		||||
@ -104,17 +109,19 @@ export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
 | 
			
		||||
  name: string,
 | 
			
		||||
  loadData: LoadData<Args, LoadDataResult>,
 | 
			
		||||
  onDataOrThunkOptions?:
 | 
			
		||||
    | AppThunkOptions
 | 
			
		||||
    | AppThunkOptions<Args>
 | 
			
		||||
    | OnData<Args, LoadDataResult, DiscardLoadData>,
 | 
			
		||||
  thunkOptions?: AppThunkOptions,
 | 
			
		||||
  thunkOptions?: AppThunkOptions<Args>,
 | 
			
		||||
): ReturnType<typeof createThunk<Args, void>>;
 | 
			
		||||
 | 
			
		||||
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
 | 
			
		||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
 | 
			
		||||
  name: string,
 | 
			
		||||
  loadData: LoadData<Args, LoadDataResult>,
 | 
			
		||||
  onDataOrThunkOptions?: AppThunkOptions | OnData<Args, LoadDataResult, void>,
 | 
			
		||||
  thunkOptions?: AppThunkOptions,
 | 
			
		||||
  onDataOrThunkOptions?:
 | 
			
		||||
    | AppThunkOptions<Args>
 | 
			
		||||
    | OnData<Args, LoadDataResult, void>,
 | 
			
		||||
  thunkOptions?: AppThunkOptions<Args>,
 | 
			
		||||
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
 | 
			
		||||
 | 
			
		||||
// Overload when there is an `onData` method returning something
 | 
			
		||||
@ -126,9 +133,9 @@ export function createDataLoadingThunk<
 | 
			
		||||
  name: string,
 | 
			
		||||
  loadData: LoadData<Args, LoadDataResult>,
 | 
			
		||||
  onDataOrThunkOptions?:
 | 
			
		||||
    | AppThunkOptions
 | 
			
		||||
    | AppThunkOptions<Args>
 | 
			
		||||
    | OnData<Args, LoadDataResult, Returned>,
 | 
			
		||||
  thunkOptions?: AppThunkOptions,
 | 
			
		||||
  thunkOptions?: AppThunkOptions<Args>,
 | 
			
		||||
): ReturnType<typeof createThunk<Args, Returned>>;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -154,6 +161,7 @@ export function createDataLoadingThunk<
 | 
			
		||||
 * @param maybeThunkOptions
 | 
			
		||||
 *   Additional Mastodon specific options for the thunk. Currently supports:
 | 
			
		||||
 *   - `useLoadingBar` to display a loading bar while this action is pending. Defaults to true.
 | 
			
		||||
 *   - `condition` is passed to `createAsyncThunk` (https://redux-toolkit.js.org/api/createAsyncThunk#canceling-before-execution)
 | 
			
		||||
 * @returns The created thunk
 | 
			
		||||
 */
 | 
			
		||||
export function createDataLoadingThunk<
 | 
			
		||||
@ -164,12 +172,12 @@ export function createDataLoadingThunk<
 | 
			
		||||
  name: string,
 | 
			
		||||
  loadData: LoadData<Args, LoadDataResult>,
 | 
			
		||||
  onDataOrThunkOptions?:
 | 
			
		||||
    | AppThunkOptions
 | 
			
		||||
    | AppThunkOptions<Args>
 | 
			
		||||
    | OnData<Args, LoadDataResult, Returned>,
 | 
			
		||||
  maybeThunkOptions?: AppThunkOptions,
 | 
			
		||||
  maybeThunkOptions?: AppThunkOptions<Args>,
 | 
			
		||||
) {
 | 
			
		||||
  let onData: OnData<Args, LoadDataResult, Returned> | undefined;
 | 
			
		||||
  let thunkOptions: AppThunkOptions | undefined;
 | 
			
		||||
  let thunkOptions: AppThunkOptions<Args> | undefined;
 | 
			
		||||
 | 
			
		||||
  if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
 | 
			
		||||
  else if (typeof onDataOrThunkOptions === 'object')
 | 
			
		||||
@ -203,6 +211,9 @@ export function createDataLoadingThunk<
 | 
			
		||||
        return undefined as Returned;
 | 
			
		||||
      else return result;
 | 
			
		||||
    },
 | 
			
		||||
    { useLoadingBar: thunkOptions?.useLoadingBar ?? true },
 | 
			
		||||
    {
 | 
			
		||||
      useLoadingBar: thunkOptions?.useLoadingBar ?? true,
 | 
			
		||||
      condition: thunkOptions?.condition,
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user