Fix `createDataLoadingThunk` and related actions (#30408)

pull/2718/head
Renaud Chaput 2024-05-23 20:22:42 +02:00 committed by GitHub
parent 133d98fb25
commit b6fd14f0e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 41 additions and 35 deletions

View File

@ -3,7 +3,8 @@ import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
export const submitAccountNote = createDataLoadingThunk( export const submitAccountNote = createDataLoadingThunk(
'account_note/submit', 'account_note/submit',
(accountId: string, note: string) => apiSubmitAccountNote(accountId, note), ({ accountId, note }: { accountId: string; note: string }) =>
apiSubmitAccountNote(accountId, note),
(relationship) => ({ relationship }), (relationship) => ({ relationship }),
{ skipLoading: true }, { skipLoading: true },
); );

View File

@ -6,8 +6,13 @@ import { importFetchedStatus } from './importer';
export const reblog = createDataLoadingThunk( export const reblog = createDataLoadingThunk(
'status/reblog', 'status/reblog',
(statusId: string, visibility: StatusVisibility) => ({
apiReblog(statusId, visibility), statusId,
visibility,
}: {
statusId: string;
visibility: StatusVisibility;
}) => apiReblog(statusId, visibility),
(data, { dispatch, discardLoadData }) => { (data, { dispatch, discardLoadData }) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only // The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper // interested in how the original is modified, hence passing it skipping the wrapper
@ -20,7 +25,7 @@ export const reblog = createDataLoadingThunk(
export const unreblog = createDataLoadingThunk( export const unreblog = createDataLoadingThunk(
'status/unreblog', 'status/unreblog',
(statusId: string) => apiUnreblog(statusId), ({ statusId }: { statusId: string }) => apiUnreblog(statusId),
(data, { dispatch, discardLoadData }) => { (data, { dispatch, discardLoadData }) => {
dispatch(importFetchedStatus(data)); dispatch(importFetchedStatus(data));

View File

@ -62,12 +62,12 @@ export default function api(withAuthorization = true) {
export async function apiRequest<ApiResponse = unknown>( export async function apiRequest<ApiResponse = unknown>(
method: Method, method: Method,
url: string, url: string,
params?: unknown, params?: Record<string, unknown>,
) { ) {
const { data } = await api().request<ApiResponse>({ const { data } = await api().request<ApiResponse>({
method, method,
url, url: '/api/' + url,
params, data: params,
}); });
return data; return data;

View File

@ -2,6 +2,6 @@ import { apiRequest } from 'mastodon/api';
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships';
export const apiSubmitAccountNote = (id: string, value: string) => export const apiSubmitAccountNote = (id: string, value: string) =>
apiRequest<ApiRelationshipJSON>('post', `/api/v1/accounts/${id}/note`, { apiRequest<ApiRelationshipJSON>('post', `v1/accounts/${id}/note`, {
comment: value, comment: value,
}); });

View File

@ -96,9 +96,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status.id)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
dispatch(reblog(status.id, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
} }
}, },

View File

@ -11,7 +11,7 @@ const mapStateToProps = (state, { account }) => ({
const mapDispatchToProps = (dispatch, { account }) => ({ const mapDispatchToProps = (dispatch, { account }) => ({
onSave (value) { onSave (value) {
dispatch(submitAccountNote(account.get('id'), value)); dispatch(submitAccountNote({ accountId: account.get('id'), note: value }));
}, },
}); });

View File

@ -39,12 +39,12 @@ const mapDispatchToProps = dispatch => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status.id, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status.id)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View File

@ -123,7 +123,7 @@ class Footer extends ImmutablePureComponent {
_performReblog = (status, privacy) => { _performReblog = (status, privacy) => {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(reblog(status.id, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = e => { handleReblogClick = e => {
@ -132,7 +132,7 @@ class Footer extends ImmutablePureComponent {
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status.id)); dispatch(unreblog({ statusId: status.get('id') }));
} else if ((e && e.shiftKey) || !boostModal) { } else if ((e && e.shiftKey) || !boostModal) {
this._performReblog(status); this._performReblog(status);
} else { } else {

View File

@ -74,12 +74,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status.id, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status.id)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View File

@ -299,7 +299,7 @@ class Status extends ImmutablePureComponent {
}; };
handleModalReblog = (status, privacy) => { handleModalReblog = (status, privacy) => {
this.props.dispatch(reblog(status.id, privacy)); this.props.dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = (status, e) => { handleReblogClick = (status, e) => {
@ -308,7 +308,7 @@ class Status extends ImmutablePureComponent {
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status.id)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if ((e && e.shiftKey) || !boostModal) { if ((e && e.shiftKey) || !boostModal) {
this.handleModalReblog(status); this.handleModalReblog(status);

View File

@ -122,13 +122,13 @@ export default function statuses(state = initialState, action) {
return statusTranslateUndo(state, action.id); return statusTranslateUndo(state, action.id);
default: default:
if(reblog.pending.match(action)) if(reblog.pending.match(action))
return state.setIn([action.meta.params.statusId, 'reblogged'], true); return state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else if(reblog.rejected.match(action)) else if(reblog.rejected.match(action))
return state.get(action.meta.params.statusId) === undefined ? state : state.setIn([action.meta.params.statusId, 'reblogged'], false); return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.pending.match(action)) else if(unreblog.pending.match(action))
return state.setIn([action.meta.params.statusId, 'reblogged'], false); return state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.rejected.match(action)) else if(unreblog.rejected.match(action))
return state.get(action.meta.params.statusId) === undefined ? state : state.setIn([action.meta.params.statusId, 'reblogged'], true); return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else else
return state; return state;
} }

View File

@ -92,20 +92,20 @@ type OnData<LoadDataResult, ReturnedData> = (
// Overload when there is no `onData` method, the payload is the `onData` result // Overload when there is no `onData` method, the payload is the `onData` result
export function createDataLoadingThunk< export function createDataLoadingThunk<
LoadDataResult, LoadDataResult,
Args extends readonly unknown[], Args extends Record<string, unknown>,
>( >(
name: string, name: string,
loadData: (...args: Args) => Promise<LoadDataResult>, loadData: (args: Args) => Promise<LoadDataResult>,
thunkOptions?: AppThunkOptions, thunkOptions?: AppThunkOptions,
): 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
export function createDataLoadingThunk< export function createDataLoadingThunk<
LoadDataResult, LoadDataResult,
Args extends readonly unknown[], Args extends Record<string, unknown>,
>( >(
name: string, name: string,
loadData: (...args: Args) => Promise<LoadDataResult>, loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: onDataOrThunkOptions?:
| AppThunkOptions | AppThunkOptions
| OnData<LoadDataResult, DiscardLoadData>, | OnData<LoadDataResult, DiscardLoadData>,
@ -115,10 +115,10 @@ export function createDataLoadingThunk<
// 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< export function createDataLoadingThunk<
LoadDataResult, LoadDataResult,
Args extends readonly unknown[], Args extends Record<string, unknown>,
>( >(
name: string, name: string,
loadData: (...args: Args) => Promise<LoadDataResult>, loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>, onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
thunkOptions?: AppThunkOptions, thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>; ): ReturnType<typeof createThunk<Args, LoadDataResult>>;
@ -126,11 +126,11 @@ export function createDataLoadingThunk<
// Overload when there is an `onData` method returning something // Overload when there is an `onData` method returning something
export function createDataLoadingThunk< export function createDataLoadingThunk<
LoadDataResult, LoadDataResult,
Args extends readonly unknown[], Args extends Record<string, unknown>,
Returned, Returned,
>( >(
name: string, name: string,
loadData: (...args: Args) => Promise<LoadDataResult>, loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>, onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
thunkOptions?: AppThunkOptions, thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, Returned>>; ): ReturnType<typeof createThunk<Args, Returned>>;
@ -142,7 +142,7 @@ export function createDataLoadingThunk<
* *
* It is a wrapper around RTK's [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk) * It is a wrapper around RTK's [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
* @param name Prefix for the actions types * @param name Prefix for the actions types
* @param loadData Function that loads the data. It's arguments will become the thunk's arguments * @param loadData Function that loads the data. It's (object) argument will become the thunk's argument
* @param onDataOrThunkOptions * @param onDataOrThunkOptions
* Callback called on the results from `loadData`. * Callback called on the results from `loadData`.
* *
@ -162,11 +162,11 @@ export function createDataLoadingThunk<
*/ */
export function createDataLoadingThunk< export function createDataLoadingThunk<
LoadDataResult, LoadDataResult,
Args extends readonly unknown[], Args extends Record<string, unknown>,
Returned, Returned,
>( >(
name: string, name: string,
loadData: (...args: Args) => Promise<LoadDataResult>, loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>, onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
maybeThunkOptions?: AppThunkOptions, maybeThunkOptions?: AppThunkOptions,
) { ) {
@ -184,7 +184,7 @@ export function createDataLoadingThunk<
return createThunk<Args, Returned>( return createThunk<Args, Returned>(
name, name,
async (arg, { getState, dispatch }) => { async (arg, { getState, dispatch }) => {
const data = await loadData(...arg); const data = await loadData(arg);
if (!onData) return data as Returned; if (!onData) return data as Returned;