Convert notification requests actions and reducers to Typescript (#31866)
parent
d5cf27e667
commit
c0eda832f3
|
@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
apiClearNotifications,
|
apiClearNotifications,
|
||||||
apiFetchNotifications,
|
apiFetchNotificationGroups,
|
||||||
} from 'mastodon/api/notifications';
|
} from 'mastodon/api/notifications';
|
||||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||||
import type {
|
import type {
|
||||||
|
@ -71,7 +71,7 @@ function dispatchAssociatedRecords(
|
||||||
export const fetchNotifications = createDataLoadingThunk(
|
export const fetchNotifications = createDataLoadingThunk(
|
||||||
'notificationGroups/fetch',
|
'notificationGroups/fetch',
|
||||||
async (_params, { getState }) =>
|
async (_params, { getState }) =>
|
||||||
apiFetchNotifications({ exclude_types: getExcludedTypes(getState()) }),
|
apiFetchNotificationGroups({ exclude_types: getExcludedTypes(getState()) }),
|
||||||
({ notifications, accounts, statuses }, { dispatch }) => {
|
({ notifications, accounts, statuses }, { dispatch }) => {
|
||||||
dispatch(importFetchedAccounts(accounts));
|
dispatch(importFetchedAccounts(accounts));
|
||||||
dispatch(importFetchedStatuses(statuses));
|
dispatch(importFetchedStatuses(statuses));
|
||||||
|
@ -92,7 +92,7 @@ export const fetchNotifications = createDataLoadingThunk(
|
||||||
export const fetchNotificationsGap = createDataLoadingThunk(
|
export const fetchNotificationsGap = createDataLoadingThunk(
|
||||||
'notificationGroups/fetchGap',
|
'notificationGroups/fetchGap',
|
||||||
async (params: { gap: NotificationGap }, { getState }) =>
|
async (params: { gap: NotificationGap }, { getState }) =>
|
||||||
apiFetchNotifications({
|
apiFetchNotificationGroups({
|
||||||
max_id: params.gap.maxId,
|
max_id: params.gap.maxId,
|
||||||
exclude_types: getExcludedTypes(getState()),
|
exclude_types: getExcludedTypes(getState()),
|
||||||
}),
|
}),
|
||||||
|
@ -108,7 +108,7 @@ export const fetchNotificationsGap = createDataLoadingThunk(
|
||||||
export const pollRecentNotifications = createDataLoadingThunk(
|
export const pollRecentNotifications = createDataLoadingThunk(
|
||||||
'notificationGroups/pollRecentNotifications',
|
'notificationGroups/pollRecentNotifications',
|
||||||
async (_params, { getState }) => {
|
async (_params, { getState }) => {
|
||||||
return apiFetchNotifications({
|
return apiFetchNotificationGroups({
|
||||||
max_id: undefined,
|
max_id: undefined,
|
||||||
exclude_types: getExcludedTypes(getState()),
|
exclude_types: getExcludedTypes(getState()),
|
||||||
// In slow mode, we don't want to include notifications that duplicate the already-displayed ones
|
// In slow mode, we don't want to include notifications that duplicate the already-displayed ones
|
||||||
|
|
|
@ -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,
|
importFetchedStatuses,
|
||||||
} from './importer';
|
} from './importer';
|
||||||
import { submitMarkers } from './markers';
|
import { submitMarkers } from './markers';
|
||||||
import { decreasePendingNotificationsCount } from './notification_policies';
|
|
||||||
import { notificationsUpdate } from "./notifications_typed";
|
import { notificationsUpdate } from "./notifications_typed";
|
||||||
import { register as registerPushNotifications } from './push_notifications';
|
import { register as registerPushNotifications } from './push_notifications';
|
||||||
import { saveSettings } from './settings';
|
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_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
|
||||||
export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
|
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_REQUEST = 'NOTIFICATION_REQUESTS_ACCEPT_REQUEST';
|
||||||
export const NOTIFICATION_REQUESTS_ACCEPT_SUCCESS = 'NOTIFICATION_REQUESTS_ACCEPT_SUCCESS';
|
export const NOTIFICATION_REQUESTS_ACCEPT_SUCCESS = 'NOTIFICATION_REQUESTS_ACCEPT_SUCCESS';
|
||||||
export const NOTIFICATION_REQUESTS_ACCEPT_FAIL = 'NOTIFICATION_REQUESTS_ACCEPT_FAIL';
|
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_SUCCESS = 'NOTIFICATION_REQUESTS_DISMISS_SUCCESS';
|
||||||
export const NOTIFICATION_REQUESTS_DISMISS_FAIL = 'NOTIFICATION_REQUESTS_DISMISS_FAIL';
|
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({
|
defineMessages({
|
||||||
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
|
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
|
||||||
group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
|
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 = () => ({
|
export const loadPending = () => ({
|
||||||
type: NOTIFICATIONS_LOAD_PENDING,
|
type: NOTIFICATIONS_LOAD_PENDING,
|
||||||
});
|
});
|
||||||
|
@ -343,296 +308,3 @@ export function setBrowserPermission (value) {
|
||||||
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 api, {
|
||||||
import type { ApiNotificationGroupsResultJSON } from 'mastodon/api_types/notifications';
|
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[];
|
exclude_types?: string[];
|
||||||
max_id?: string;
|
max_id?: string;
|
||||||
since_id?: string;
|
since_id?: string;
|
||||||
|
@ -24,3 +53,43 @@ export const apiFetchNotifications = async (params?: {
|
||||||
|
|
||||||
export const apiClearNotifications = () =>
|
export const apiClearNotifications = () =>
|
||||||
apiRequest<undefined>('POST', 'v1/notifications/clear');
|
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[];
|
statuses: ApiStatusJSON[];
|
||||||
notification_groups: ApiNotificationGroupJSON[];
|
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 MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import { initBlockModal } from 'mastodon/actions/blocks';
|
import { initBlockModal } from 'mastodon/actions/blocks';
|
||||||
import { initMuteModal } from 'mastodon/actions/mutes';
|
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 { initReport } from 'mastodon/actions/reports';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { CheckBox } from 'mastodon/components/check_box';
|
import { CheckBox } from 'mastodon/components/check_box';
|
||||||
|
@ -40,11 +40,11 @@ export const NotificationRequest = ({ id, accountId, notificationsCount, checked
|
||||||
const { push: historyPush } = useHistory();
|
const { push: historyPush } = useHistory();
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
dispatch(dismissNotificationRequest(id));
|
dispatch(dismissNotificationRequest({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|
||||||
const handleAccept = useCallback(() => {
|
const handleAccept = useCallback(() => {
|
||||||
dispatch(acceptNotificationRequest(id));
|
dispatch(acceptNotificationRequest({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|
||||||
const handleMute = useCallback(() => {
|
const handleMute = useCallback(() => {
|
||||||
|
|
|
@ -10,7 +10,13 @@ import { useSelector, useDispatch } from 'react-redux';
|
||||||
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
|
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
|
||||||
import DoneIcon from '@/material-icons/400-24px/done.svg?react';
|
import DoneIcon from '@/material-icons/400-24px/done.svg?react';
|
||||||
import InventoryIcon from '@/material-icons/400-24px/inventory_2.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 Column from 'mastodon/components/column';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
@ -44,28 +50,28 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
|
||||||
const columnRef = useRef();
|
const columnRef = useRef();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const notificationRequest = useSelector(state => state.getIn(['notificationRequests', 'current', 'item', 'id']) === id ? state.getIn(['notificationRequests', 'current', 'item']) : null);
|
const notificationRequest = useSelector(state => state.notificationRequests.current.item?.id === id ? state.notificationRequests.current.item : null);
|
||||||
const accountId = notificationRequest?.get('account');
|
const accountId = notificationRequest?.account_id;
|
||||||
const account = useSelector(state => state.getIn(['accounts', accountId]));
|
const account = useSelector(state => state.getIn(['accounts', accountId]));
|
||||||
const notifications = useSelector(state => state.getIn(['notificationRequests', 'current', 'notifications', 'items']));
|
const notifications = useSelector(state => state.notificationRequests.current.notifications.items);
|
||||||
const isLoading = useSelector(state => state.getIn(['notificationRequests', 'current', 'notifications', 'isLoading']));
|
const isLoading = useSelector(state => state.notificationRequests.current.notifications.isLoading);
|
||||||
const hasMore = useSelector(state => !!state.getIn(['notificationRequests', 'current', 'notifications', 'next']));
|
const hasMore = useSelector(state => !!state.notificationRequests.current.notifications.next);
|
||||||
const removed = useSelector(state => state.getIn(['notificationRequests', 'current', 'removed']));
|
const removed = useSelector(state => state.notificationRequests.current.removed);
|
||||||
|
|
||||||
const handleHeaderClick = useCallback(() => {
|
const handleHeaderClick = useCallback(() => {
|
||||||
columnRef.current?.scrollTop();
|
columnRef.current?.scrollTop();
|
||||||
}, [columnRef]);
|
}, [columnRef]);
|
||||||
|
|
||||||
const handleLoadMore = useCallback(() => {
|
const handleLoadMore = useCallback(() => {
|
||||||
dispatch(expandNotificationsForRequest());
|
dispatch(expandNotificationsForRequest({ accountId }));
|
||||||
}, [dispatch]);
|
}, [dispatch, accountId]);
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
dispatch(dismissNotificationRequest(id));
|
dispatch(dismissNotificationRequest({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|
||||||
const handleAccept = useCallback(() => {
|
const handleAccept = useCallback(() => {
|
||||||
dispatch(acceptNotificationRequest(id));
|
dispatch(acceptNotificationRequest({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|
||||||
const handleMoveUp = useCallback(id => {
|
const handleMoveUp = useCallback(id => {
|
||||||
|
@ -79,12 +85,12 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
|
||||||
}, [columnRef, notifications]);
|
}, [columnRef, notifications]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchNotificationRequest(id));
|
dispatch(fetchNotificationRequest({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (accountId) {
|
if (accountId) {
|
||||||
dispatch(fetchNotificationsForRequest(accountId));
|
dispatch(fetchNotificationsForRequest({ accountId }));
|
||||||
}
|
}
|
||||||
}, [dispatch, 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 InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
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 { changeSetting } from 'mastodon/actions/settings';
|
||||||
import { CheckBox } from 'mastodon/components/check_box';
|
import { CheckBox } from 'mastodon/components/check_box';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
|
@ -84,7 +89,7 @@ const SelectRow = ({selectAllChecked, toggleSelectAll, selectedItems, selectionM
|
||||||
message: intl.formatMessage(messages.confirmAcceptMultipleMessage, { count: selectedItems.length }),
|
message: intl.formatMessage(messages.confirmAcceptMultipleMessage, { count: selectedItems.length }),
|
||||||
confirm: intl.formatMessage(messages.confirmAcceptMultipleButton, { count: selectedItems.length}),
|
confirm: intl.formatMessage(messages.confirmAcceptMultipleButton, { count: selectedItems.length}),
|
||||||
onConfirm: () =>
|
onConfirm: () =>
|
||||||
dispatch(acceptNotificationRequests(selectedItems)),
|
dispatch(acceptNotificationRequests({ ids: selectedItems })),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}, [dispatch, intl, selectedItems]);
|
}, [dispatch, intl, selectedItems]);
|
||||||
|
@ -97,7 +102,7 @@ const SelectRow = ({selectAllChecked, toggleSelectAll, selectedItems, selectionM
|
||||||
message: intl.formatMessage(messages.confirmDismissMultipleMessage, { count: selectedItems.length }),
|
message: intl.formatMessage(messages.confirmDismissMultipleMessage, { count: selectedItems.length }),
|
||||||
confirm: intl.formatMessage(messages.confirmDismissMultipleButton, { count: selectedItems.length}),
|
confirm: intl.formatMessage(messages.confirmDismissMultipleButton, { count: selectedItems.length}),
|
||||||
onConfirm: () =>
|
onConfirm: () =>
|
||||||
dispatch(dismissNotificationRequests(selectedItems)),
|
dispatch(dismissNotificationRequests({ ids: selectedItems })),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}, [dispatch, intl, selectedItems]);
|
}, [dispatch, intl, selectedItems]);
|
||||||
|
@ -161,9 +166,9 @@ export const NotificationRequests = ({ multiColumn }) => {
|
||||||
const columnRef = useRef();
|
const columnRef = useRef();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isLoading = useSelector(state => state.getIn(['notificationRequests', 'isLoading']));
|
const isLoading = useSelector(state => state.notificationRequests.isLoading);
|
||||||
const notificationRequests = useSelector(state => state.getIn(['notificationRequests', 'items']));
|
const notificationRequests = useSelector(state => state.notificationRequests.items);
|
||||||
const hasMore = useSelector(state => !!state.getIn(['notificationRequests', 'next']));
|
const hasMore = useSelector(state => !!state.notificationRequests.next);
|
||||||
|
|
||||||
const [selectionMode, setSelectionMode] = useState(false);
|
const [selectionMode, setSelectionMode] = useState(false);
|
||||||
const [checkedRequestIds, setCheckedRequestIds] = useState([]);
|
const [checkedRequestIds, setCheckedRequestIds] = useState([]);
|
||||||
|
@ -182,7 +187,7 @@ export const NotificationRequests = ({ multiColumn }) => {
|
||||||
else
|
else
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
|
|
||||||
setSelectAllChecked(ids.length === notificationRequests.size);
|
setSelectAllChecked(ids.length === notificationRequests.length);
|
||||||
|
|
||||||
return [...ids];
|
return [...ids];
|
||||||
});
|
});
|
||||||
|
@ -193,7 +198,7 @@ export const NotificationRequests = ({ multiColumn }) => {
|
||||||
if(checked)
|
if(checked)
|
||||||
setCheckedRequestIds([]);
|
setCheckedRequestIds([]);
|
||||||
else
|
else
|
||||||
setCheckedRequestIds(notificationRequests.map(request => request.get('id')).toArray());
|
setCheckedRequestIds(notificationRequests.map(request => request.id));
|
||||||
|
|
||||||
return !checked;
|
return !checked;
|
||||||
});
|
});
|
||||||
|
@ -217,7 +222,7 @@ export const NotificationRequests = ({ multiColumn }) => {
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
showBackButton
|
showBackButton
|
||||||
appendContent={
|
appendContent={
|
||||||
notificationRequests.size > 0 && (
|
notificationRequests.length > 0 && (
|
||||||
<SelectRow selectionMode={selectionMode} setSelectionMode={setSelectionMode} selectAllChecked={selectAllChecked} toggleSelectAll={toggleSelectAll} selectedItems={checkedRequestIds} />
|
<SelectRow selectionMode={selectionMode} setSelectionMode={setSelectionMode} selectAllChecked={selectAllChecked} toggleSelectAll={toggleSelectAll} selectedItems={checkedRequestIds} />
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -236,12 +241,12 @@ export const NotificationRequests = ({ multiColumn }) => {
|
||||||
>
|
>
|
||||||
{notificationRequests.map(request => (
|
{notificationRequests.map(request => (
|
||||||
<NotificationRequest
|
<NotificationRequest
|
||||||
key={request.get('id')}
|
key={request.id}
|
||||||
id={request.get('id')}
|
id={request.id}
|
||||||
accountId={request.get('account')}
|
accountId={request.account_id}
|
||||||
notificationsCount={request.get('notifications_count')}
|
notificationsCount={request.notifications_count}
|
||||||
showCheckbox={selectionMode}
|
showCheckbox={selectionMode}
|
||||||
checked={checkedRequestIds.includes(request.get('id'))}
|
checked={checkedRequestIds.includes(request.id)}
|
||||||
toggleCheck={handleCheck}
|
toggleCheck={handleCheck}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -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'>;
|
type AppThunkApi = Pick<GetThunkAPI<AppThunkConfig>, 'getState' | 'dispatch'>;
|
||||||
|
|
||||||
interface AppThunkOptions {
|
interface AppThunkOptions<Arg> {
|
||||||
useLoadingBar?: boolean;
|
useLoadingBar?: boolean;
|
||||||
|
condition?: (
|
||||||
|
arg: Arg,
|
||||||
|
{ getState }: { getState: AppThunkApi['getState'] },
|
||||||
|
) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createBaseAsyncThunk = createAsyncThunk.withTypes<AppThunkConfig>();
|
const createBaseAsyncThunk = createAsyncThunk.withTypes<AppThunkConfig>();
|
||||||
|
@ -42,7 +46,7 @@ const createBaseAsyncThunk = createAsyncThunk.withTypes<AppThunkConfig>();
|
||||||
export function createThunk<Arg = void, Returned = void>(
|
export function createThunk<Arg = void, Returned = void>(
|
||||||
name: string,
|
name: string,
|
||||||
creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
|
creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
|
||||||
options: AppThunkOptions = {},
|
options: AppThunkOptions<Arg> = {},
|
||||||
) {
|
) {
|
||||||
return createBaseAsyncThunk(
|
return createBaseAsyncThunk(
|
||||||
name,
|
name,
|
||||||
|
@ -70,6 +74,7 @@ export function createThunk<Arg = void, Returned = void>(
|
||||||
if (options.useLoadingBar) return { useLoadingBar: true };
|
if (options.useLoadingBar) return { useLoadingBar: true };
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
condition: options.condition,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +101,7 @@ type ArgsType = Record<string, unknown> | undefined;
|
||||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
||||||
name: string,
|
name: string,
|
||||||
loadData: (args: Args) => Promise<LoadDataResult>,
|
loadData: (args: Args) => Promise<LoadDataResult>,
|
||||||
thunkOptions?: AppThunkOptions,
|
thunkOptions?: AppThunkOptions<Args>,
|
||||||
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
|
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
|
||||||
|
|
||||||
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
|
// 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,
|
name: string,
|
||||||
loadData: LoadData<Args, LoadDataResult>,
|
loadData: LoadData<Args, LoadDataResult>,
|
||||||
onDataOrThunkOptions?:
|
onDataOrThunkOptions?:
|
||||||
| AppThunkOptions
|
| AppThunkOptions<Args>
|
||||||
| OnData<Args, LoadDataResult, DiscardLoadData>,
|
| OnData<Args, LoadDataResult, DiscardLoadData>,
|
||||||
thunkOptions?: AppThunkOptions,
|
thunkOptions?: AppThunkOptions<Args>,
|
||||||
): ReturnType<typeof createThunk<Args, void>>;
|
): ReturnType<typeof createThunk<Args, void>>;
|
||||||
|
|
||||||
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
|
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
|
||||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
||||||
name: string,
|
name: string,
|
||||||
loadData: LoadData<Args, LoadDataResult>,
|
loadData: LoadData<Args, LoadDataResult>,
|
||||||
onDataOrThunkOptions?: AppThunkOptions | OnData<Args, LoadDataResult, void>,
|
onDataOrThunkOptions?:
|
||||||
thunkOptions?: AppThunkOptions,
|
| AppThunkOptions<Args>
|
||||||
|
| OnData<Args, LoadDataResult, void>,
|
||||||
|
thunkOptions?: AppThunkOptions<Args>,
|
||||||
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
|
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
|
||||||
|
|
||||||
// Overload when there is an `onData` method returning something
|
// Overload when there is an `onData` method returning something
|
||||||
|
@ -126,9 +133,9 @@ export function createDataLoadingThunk<
|
||||||
name: string,
|
name: string,
|
||||||
loadData: LoadData<Args, LoadDataResult>,
|
loadData: LoadData<Args, LoadDataResult>,
|
||||||
onDataOrThunkOptions?:
|
onDataOrThunkOptions?:
|
||||||
| AppThunkOptions
|
| AppThunkOptions<Args>
|
||||||
| OnData<Args, LoadDataResult, Returned>,
|
| OnData<Args, LoadDataResult, Returned>,
|
||||||
thunkOptions?: AppThunkOptions,
|
thunkOptions?: AppThunkOptions<Args>,
|
||||||
): ReturnType<typeof createThunk<Args, Returned>>;
|
): ReturnType<typeof createThunk<Args, Returned>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,6 +161,7 @@ export function createDataLoadingThunk<
|
||||||
* @param maybeThunkOptions
|
* @param maybeThunkOptions
|
||||||
* Additional Mastodon specific options for the thunk. Currently supports:
|
* Additional Mastodon specific options for the thunk. Currently supports:
|
||||||
* - `useLoadingBar` to display a loading bar while this action is pending. Defaults to true.
|
* - `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
|
* @returns The created thunk
|
||||||
*/
|
*/
|
||||||
export function createDataLoadingThunk<
|
export function createDataLoadingThunk<
|
||||||
|
@ -164,12 +172,12 @@ export function createDataLoadingThunk<
|
||||||
name: string,
|
name: string,
|
||||||
loadData: LoadData<Args, LoadDataResult>,
|
loadData: LoadData<Args, LoadDataResult>,
|
||||||
onDataOrThunkOptions?:
|
onDataOrThunkOptions?:
|
||||||
| AppThunkOptions
|
| AppThunkOptions<Args>
|
||||||
| OnData<Args, LoadDataResult, Returned>,
|
| OnData<Args, LoadDataResult, Returned>,
|
||||||
maybeThunkOptions?: AppThunkOptions,
|
maybeThunkOptions?: AppThunkOptions<Args>,
|
||||||
) {
|
) {
|
||||||
let onData: OnData<Args, LoadDataResult, Returned> | undefined;
|
let onData: OnData<Args, LoadDataResult, Returned> | undefined;
|
||||||
let thunkOptions: AppThunkOptions | undefined;
|
let thunkOptions: AppThunkOptions<Args> | undefined;
|
||||||
|
|
||||||
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
|
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
|
||||||
else if (typeof onDataOrThunkOptions === 'object')
|
else if (typeof onDataOrThunkOptions === 'object')
|
||||||
|
@ -203,6 +211,9 @@ export function createDataLoadingThunk<
|
||||||
return undefined as Returned;
|
return undefined as Returned;
|
||||||
else return result;
|
else return result;
|
||||||
},
|
},
|
||||||
{ useLoadingBar: thunkOptions?.useLoadingBar ?? true },
|
{
|
||||||
|
useLoadingBar: thunkOptions?.useLoadingBar ?? true,
|
||||||
|
condition: thunkOptions?.condition,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue