Migrate most of the dashboard to pl-api
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
878fdd8646
commit
a37d04b300
26 changed files with 260 additions and 649 deletions
|
@ -132,7 +132,7 @@
|
|||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"object-to-formdata": "^4.5.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pl-api": "^0.0.16",
|
||||
"pl-api": "^0.0.17",
|
||||
"postcss": "^8.4.29",
|
||||
"process": "^0.11.10",
|
||||
"punycode": "^2.1.1",
|
||||
|
|
|
@ -4,10 +4,10 @@ import { importFetchedAccount, importFetchedAccounts, importFetchedStatuses } fr
|
|||
import { accountIdsToAccts } from 'soapbox/selectors';
|
||||
import { filterBadges, getTagDiff } from 'soapbox/utils/badges';
|
||||
|
||||
import { getClient, getNextLink } from '../api';
|
||||
import { getClient } from '../api';
|
||||
|
||||
import type { Account, AdminGetAccountsParams, AdminGetReportsParams } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST' as const;
|
||||
const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS' as const;
|
||||
|
@ -21,25 +21,25 @@ const ADMIN_REPORTS_FETCH_REQUEST = 'ADMIN_REPORTS_FETCH_REQUEST' as const;
|
|||
const ADMIN_REPORTS_FETCH_SUCCESS = 'ADMIN_REPORTS_FETCH_SUCCESS' as const;
|
||||
const ADMIN_REPORTS_FETCH_FAIL = 'ADMIN_REPORTS_FETCH_FAIL' as const;
|
||||
|
||||
const ADMIN_REPORTS_PATCH_REQUEST = 'ADMIN_REPORTS_PATCH_REQUEST' as const;
|
||||
const ADMIN_REPORTS_PATCH_SUCCESS = 'ADMIN_REPORTS_PATCH_SUCCESS' as const;
|
||||
const ADMIN_REPORTS_PATCH_FAIL = 'ADMIN_REPORTS_PATCH_FAIL' as const;
|
||||
const ADMIN_REPORT_PATCH_REQUEST = 'ADMIN_REPORT_PATCH_REQUEST' as const;
|
||||
const ADMIN_REPORT_PATCH_SUCCESS = 'ADMIN_REPORT_PATCH_SUCCESS' as const;
|
||||
const ADMIN_REPORT_PATCH_FAIL = 'ADMIN_REPORT_PATCH_FAIL' as const;
|
||||
|
||||
const ADMIN_USERS_FETCH_REQUEST = 'ADMIN_USERS_FETCH_REQUEST' as const;
|
||||
const ADMIN_USERS_FETCH_SUCCESS = 'ADMIN_USERS_FETCH_SUCCESS' as const;
|
||||
const ADMIN_USERS_FETCH_FAIL = 'ADMIN_USERS_FETCH_FAIL' as const;
|
||||
|
||||
const ADMIN_USERS_DELETE_REQUEST = 'ADMIN_USERS_DELETE_REQUEST' as const;
|
||||
const ADMIN_USERS_DELETE_SUCCESS = 'ADMIN_USERS_DELETE_SUCCESS' as const;
|
||||
const ADMIN_USERS_DELETE_FAIL = 'ADMIN_USERS_DELETE_FAIL' as const;
|
||||
const ADMIN_USER_DELETE_REQUEST = 'ADMIN_USER_DELETE_REQUEST' as const;
|
||||
const ADMIN_USER_DELETE_SUCCESS = 'ADMIN_USER_DELETE_SUCCESS' as const;
|
||||
const ADMIN_USER_DELETE_FAIL = 'ADMIN_USER_DELETE_FAIL' as const;
|
||||
|
||||
const ADMIN_USERS_APPROVE_REQUEST = 'ADMIN_USERS_APPROVE_REQUEST' as const;
|
||||
const ADMIN_USERS_APPROVE_SUCCESS = 'ADMIN_USERS_APPROVE_SUCCESS' as const;
|
||||
const ADMIN_USERS_APPROVE_FAIL = 'ADMIN_USERS_APPROVE_FAIL' as const;
|
||||
const ADMIN_USER_APPROVE_REQUEST = 'ADMIN_USER_APPROVE_REQUEST' as const;
|
||||
const ADMIN_USER_APPROVE_SUCCESS = 'ADMIN_USER_APPROVE_SUCCESS' as const;
|
||||
const ADMIN_USER_APPROVE_FAIL = 'ADMIN_USER_APPROVE_FAIL' as const;
|
||||
|
||||
const ADMIN_USERS_DEACTIVATE_REQUEST = 'ADMIN_USERS_DEACTIVATE_REQUEST' as const;
|
||||
const ADMIN_USERS_DEACTIVATE_SUCCESS = 'ADMIN_USERS_DEACTIVATE_SUCCESS' as const;
|
||||
const ADMIN_USERS_DEACTIVATE_FAIL = 'ADMIN_USERS_DEACTIVATE_FAIL' as const;
|
||||
const ADMIN_USER_DEACTIVATE_REQUEST = 'ADMIN_USER_DEACTIVATE_REQUEST' as const;
|
||||
const ADMIN_USER_DEACTIVATE_SUCCESS = 'ADMIN_USER_DEACTIVATE_SUCCESS' as const;
|
||||
const ADMIN_USER_DEACTIVATE_FAIL = 'ADMIN_USER_DEACTIVATE_FAIL' as const;
|
||||
|
||||
const ADMIN_STATUS_DELETE_REQUEST = 'ADMIN_STATUS_DELETE_REQUEST' as const;
|
||||
const ADMIN_STATUS_DELETE_SUCCESS = 'ADMIN_STATUS_DELETE_SUCCESS' as const;
|
||||
|
@ -57,14 +57,6 @@ const ADMIN_USERS_UNTAG_REQUEST = 'ADMIN_USERS_UNTAG_REQUEST' as const;
|
|||
const ADMIN_USERS_UNTAG_SUCCESS = 'ADMIN_USERS_UNTAG_SUCCESS' as const;
|
||||
const ADMIN_USERS_UNTAG_FAIL = 'ADMIN_USERS_UNTAG_FAIL' as const;
|
||||
|
||||
const ADMIN_ADD_PERMISSION_GROUP_REQUEST = 'ADMIN_ADD_PERMISSION_GROUP_REQUEST' as const;
|
||||
const ADMIN_ADD_PERMISSION_GROUP_SUCCESS = 'ADMIN_ADD_PERMISSION_GROUP_SUCCESS' as const;
|
||||
const ADMIN_ADD_PERMISSION_GROUP_FAIL = 'ADMIN_ADD_PERMISSION_GROUP_FAIL' as const;
|
||||
|
||||
const ADMIN_REMOVE_PERMISSION_GROUP_REQUEST = 'ADMIN_REMOVE_PERMISSION_GROUP_REQUEST' as const;
|
||||
const ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS = 'ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS' as const;
|
||||
const ADMIN_REMOVE_PERMISSION_GROUP_FAIL = 'ADMIN_REMOVE_PERMISSION_GROUP_FAIL' as const;
|
||||
|
||||
const ADMIN_USER_INDEX_EXPAND_FAIL = 'ADMIN_USER_INDEX_EXPAND_FAIL' as const;
|
||||
const ADMIN_USER_INDEX_EXPAND_REQUEST = 'ADMIN_USER_INDEX_EXPAND_REQUEST' as const;
|
||||
const ADMIN_USER_INDEX_EXPAND_SUCCESS = 'ADMIN_USER_INDEX_EXPAND_SUCCESS' as const;
|
||||
|
@ -110,261 +102,94 @@ const updateSoapboxConfig = (data: Record<string, any>) =>
|
|||
return dispatch(updateConfig(params));
|
||||
};
|
||||
|
||||
const fetchMastodonReports = (params: Record<string, any>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).request('/api/v1/admin/reports', { params })
|
||||
.then(({ json: reports }) => {
|
||||
reports.forEach((report: APIEntity) => {
|
||||
dispatch(importFetchedAccount(report.account?.account));
|
||||
dispatch(importFetchedAccount(report.target_account?.account));
|
||||
dispatch(importFetchedStatuses(report.statuses));
|
||||
});
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_SUCCESS, reports, params });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
||||
});
|
||||
|
||||
const fetchPleromaReports = (params: Record<string, any>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).request('/api/v1/pleroma/admin/reports', { params })
|
||||
.then(({ json: { reports } }) => {
|
||||
reports.forEach((report: APIEntity) => {
|
||||
dispatch(importFetchedAccount(report.account));
|
||||
dispatch(importFetchedAccount(report.actor));
|
||||
dispatch(importFetchedStatuses(report.statuses));
|
||||
});
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_SUCCESS, reports, params });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
||||
});
|
||||
|
||||
const fetchReports = (params: Record<string, any> = {}) =>
|
||||
const fetchReports = (params?: AdminGetReportsParams) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
const features = state.auth.client.features;
|
||||
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_REQUEST, params });
|
||||
|
||||
if (features.mastodonAdmin) {
|
||||
return dispatch(fetchMastodonReports(params));
|
||||
} else {
|
||||
const { resolved } = params;
|
||||
|
||||
return dispatch(fetchPleromaReports({
|
||||
state: resolved === false ? 'open' : (resolved ? 'resolved' : null),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const patchMastodonReports = (reports: { id: string; state: string }[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const client = getClient(getState);
|
||||
|
||||
return Promise.all(reports.map(({ id, state }) =>
|
||||
client.request(`/api/v1/admin/reports/${id}/${state === 'resolved' ? 'reopen' : 'resolve'}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
.then(() => {
|
||||
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
||||
}),
|
||||
));
|
||||
};
|
||||
|
||||
const patchPleromaReports = (reports: { id: string; state: string }[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).request('/api/v1/pleroma/admin/reports', {
|
||||
method: 'PATCH',
|
||||
body: reports,
|
||||
}).then(() => {
|
||||
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
||||
});
|
||||
|
||||
const patchReports = (ids: string[], reportState: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
const features = state.auth.client.features;
|
||||
|
||||
const reports = ids.map(id => ({ id, state: reportState }));
|
||||
|
||||
dispatch({ type: ADMIN_REPORTS_PATCH_REQUEST, reports });
|
||||
|
||||
if (features.mastodonAdmin) {
|
||||
return dispatch(patchMastodonReports(reports));
|
||||
} else {
|
||||
return dispatch(patchPleromaReports(reports));
|
||||
}
|
||||
};
|
||||
|
||||
const closeReports = (ids: string[]) =>
|
||||
patchReports(ids, 'closed');
|
||||
|
||||
const fetchMastodonUsers = (filters: string[], page: number, query: string | null | undefined, pageSize: number, next?: string | null) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const params: Record<string, any> = {
|
||||
username: query,
|
||||
};
|
||||
|
||||
if (filters.includes('local')) params.local = true;
|
||||
if (filters.includes('active')) params.active = true;
|
||||
if (filters.includes('need_approval')) params.pending = true;
|
||||
|
||||
return getClient(getState).request(next || '/api/v1/admin/accounts', { params })
|
||||
.then((response) => {
|
||||
const accounts = response.json;
|
||||
const next = getNextLink(response);
|
||||
|
||||
const count = next
|
||||
? page * pageSize + 1
|
||||
: (page - 1) * pageSize + accounts.length;
|
||||
|
||||
dispatch(importFetchedAccounts(accounts.map(({ account }: APIEntity) => account)));
|
||||
dispatch(fetchRelationships(accounts.map((account: APIEntity) => account.id)));
|
||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: accounts, count, pageSize, filters, page, next: next || false });
|
||||
return { users: accounts, count, pageSize, next: next || false };
|
||||
}).catch(error =>
|
||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize }),
|
||||
);
|
||||
};
|
||||
|
||||
const fetchPleromaUsers = (filters: string[], page: number, query?: string | null, pageSize?: number) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const params: Record<string, any> = { filters: filters.join(), page, page_size: pageSize };
|
||||
if (query) params.query = query;
|
||||
|
||||
return getClient(getState).request('/api/v1/pleroma/admin/users', { params })
|
||||
.then(({ json: { users, count, page_size: pageSize } }) => {
|
||||
dispatch(fetchRelationships(users.map((user: APIEntity) => user.id)));
|
||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users, count, pageSize, filters, page });
|
||||
return { users, count, pageSize };
|
||||
}).catch(error =>
|
||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize }),
|
||||
);
|
||||
};
|
||||
|
||||
const fetchUsers = (filters: string[] = [], page = 1, query?: string | null, pageSize = 50, next?: string | null) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
const features = state.auth.client.features;
|
||||
|
||||
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, filters, page, pageSize });
|
||||
|
||||
if (features.mastodonAdmin) {
|
||||
return dispatch(fetchMastodonUsers(filters, page, query, pageSize, next));
|
||||
} else {
|
||||
return dispatch(fetchPleromaUsers(filters, page, query, pageSize));
|
||||
}
|
||||
};
|
||||
|
||||
const deactivateMastodonUsers = (accountIds: string[], reportId?: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const client = getClient(getState);
|
||||
|
||||
return Promise.all(accountIds.map(accountId => {
|
||||
client.request(`/api/v1/admin/accounts/${accountId}/action`, {
|
||||
method: 'POST',
|
||||
body: { type: 'disable', report_id: reportId },
|
||||
})
|
||||
.then(() => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_SUCCESS, accountIds: [accountId] });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds: [accountId] });
|
||||
return getClient(state).admin.reports.getReports(params)
|
||||
.then(({ items }) => {
|
||||
items.forEach((report) => {
|
||||
if (report.account?.account) dispatch(importFetchedAccount(report.account.account));
|
||||
if (report.target_account?.account) dispatch(importFetchedAccount(report.target_account.account));
|
||||
dispatch(importFetchedStatuses(report.statuses));
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_SUCCESS, reports: items, params });
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
const deactivatePleromaUsers = (accountIds: string[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||
return getClient(getState).request('/api/v1/pleroma/admin/users/deactivate', {
|
||||
method: 'PATCH',
|
||||
body: nicknames,
|
||||
})
|
||||
.then(({ json: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds });
|
||||
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
||||
});
|
||||
};
|
||||
|
||||
const deactivateUsers = (accountIds: string[], reportId?: string) =>
|
||||
const closeReport = (reportId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
const features = state.auth.client.features;
|
||||
dispatch({ type: ADMIN_REPORT_PATCH_REQUEST, reportId });
|
||||
|
||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_REQUEST, accountIds });
|
||||
|
||||
if (features.mastodonAdmin) {
|
||||
return dispatch(deactivateMastodonUsers(accountIds, reportId));
|
||||
} else {
|
||||
return dispatch(deactivatePleromaUsers(accountIds));
|
||||
}
|
||||
};
|
||||
|
||||
const deleteUsers = (accountIds: string[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountIds });
|
||||
return getClient(getState).request('/api/v1/pleroma/admin/users', {
|
||||
method: 'DELETE', body: { nicknames },
|
||||
}).then(({ json: nicknames }) => {
|
||||
dispatch({ type: ADMIN_USERS_DELETE_SUCCESS, nicknames, accountIds });
|
||||
return getClient(state).admin.reports.resolveReport(reportId).then(() => {
|
||||
dispatch({ type: ADMIN_REPORT_PATCH_SUCCESS, reportId });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountIds });
|
||||
dispatch({ type: ADMIN_REPORT_PATCH_FAIL, error, reportId });
|
||||
});
|
||||
};
|
||||
|
||||
const approveMastodonUsers = (accountIds: string[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const client = getClient(getState);
|
||||
return Promise.all(accountIds.map(accountId => {
|
||||
client.request(`/api/v1/admin/accounts/${accountId}/approve`, { method: 'POST' })
|
||||
.then(({ json: user }) => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users: [user], accountIds: [accountId] });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds: [accountId] });
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
const approvePleromaUsers = (accountIds: string[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||
return getClient(getState).request('/api/v1/pleroma/admin/users/approve', {
|
||||
method: 'POST', body: { nicknames },
|
||||
}).then(({ json: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
|
||||
const approveUsers = (accountIds: string[]) =>
|
||||
const fetchUsers = (params?: AdminGetAccountsParams) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
const features = state.auth.client.features;
|
||||
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, params });
|
||||
|
||||
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountIds });
|
||||
return getClient(state).admin.accounts.getAccounts(params).then((res) => {
|
||||
dispatch(importFetchedAccounts(res.items.map(({ account }) => account).filter((account): account is Account => account !== null)));
|
||||
dispatch(fetchRelationships(res.items.map((account) => account.id)));
|
||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: res.items, params, next: res.next });
|
||||
return res;
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, params });
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
if (features.mastodonAdmin) {
|
||||
return dispatch(approveMastodonUsers(accountIds));
|
||||
} else {
|
||||
return dispatch(approvePleromaUsers(accountIds));
|
||||
}
|
||||
const deactivateUser = (accountId: string, report_id?: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
dispatch({ type: ADMIN_USER_DEACTIVATE_REQUEST, accountId });
|
||||
|
||||
return getClient(state).admin.accounts.performAccountAction(accountId, 'suspend', { report_id });
|
||||
};
|
||||
|
||||
const deleteUser = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch({ type: ADMIN_USER_DELETE_REQUEST, accountId });
|
||||
|
||||
return getClient(getState).admin.accounts.deleteAccount(accountId)
|
||||
.then(() => {
|
||||
dispatch({ type: ADMIN_USER_DELETE_SUCCESS, accountId });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USER_DELETE_FAIL, error, accountId });
|
||||
});
|
||||
};
|
||||
|
||||
const approveUser = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
dispatch({ type: ADMIN_USER_APPROVE_REQUEST, accountId });
|
||||
|
||||
return getClient(state).admin.accounts.approveAccount(accountId)
|
||||
.then((user) => {
|
||||
dispatch({ type: ADMIN_USER_APPROVE_SUCCESS, user, accountId });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USER_APPROVE_FAIL, error, accountId });
|
||||
});
|
||||
};
|
||||
|
||||
const deleteStatus = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch({ type: ADMIN_STATUS_DELETE_REQUEST, statusId });
|
||||
return getClient(getState).request(`/api/v1/pleroma/admin/statuses/${statusId}`, { method: 'DELETE' })
|
||||
return getClient(getState).admin.statuses.deleteStatus(statusId)
|
||||
.then(() => {
|
||||
dispatch({ type: ADMIN_STATUS_DELETE_SUCCESS, statusId });
|
||||
}).catch(error => {
|
||||
|
@ -375,13 +200,12 @@ const deleteStatus = (statusId: string) =>
|
|||
const toggleStatusSensitivity = (statusId: string, sensitive: boolean) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST, statusId });
|
||||
return getClient(getState).request(`/api/v1/pleroma/admin/statuses/${statusId}`, {
|
||||
method: 'PUT', body: { sensitive: !sensitive },
|
||||
}).then(() => {
|
||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS, statusId });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, statusId });
|
||||
});
|
||||
return getClient(getState).admin.statuses.updateStatus(statusId, { sensitive: !sensitive })
|
||||
.then(() => {
|
||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS, statusId });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, statusId });
|
||||
});
|
||||
};
|
||||
|
||||
const tagUsers = (accountIds: string[], tags: string[]) =>
|
||||
|
@ -432,52 +256,17 @@ const setBadges = (accountId: string, oldTags: string[], newTags: string[]) =>
|
|||
return dispatch(setTags(accountId, oldBadges, newBadges));
|
||||
};
|
||||
|
||||
const addPermission = (accountIds: string[], permissionGroup: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
||||
return getClient(getState).request(`/api/v1/pleroma/admin/users/permission_group/${permissionGroup}`, {
|
||||
method: 'POST', body: { nicknames },
|
||||
}).then(({ json: data }) => {
|
||||
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_SUCCESS, accountIds, permissionGroup, data });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
||||
});
|
||||
};
|
||||
|
||||
const removePermission = (accountIds: string[], permissionGroup: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const nicknames = accountIdsToAccts(getState(), accountIds);
|
||||
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
||||
return getClient(getState).request(`/api/v1/pleroma/admin/users/permission_group/${permissionGroup}`, {
|
||||
method: 'DELETE', body: { nicknames },
|
||||
}).then(({ json: data }) => {
|
||||
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS, accountIds, permissionGroup, data });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
||||
});
|
||||
};
|
||||
|
||||
const promoteToAdmin = (accountId: string) =>
|
||||
(dispatch: AppDispatch) =>
|
||||
Promise.all([
|
||||
dispatch(addPermission([accountId], 'admin')),
|
||||
dispatch(removePermission([accountId], 'moderator')),
|
||||
]);
|
||||
(_dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).admin.accounts.promoteToAdmin(accountId);
|
||||
|
||||
const promoteToModerator = (accountId: string) =>
|
||||
(dispatch: AppDispatch) =>
|
||||
Promise.all([
|
||||
dispatch(removePermission([accountId], 'admin')),
|
||||
dispatch(addPermission([accountId], 'moderator')),
|
||||
]);
|
||||
(_dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).admin.accounts.promoteToModerator(accountId);
|
||||
|
||||
const demoteToUser = (accountId: string) =>
|
||||
(dispatch: AppDispatch) =>
|
||||
Promise.all([
|
||||
dispatch(removePermission([accountId], 'admin')),
|
||||
dispatch(removePermission([accountId], 'moderator')),
|
||||
]);
|
||||
(_dispatch: AppDispatch, getState: () => RootState) =>
|
||||
getClient(getState).admin.accounts.demoteToUser(accountId);
|
||||
|
||||
const setRole = (accountId: string, role: 'user' | 'moderator' | 'admin') =>
|
||||
(dispatch: AppDispatch) => {
|
||||
|
@ -495,20 +284,22 @@ const setUserIndexQuery = (query: string) => ({ type: ADMIN_USER_INDEX_QUERY_SET
|
|||
|
||||
const fetchUserIndex = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const { filters, page, query, pageSize, isLoading } = getState().admin_user_index;
|
||||
const { query, isLoading } = getState().admin_user_index;
|
||||
|
||||
if (isLoading) return;
|
||||
|
||||
dispatch({ type: ADMIN_USER_INDEX_FETCH_REQUEST });
|
||||
|
||||
dispatch(fetchUsers(filters.toJS() as string[], page + 1, query, pageSize))
|
||||
.then((data: any) => {
|
||||
if (data.error) {
|
||||
dispatch({ type: ADMIN_USER_INDEX_FETCH_FAIL });
|
||||
} else {
|
||||
const { users, count, next } = (data);
|
||||
dispatch({ type: ADMIN_USER_INDEX_FETCH_SUCCESS, users, count, next });
|
||||
}
|
||||
const params: AdminGetAccountsParams = {
|
||||
origin: 'local',
|
||||
status: 'active',
|
||||
username: query,
|
||||
};
|
||||
|
||||
dispatch(fetchUsers(params))
|
||||
.then((data) => {
|
||||
const { items, total, next } = data;
|
||||
dispatch({ type: ADMIN_USER_INDEX_FETCH_SUCCESS, users: items, total, next, params });
|
||||
}).catch(() => {
|
||||
dispatch({ type: ADMIN_USER_INDEX_FETCH_FAIL });
|
||||
});
|
||||
|
@ -516,20 +307,16 @@ const fetchUserIndex = () =>
|
|||
|
||||
const expandUserIndex = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const { filters, page, query, pageSize, isLoading, next, loaded } = getState().admin_user_index;
|
||||
const { params, next, isLoading, loaded } = getState().admin_user_index;
|
||||
|
||||
if (!loaded || isLoading) return;
|
||||
if (!loaded || isLoading || !next) return;
|
||||
|
||||
dispatch({ type: ADMIN_USER_INDEX_EXPAND_REQUEST });
|
||||
|
||||
dispatch(fetchUsers(filters.toJS() as string[], page + 1, query, pageSize, next))
|
||||
.then((data: any) => {
|
||||
if (data.error) {
|
||||
dispatch({ type: ADMIN_USER_INDEX_EXPAND_FAIL });
|
||||
} else {
|
||||
const { users, count, next } = (data);
|
||||
dispatch({ type: ADMIN_USER_INDEX_EXPAND_SUCCESS, users, count, next });
|
||||
}
|
||||
next()
|
||||
.then((data) => {
|
||||
const { items, total, next } = data;
|
||||
dispatch({ type: ADMIN_USER_INDEX_EXPAND_SUCCESS, users: items, total, next, params });
|
||||
}).catch(() => {
|
||||
dispatch({ type: ADMIN_USER_INDEX_EXPAND_FAIL });
|
||||
});
|
||||
|
@ -557,21 +344,21 @@ export {
|
|||
ADMIN_REPORTS_FETCH_REQUEST,
|
||||
ADMIN_REPORTS_FETCH_SUCCESS,
|
||||
ADMIN_REPORTS_FETCH_FAIL,
|
||||
ADMIN_REPORTS_PATCH_REQUEST,
|
||||
ADMIN_REPORTS_PATCH_SUCCESS,
|
||||
ADMIN_REPORTS_PATCH_FAIL,
|
||||
ADMIN_REPORT_PATCH_REQUEST,
|
||||
ADMIN_REPORT_PATCH_SUCCESS,
|
||||
ADMIN_REPORT_PATCH_FAIL,
|
||||
ADMIN_USERS_FETCH_REQUEST,
|
||||
ADMIN_USERS_FETCH_SUCCESS,
|
||||
ADMIN_USERS_FETCH_FAIL,
|
||||
ADMIN_USERS_DELETE_REQUEST,
|
||||
ADMIN_USERS_DELETE_SUCCESS,
|
||||
ADMIN_USERS_DELETE_FAIL,
|
||||
ADMIN_USERS_APPROVE_REQUEST,
|
||||
ADMIN_USERS_APPROVE_SUCCESS,
|
||||
ADMIN_USERS_APPROVE_FAIL,
|
||||
ADMIN_USERS_DEACTIVATE_REQUEST,
|
||||
ADMIN_USERS_DEACTIVATE_SUCCESS,
|
||||
ADMIN_USERS_DEACTIVATE_FAIL,
|
||||
ADMIN_USER_DELETE_REQUEST,
|
||||
ADMIN_USER_DELETE_SUCCESS,
|
||||
ADMIN_USER_DELETE_FAIL,
|
||||
ADMIN_USER_APPROVE_REQUEST,
|
||||
ADMIN_USER_APPROVE_SUCCESS,
|
||||
ADMIN_USER_APPROVE_FAIL,
|
||||
ADMIN_USER_DEACTIVATE_REQUEST,
|
||||
ADMIN_USER_DEACTIVATE_SUCCESS,
|
||||
ADMIN_USER_DEACTIVATE_FAIL,
|
||||
ADMIN_STATUS_DELETE_REQUEST,
|
||||
ADMIN_STATUS_DELETE_SUCCESS,
|
||||
ADMIN_STATUS_DELETE_FAIL,
|
||||
|
@ -584,12 +371,6 @@ export {
|
|||
ADMIN_USERS_UNTAG_REQUEST,
|
||||
ADMIN_USERS_UNTAG_SUCCESS,
|
||||
ADMIN_USERS_UNTAG_FAIL,
|
||||
ADMIN_ADD_PERMISSION_GROUP_REQUEST,
|
||||
ADMIN_ADD_PERMISSION_GROUP_SUCCESS,
|
||||
ADMIN_ADD_PERMISSION_GROUP_FAIL,
|
||||
ADMIN_REMOVE_PERMISSION_GROUP_REQUEST,
|
||||
ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS,
|
||||
ADMIN_REMOVE_PERMISSION_GROUP_FAIL,
|
||||
ADMIN_USER_INDEX_EXPAND_FAIL,
|
||||
ADMIN_USER_INDEX_EXPAND_REQUEST,
|
||||
ADMIN_USER_INDEX_EXPAND_SUCCESS,
|
||||
|
@ -601,19 +382,17 @@ export {
|
|||
updateConfig,
|
||||
updateSoapboxConfig,
|
||||
fetchReports,
|
||||
closeReports,
|
||||
closeReport,
|
||||
fetchUsers,
|
||||
deactivateUsers,
|
||||
deleteUsers,
|
||||
approveUsers,
|
||||
deactivateUser,
|
||||
deleteUser,
|
||||
approveUser,
|
||||
deleteStatus,
|
||||
toggleStatusSensitivity,
|
||||
tagUsers,
|
||||
untagUsers,
|
||||
setTags,
|
||||
setBadges,
|
||||
addPermission,
|
||||
removePermission,
|
||||
promoteToAdmin,
|
||||
promoteToModerator,
|
||||
demoteToUser,
|
||||
|
|
|
@ -110,18 +110,19 @@ const isBroken = (status: BaseStatus) => {
|
|||
}
|
||||
};
|
||||
|
||||
const importFetchedStatuses = (statuses: Array<BaseStatus>) => (dispatch: AppDispatch) => {
|
||||
const importFetchedStatuses = (statuses: Array<Omit<BaseStatus, 'account'> & { account: BaseAccount | null }>) => (dispatch: AppDispatch) => {
|
||||
const accounts: Array<BaseAccount> = [];
|
||||
const normalStatuses: Array<BaseStatus> = [];
|
||||
const polls: Array<Poll> = [];
|
||||
|
||||
const processStatus = (status: BaseStatus) => {
|
||||
if (status.account === null) return;
|
||||
// Skip broken statuses
|
||||
if (isBroken(status)) return;
|
||||
|
||||
normalStatuses.push(status);
|
||||
|
||||
accounts.push(status.account);
|
||||
if (status.account !== null) accounts.push(status.account);
|
||||
// if (status.accounts) {
|
||||
// accounts.push(...status.accounts);
|
||||
// }
|
||||
|
@ -140,7 +141,7 @@ const importFetchedStatuses = (statuses: Array<BaseStatus>) => (dispatch: AppDis
|
|||
}
|
||||
};
|
||||
|
||||
statuses.forEach(processStatus);
|
||||
(statuses as Array<BaseStatus>).forEach(processStatus);
|
||||
|
||||
dispatch(importPolls(polls));
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, IntlShape } from 'react-intl';
|
||||
|
||||
import { fetchAccountByUsername } from 'soapbox/actions/accounts';
|
||||
import { deactivateUsers, deleteUsers, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin';
|
||||
import { deactivateUser, deleteUser, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin';
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import OutlineBox from 'soapbox/components/outline-box';
|
||||
import { Stack, Text } from 'soapbox/components/ui';
|
||||
|
@ -62,7 +62,7 @@ const deactivateUserModal = (intl: IntlShape, accountId: string, afterConfirm =
|
|||
message,
|
||||
confirm: intl.formatMessage(messages.deactivateUserConfirm, { name }),
|
||||
onConfirm: () => {
|
||||
dispatch(deactivateUsers([accountId])).then(() => {
|
||||
dispatch(deactivateUser(accountId)).then(() => {
|
||||
const message = intl.formatMessage(messages.userDeactivated, { acct });
|
||||
toast.success(message);
|
||||
afterConfirm();
|
||||
|
@ -100,7 +100,7 @@ const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () =
|
|||
confirm,
|
||||
checkbox,
|
||||
onConfirm: () => {
|
||||
dispatch(deleteUsers([accountId])).then(() => {
|
||||
dispatch(deleteUser(accountId)).then(() => {
|
||||
const message = intl.formatMessage(messages.userDeleted, { acct });
|
||||
dispatch(fetchAccountByUsername(acct));
|
||||
toast.success(message);
|
||||
|
|
|
@ -209,7 +209,7 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
|
|||
}
|
||||
|
||||
if (maxId?.includes('+')) {
|
||||
const ids = maxId.split('+');
|
||||
const ids = maxId.split('+');
|
||||
|
||||
maxId = ids[ids.length - 1];
|
||||
}
|
||||
|
|
|
@ -2,25 +2,12 @@
|
|||
* API: HTTP client and utilities.
|
||||
* @module soapbox/api
|
||||
*/
|
||||
import LinkHeader from 'http-link-header';
|
||||
|
||||
import * as BuildConfig from 'soapbox/build-config';
|
||||
import { RootState } from 'soapbox/store';
|
||||
import { buildFullPath } from 'soapbox/utils/url';
|
||||
|
||||
type PlfeResponse<T = any> = Response & { data: string; json: T };
|
||||
|
||||
/**
|
||||
Parse Link headers, mostly for pagination.
|
||||
@param {object} response - Fetch API response object
|
||||
@returns {object} Link object
|
||||
*/
|
||||
const getLinks = (response: Pick<Response, 'headers'>): LinkHeader =>
|
||||
new LinkHeader(response.headers?.get('link') || undefined);
|
||||
|
||||
const getNextLink = (response: Pick<Response, 'headers'>): string | undefined =>
|
||||
getLinks(response).refs.find(link => link.rel === 'next')?.uri;
|
||||
|
||||
/**
|
||||
* Dumb client for grabbing static files.
|
||||
* It uses FE_SUBDIRECTORY and parses JSON if possible.
|
||||
|
@ -53,8 +40,6 @@ const getClient = (state: RootState | (() => RootState)) => {
|
|||
|
||||
export {
|
||||
type PlfeResponse,
|
||||
getLinks,
|
||||
getNextLink,
|
||||
staticFetch,
|
||||
getClient,
|
||||
};
|
||||
|
|
|
@ -212,7 +212,7 @@ const SidebarNavigation = () => {
|
|||
text={<FormattedMessage id='tabs_bar.settings' defaultMessage='Settings' />}
|
||||
/>
|
||||
|
||||
{account.is_admin || account.is_moderator && (
|
||||
{(account.is_admin || account.is_moderator) && (
|
||||
<SidebarNavigationLink
|
||||
to='/soapbox/admin'
|
||||
icon={require('@tabler/icons/outline/dashboard.svg')}
|
||||
|
|
|
@ -188,7 +188,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
|||
|
||||
if (spoilerText) {
|
||||
output.push(
|
||||
<Text size='2xl' weight='medium'>
|
||||
<Text key='spoiler' size='2xl' weight='medium'>
|
||||
<span dangerouslySetInnerHTML={{ __html: spoilerText }} />
|
||||
{expandable && (
|
||||
<Button
|
||||
|
|
|
@ -23,14 +23,16 @@ const LatestAccountsPanel: React.FC<ILatestAccountsPanel> = ({ limit = 5 }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
const accountIds = useAppSelector<ImmutableOrderedSet<string>>((state) => state.admin.get('latestUsers').take(limit));
|
||||
|
||||
const [total, setTotal] = useState(accountIds.size);
|
||||
const [total, setTotal] = useState<number | undefined>(accountIds.size);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchUsers(['local', 'active'], 1, null, limit))
|
||||
.then((value) => {
|
||||
setTotal((value as { count: number }).count);
|
||||
})
|
||||
.catch(() => {});
|
||||
dispatch(fetchUsers({
|
||||
origin: 'local',
|
||||
status: 'active',
|
||||
limit,
|
||||
})).then((value) => {
|
||||
setTotal(value.total);
|
||||
}).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const handleAction = () => {
|
||||
|
|
|
@ -8,8 +8,7 @@ import StatusMedia from 'soapbox/components/status-media';
|
|||
import { HStack, Stack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
|
||||
import type { Status } from 'soapbox/normalizers';
|
||||
import type { AdminReport } from 'soapbox/types/entities';
|
||||
import type { SelectedStatus } from 'soapbox/selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
viewStatus: { id: 'admin.reports.actions.view_status', defaultMessage: 'View post' },
|
||||
|
@ -17,8 +16,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IReportStatus {
|
||||
status: Status;
|
||||
report?: AdminReport;
|
||||
status: SelectedStatus;
|
||||
}
|
||||
|
||||
const ReportStatus: React.FC<IReportStatus> = ({ status }) => {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useCallback, useState } from 'react';
|
|||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { closeReports } from 'soapbox/actions/admin';
|
||||
import { closeReport } from 'soapbox/actions/admin';
|
||||
import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation';
|
||||
import DropdownMenu from 'soapbox/components/dropdown-menu';
|
||||
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper';
|
||||
|
@ -13,10 +13,6 @@ import toast from 'soapbox/toast';
|
|||
|
||||
import ReportStatus from './report-status';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
import type { Account, Status } from 'soapbox/normalizers';
|
||||
import type { AdminReport } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
reportClosed: { id: 'admin.reports.report_closed_message', defaultMessage: 'Report on @{name} was closed' },
|
||||
deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate @{name}' },
|
||||
|
@ -33,14 +29,14 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
|
||||
const getReport = useCallback(makeGetReport(), []);
|
||||
|
||||
const report = useAppSelector((state) => getReport(state, id) as AdminReport | undefined);
|
||||
const report = useAppSelector((state) => getReport(state, id));
|
||||
|
||||
const [accordionExpanded, setAccordionExpanded] = useState(false);
|
||||
|
||||
if (!report) return null;
|
||||
|
||||
const account = report.account as Account;
|
||||
const targetAccount = report.target_account as Account;
|
||||
const account = report.account;
|
||||
const targetAccount = report.target_account!;
|
||||
|
||||
const makeMenu = () => [{
|
||||
text: intl.formatMessage(messages.deactivateUser, { name: targetAccount.username }),
|
||||
|
@ -54,7 +50,7 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
}];
|
||||
|
||||
const handleCloseReport = () => {
|
||||
dispatch(closeReports([report.id])).then(() => {
|
||||
dispatch(closeReport(report.id)).then(() => {
|
||||
const message = intl.formatMessage(messages.reportClosed, { name: targetAccount.username as string });
|
||||
toast.success(message);
|
||||
}).catch(() => {});
|
||||
|
@ -62,12 +58,12 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
|
||||
const handleDeactivateUser = () => {
|
||||
const accountId = targetAccount.id;
|
||||
dispatch(deactivateUserModal(intl, accountId, () => handleCloseReport()));
|
||||
dispatch(deactivateUserModal(intl, accountId!, () => handleCloseReport()));
|
||||
};
|
||||
|
||||
const handleDeleteUser = () => {
|
||||
const accountId = targetAccount.id as string;
|
||||
dispatch(deleteUserModal(intl, accountId, () => handleCloseReport()));
|
||||
dispatch(deleteUserModal(intl, accountId!, () => handleCloseReport()));
|
||||
};
|
||||
|
||||
const handleAccordionToggle = (setting: boolean) => {
|
||||
|
@ -75,10 +71,10 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
};
|
||||
|
||||
const menu = makeMenu();
|
||||
const statuses = report.statuses as ImmutableList<Status>;
|
||||
const statusCount = statuses.count();
|
||||
const acct = targetAccount.acct as string;
|
||||
const reporterAcct = account.acct as string;
|
||||
const statuses = report.statuses;
|
||||
const statusCount = statuses.length;
|
||||
const acct = targetAccount.acct;
|
||||
const reporterAcct = account?.acct;
|
||||
|
||||
return (
|
||||
<HStack space={3} className='p-3' key={report.id}>
|
||||
|
@ -86,7 +82,7 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
<Link to={`/@${acct}`} title={acct}>
|
||||
<Avatar
|
||||
src={targetAccount.avatar}
|
||||
alt={account.avatar_description}
|
||||
alt={targetAccount.avatar_description}
|
||||
size={32}
|
||||
className='overflow-hidden'
|
||||
/>
|
||||
|
@ -116,7 +112,6 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
{statuses.map(status => (
|
||||
<ReportStatus
|
||||
key={status.id}
|
||||
report={report}
|
||||
status={status}
|
||||
/>
|
||||
))}
|
||||
|
@ -125,26 +120,28 @@ const Report: React.FC<IReport> = ({ id }) => {
|
|||
)}
|
||||
|
||||
<Stack>
|
||||
{(report.comment || '').length > 0 && (
|
||||
{!!report.comment && report.comment.length > 0 && (
|
||||
<Text
|
||||
tag='blockquote'
|
||||
dangerouslySetInnerHTML={{ __html: report.comment }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<HStack space={1}>
|
||||
<Text theme='muted' tag='span'>—</Text>
|
||||
{!!account && (
|
||||
<HStack space={1}>
|
||||
<Text theme='muted' tag='span'>—</Text>
|
||||
|
||||
<HoverRefWrapper accountId={account.id} inline>
|
||||
<Link
|
||||
to={`/@${reporterAcct}`}
|
||||
title={reporterAcct}
|
||||
className='text-primary-600 hover:underline dark:text-accent-blue'
|
||||
>
|
||||
@{reporterAcct}
|
||||
</Link>
|
||||
</HoverRefWrapper>
|
||||
</HStack>
|
||||
<HoverRefWrapper accountId={account.id} inline>
|
||||
<Link
|
||||
to={`/@${reporterAcct}`}
|
||||
title={reporterAcct}
|
||||
className='text-primary-600 hover:underline dark:text-accent-blue'
|
||||
>
|
||||
@{reporterAcct}
|
||||
</Link>
|
||||
</HoverRefWrapper>
|
||||
</HStack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
import { approveUsers, deleteUsers } from 'soapbox/actions/admin';
|
||||
import { approveUser, deleteUser } from 'soapbox/actions/admin';
|
||||
import { useAccount } from 'soapbox/api/hooks';
|
||||
import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons';
|
||||
import { Stack, HStack, Text } from 'soapbox/components/ui';
|
||||
|
@ -19,8 +19,8 @@ const UnapprovedAccount: React.FC<IUnapprovedAccount> = ({ accountId }) => {
|
|||
|
||||
if (!account) return null;
|
||||
|
||||
const handleApprove = () => dispatch(approveUsers([account.id]));
|
||||
const handleReject = () => dispatch(deleteUsers([account.id]));
|
||||
const handleApprove = () => dispatch(approveUser(account.id));
|
||||
const handleReject = () => dispatch(deleteUser(account.id));
|
||||
|
||||
return (
|
||||
<HStack space={4} justifyContent='between'>
|
||||
|
|
|
@ -20,7 +20,10 @@ const AwaitingApproval: React.FC = () => {
|
|||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchUsers(['local', 'need_approval']))
|
||||
dispatch(fetchUsers({
|
||||
origin: 'local',
|
||||
status: 'pending',
|
||||
}))
|
||||
.then(() => setLoading(false))
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
|
|
@ -52,7 +52,7 @@ const MODAL_COMPONENTS = {
|
|||
|
||||
type ModalType = keyof typeof MODAL_COMPONENTS | null;
|
||||
|
||||
type BaseModalProps = {
|
||||
type BaseModalProps = {
|
||||
/** Action to close the modal. */
|
||||
onClose(type?: ModalType): void;
|
||||
};
|
||||
|
|
|
@ -392,7 +392,10 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
|
||||
if (account.is_admin || account.is_moderator) {
|
||||
dispatch(fetchReports({ resolved: false }));
|
||||
dispatch(fetchUsers(['local', 'need_approval']));
|
||||
dispatch(fetchUsers({
|
||||
origin: 'local',
|
||||
status: 'pending',
|
||||
}));
|
||||
}
|
||||
|
||||
if (account.is_admin) {
|
||||
|
|
|
@ -24,7 +24,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
const formatTime = (secondsNum: number): string => {
|
||||
let hours: number | string = Math.floor(secondsNum / 3600);
|
||||
let hours: number | string = Math.floor(secondsNum / 3600);
|
||||
let minutes: number | string = Math.floor((secondsNum - (hours * 3600)) / 60);
|
||||
let seconds: number | string = secondsNum - (hours * 3600) - (minutes * 60);
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Admin account normalizer:
|
||||
* Converts API admin-level account information into our internal format.
|
||||
*/
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
|
||||
import type { Account } from 'soapbox/normalizers';
|
||||
import type { EmbeddedEntity } from 'soapbox/types/entities';
|
||||
|
||||
const AdminAccountRecord = ImmutableRecord({
|
||||
account: null as EmbeddedEntity<Account>,
|
||||
approved: false,
|
||||
confirmed: false,
|
||||
created_at: new Date(),
|
||||
disabled: false,
|
||||
domain: '',
|
||||
email: '',
|
||||
id: '',
|
||||
invite_request: null as string | null,
|
||||
ip: null as string | null,
|
||||
ips: ImmutableList<string>(),
|
||||
locale: null as string | null,
|
||||
role: null as 'admin' | 'moderator' | null,
|
||||
sensitized: false,
|
||||
silenced: false,
|
||||
suspended: false,
|
||||
username: '',
|
||||
});
|
||||
|
||||
const normalizePleromaAccount = (account: ImmutableMap<string, any>) => {
|
||||
if (!account.get('account')) {
|
||||
return account.withMutations(account => {
|
||||
account.set('approved', account.get('is_approved'));
|
||||
account.set('confirmed', account.get('is_confirmed'));
|
||||
account.set('disabled', !account.get('is_active'));
|
||||
account.set('invite_request', account.get('registration_reason'));
|
||||
account.set('role', account.getIn(['roles', 'admin']) ? 'admin' : (account.getIn(['roles', 'moderator']) ? 'moderator' : null));
|
||||
});
|
||||
}
|
||||
|
||||
return account;
|
||||
};
|
||||
|
||||
const normalizeAdminAccount = (account: Record<string, any>) => AdminAccountRecord(
|
||||
ImmutableMap(fromJS(account)).withMutations((account: ImmutableMap<string, any>) => {
|
||||
normalizePleromaAccount(account);
|
||||
}),
|
||||
);
|
||||
|
||||
export { AdminAccountRecord, normalizeAdminAccount };
|
|
@ -1,51 +1,14 @@
|
|||
/**
|
||||
* Admin report normalizer:
|
||||
* Converts API admin-level report information into our internal format.
|
||||
*/
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
import type { AdminReport as BaseAdminReport } from 'pl-api';
|
||||
|
||||
import type { Account, Status } from 'soapbox/normalizers';
|
||||
import type { EmbeddedEntity } from 'soapbox/types/entities';
|
||||
|
||||
const AdminReportRecord = ImmutableRecord({
|
||||
account: null as EmbeddedEntity<Account>,
|
||||
action_taken: false,
|
||||
action_taken_by_account: null as EmbeddedEntity<Account> | null,
|
||||
assigned_account: null as EmbeddedEntity<Account> | null,
|
||||
category: '',
|
||||
comment: '',
|
||||
created_at: new Date(),
|
||||
id: '',
|
||||
rules: ImmutableList<string>(),
|
||||
statuses: ImmutableList<EmbeddedEntity<Status>>(),
|
||||
target_account: null as EmbeddedEntity<Account>,
|
||||
updated_at: new Date(),
|
||||
const normalizeAdminReport = (report: BaseAdminReport) => ({
|
||||
...report,
|
||||
account_id: report.account?.id || null,
|
||||
target_account_id: report.target_account?.id || null,
|
||||
action_taken_by_account_id: report.action_taken_by_account?.id || null,
|
||||
assigned_account_id: report.assigned_account?.id || null,
|
||||
status_ids: report.statuses.map(status => status.id),
|
||||
});
|
||||
|
||||
const normalizePleromaReport = (report: ImmutableMap<string, any>) => {
|
||||
if (report.get('actor')){
|
||||
return report.withMutations(report => {
|
||||
report.set('target_account', report.get('account'));
|
||||
report.set('account', report.get('actor'));
|
||||
type AdminReport = ReturnType<typeof normalizeAdminReport>;
|
||||
|
||||
report.set('action_taken', report.get('state') !== 'open');
|
||||
report.set('comment', report.get('content'));
|
||||
report.set('updated_at', report.get('created_at'));
|
||||
});
|
||||
}
|
||||
|
||||
return report;
|
||||
};
|
||||
|
||||
const normalizeAdminReport = (report: Record<string, any>) => AdminReportRecord(
|
||||
ImmutableMap(fromJS(report)).withMutations((report: ImmutableMap<string, any>) => {
|
||||
normalizePleromaReport(report);
|
||||
}),
|
||||
);
|
||||
|
||||
export { AdminReportRecord, normalizeAdminReport };
|
||||
export { normalizeAdminReport, type AdminReport };
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export { normalizeAccount, type Account } from './account';
|
||||
export { AdminAccountRecord, normalizeAdminAccount } from './admin-account';
|
||||
export { AdminReportRecord, normalizeAdminReport } from './admin-report';
|
||||
export { normalizeAdminReport, type AdminReport } from './admin-report';
|
||||
export { normalizeAnnouncement, type Announcement } from './announcement';
|
||||
export { normalizeChatMessage, type ChatMessage } from './chat-message';
|
||||
export { normalizeGroup, type Group } from './group';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Set as ImmutableSet, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
|
||||
import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
ADMIN_USER_INDEX_EXPAND_FAIL,
|
||||
|
@ -10,6 +10,7 @@ import {
|
|||
ADMIN_USER_INDEX_QUERY_SET,
|
||||
} from 'soapbox/actions/admin';
|
||||
|
||||
import type { AdminAccount, AdminGetAccountsParams, PaginatedResponse } from 'pl-api';
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
|
@ -17,12 +18,11 @@ const ReducerRecord = ImmutableRecord({
|
|||
isLoading: false,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
filters: ImmutableSet(['local', 'active']),
|
||||
total: Infinity,
|
||||
pageSize: 50,
|
||||
page: -1,
|
||||
query: '',
|
||||
next: null as string | null,
|
||||
next: null as (() => Promise<PaginatedResponse<AdminAccount>>) | null,
|
||||
params: null as AdminGetAccountsParams | null,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
@ -36,7 +36,7 @@ const admin_user_index = (state: State = ReducerRecord(), action: AnyAction): St
|
|||
.set('isLoading', true)
|
||||
.set('loaded', true)
|
||||
.set('items', ImmutableOrderedSet())
|
||||
.set('total', action.count)
|
||||
.set('total', action.total)
|
||||
.set('page', 0)
|
||||
.set('next', null);
|
||||
case ADMIN_USER_INDEX_FETCH_SUCCESS:
|
||||
|
@ -44,7 +44,7 @@ const admin_user_index = (state: State = ReducerRecord(), action: AnyAction): St
|
|||
.set('isLoading', false)
|
||||
.set('loaded', true)
|
||||
.set('items', ImmutableOrderedSet(action.users.map((user: APIEntity) => user.id)))
|
||||
.set('total', action.count)
|
||||
.set('total', action.total)
|
||||
.set('page', 1)
|
||||
.set('next', action.next);
|
||||
case ADMIN_USER_INDEX_FETCH_FAIL:
|
||||
|
@ -59,7 +59,7 @@ const admin_user_index = (state: State = ReducerRecord(), action: AnyAction): St
|
|||
.set('isLoading', false)
|
||||
.set('loaded', true)
|
||||
.set('items', state.items.union(action.users.map((user: APIEntity) => user.id)))
|
||||
.set('total', action.count)
|
||||
.set('total', action.total)
|
||||
.set('page', 1)
|
||||
.set('next', action.next);
|
||||
default:
|
||||
|
|
|
@ -1,36 +1,34 @@
|
|||
import {
|
||||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
Set as ImmutableSet,
|
||||
Record as ImmutableRecord,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
fromJS,
|
||||
is,
|
||||
} from 'immutable';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import {
|
||||
ADMIN_CONFIG_FETCH_SUCCESS,
|
||||
ADMIN_CONFIG_UPDATE_SUCCESS,
|
||||
ADMIN_REPORTS_FETCH_SUCCESS,
|
||||
ADMIN_REPORTS_PATCH_REQUEST,
|
||||
ADMIN_REPORTS_PATCH_SUCCESS,
|
||||
ADMIN_REPORT_PATCH_REQUEST,
|
||||
ADMIN_REPORT_PATCH_SUCCESS,
|
||||
ADMIN_USERS_FETCH_SUCCESS,
|
||||
ADMIN_USERS_DELETE_REQUEST,
|
||||
ADMIN_USERS_DELETE_SUCCESS,
|
||||
ADMIN_USERS_APPROVE_REQUEST,
|
||||
ADMIN_USERS_APPROVE_SUCCESS,
|
||||
ADMIN_USER_DELETE_REQUEST,
|
||||
ADMIN_USER_DELETE_SUCCESS,
|
||||
ADMIN_USER_APPROVE_REQUEST,
|
||||
ADMIN_USER_APPROVE_SUCCESS,
|
||||
} from 'soapbox/actions/admin';
|
||||
import { normalizeAdminReport, normalizeAdminAccount } from 'soapbox/normalizers';
|
||||
import { normalizeId } from 'soapbox/utils/normalizers';
|
||||
import { normalizeAdminReport, type AdminReport } from 'soapbox/normalizers';
|
||||
|
||||
import type { AdminAccount, AdminGetAccountsParams, AdminReport as BaseAdminReport } from 'pl-api';
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
import type { Config } from 'soapbox/utils/config-db';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
reports: ImmutableMap<string, ReducerAdminReport>(),
|
||||
reports: ImmutableMap<string, MinifiedReport>(),
|
||||
openReports: ImmutableOrderedSet<string>(),
|
||||
users: ImmutableMap<string, ReducerAdminAccount>(),
|
||||
users: ImmutableMap<string, MinifiedUser>(),
|
||||
latestUsers: ImmutableOrderedSet<string>(),
|
||||
awaitingApproval: ImmutableOrderedSet<string>(),
|
||||
configs: ImmutableList<Config>(),
|
||||
|
@ -39,80 +37,50 @@ const ReducerRecord = ImmutableRecord({
|
|||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
type AdminAccountRecord = ReturnType<typeof normalizeAdminAccount>;
|
||||
type AdminReportRecord = ReturnType<typeof normalizeAdminReport>;
|
||||
|
||||
interface ReducerAdminAccount extends AdminAccountRecord {
|
||||
account: string | null;
|
||||
}
|
||||
|
||||
interface ReducerAdminReport extends AdminReportRecord {
|
||||
account: string | null;
|
||||
target_account: string | null;
|
||||
action_taken_by_account: string | null;
|
||||
assigned_account: string | null;
|
||||
statuses: ImmutableList<string | null>;
|
||||
}
|
||||
|
||||
// Lol https://javascript.plainenglish.io/typescript-essentials-conditionally-filter-types-488705bfbf56
|
||||
type FilterConditionally<Source, Condition> = Pick<Source, {[K in keyof Source]: Source[K] extends Condition ? K : never}[keyof Source]>;
|
||||
|
||||
type SetKeys = keyof FilterConditionally<State, ImmutableOrderedSet<string>>;
|
||||
|
||||
type APIReport = { id: string; state: string; statuses: any[] };
|
||||
type APIUser = { id: string; email: string; nickname: string; registration_reason: string };
|
||||
|
||||
type Filter = 'local' | 'need_approval' | 'active';
|
||||
|
||||
const FILTER_UNAPPROVED: Filter[] = ['local', 'need_approval'];
|
||||
const FILTER_LATEST: Filter[] = ['local', 'active'];
|
||||
|
||||
const filtersMatch = (f1: string[], f2: string[]) => is(ImmutableSet(f1), ImmutableSet(f2));
|
||||
const toIds = (items: any[]) => items.map(item => item.id);
|
||||
|
||||
const mergeSet = (state: State, key: SetKeys, users: APIUser[]): State => {
|
||||
const mergeSet = (state: State, key: SetKeys, users: Array<AdminAccount>): State => {
|
||||
const newIds = toIds(users);
|
||||
return state.update(key, (ids: ImmutableOrderedSet<string>) => ids.union(newIds));
|
||||
};
|
||||
|
||||
const replaceSet = (state: State, key: SetKeys, users: APIUser[]): State => {
|
||||
const replaceSet = (state: State, key: SetKeys, users: Array<AdminAccount>): State => {
|
||||
const newIds = toIds(users);
|
||||
return state.set(key, ImmutableOrderedSet(newIds));
|
||||
};
|
||||
|
||||
const maybeImportUnapproved = (state: State, users: APIUser[], filters: Filter[]): State => {
|
||||
if (filtersMatch(FILTER_UNAPPROVED, filters)) {
|
||||
const maybeImportUnapproved = (state: State, users: Array<AdminAccount>, params?: AdminGetAccountsParams): State => {
|
||||
if (params?.origin === 'local' && params.status === 'pending') {
|
||||
return mergeSet(state, 'awaitingApproval', users);
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const maybeImportLatest = (state: State, users: APIUser[], filters: Filter[], page: number): State => {
|
||||
if (page === 1 && filtersMatch(FILTER_LATEST, filters)) {
|
||||
const maybeImportLatest = (state: State, users: Array<AdminAccount>, params?: AdminGetAccountsParams): State => {
|
||||
if (params?.origin === 'local' && params.status === 'active') {
|
||||
return replaceSet(state, 'latestUsers', users);
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const minifyUser = (user: AdminAccountRecord): ReducerAdminAccount =>
|
||||
user.mergeWith((o, n) => n || o, {
|
||||
account: normalizeId(user.getIn(['account', 'id'])),
|
||||
}) as ReducerAdminAccount;
|
||||
const minifyUser = (user: AdminAccount) => omit(user, ['account']);
|
||||
|
||||
const fixUser = (user: APIEntity): ReducerAdminAccount =>
|
||||
normalizeAdminAccount(user).withMutations(user => {
|
||||
minifyUser(user);
|
||||
}) as ReducerAdminAccount;
|
||||
type MinifiedUser = ReturnType<typeof minifyUser>;
|
||||
|
||||
const importUsers = (state: State, users: APIUser[], filters: Filter[], page: number): State =>
|
||||
const importUsers = (state: State, users: Array<AdminAccount>, params: AdminGetAccountsParams): State =>
|
||||
state.withMutations(state => {
|
||||
maybeImportUnapproved(state, users, filters);
|
||||
maybeImportLatest(state, users, filters, page);
|
||||
maybeImportUnapproved(state, users, params);
|
||||
maybeImportLatest(state, users, params);
|
||||
|
||||
users.forEach(user => {
|
||||
const normalizedUser = fixUser(user);
|
||||
const normalizedUser = minifyUser(user);
|
||||
state.setIn(['users', user.id], normalizedUser);
|
||||
});
|
||||
});
|
||||
|
@ -125,47 +93,40 @@ const deleteUsers = (state: State, accountIds: string[]): State =>
|
|||
});
|
||||
});
|
||||
|
||||
const approveUsers = (state: State, users: APIUser[]): State =>
|
||||
const approveUsers = (state: State, users: Array<AdminAccount>): State =>
|
||||
state.withMutations(state => {
|
||||
users.forEach(user => {
|
||||
const normalizedUser = fixUser(user);
|
||||
const normalizedUser = minifyUser(user);
|
||||
state.update('awaitingApproval', orderedSet => orderedSet.delete(user.id));
|
||||
state.setIn(['users', user.id], normalizedUser);
|
||||
});
|
||||
});
|
||||
|
||||
const minifyReport = (report: AdminReportRecord): ReducerAdminReport =>
|
||||
report.mergeWith((o, n) => n || o, {
|
||||
account: normalizeId(report.getIn(['account', 'id'])),
|
||||
target_account: normalizeId(report.getIn(['target_account', 'id'])),
|
||||
action_taken_by_account: normalizeId(report.getIn(['action_taken_by_account', 'id'])),
|
||||
assigned_account: normalizeId(report.getIn(['assigned_account', 'id'])),
|
||||
statuses: report.get('statuses').map((status: any) => normalizeId(status.get('id'))),
|
||||
}) as ReducerAdminReport;
|
||||
const minifyReport = (report: AdminReport) => omit(
|
||||
report,
|
||||
['account', 'target_account', 'action_taken_by_account', 'assigned_account', 'statuses'],
|
||||
);
|
||||
|
||||
const fixReport = (report: APIEntity): ReducerAdminReport =>
|
||||
normalizeAdminReport(report).withMutations(report => {
|
||||
minifyReport(report);
|
||||
}) as ReducerAdminReport;
|
||||
type MinifiedReport = ReturnType<typeof minifyReport>;
|
||||
|
||||
const importReports = (state: State, reports: APIEntity[]): State =>
|
||||
const importReports = (state: State, reports: Array<BaseAdminReport>): State =>
|
||||
state.withMutations(state => {
|
||||
reports.forEach(report => {
|
||||
const normalizedReport = fixReport(report);
|
||||
if (!normalizedReport.action_taken) {
|
||||
const minifiedReport = minifyReport(normalizeAdminReport(report));
|
||||
if (!minifiedReport.action_taken) {
|
||||
state.update('openReports', orderedSet => orderedSet.add(report.id));
|
||||
}
|
||||
state.setIn(['reports', report.id], normalizedReport);
|
||||
state.setIn(['reports', report.id], minifiedReport);
|
||||
});
|
||||
});
|
||||
|
||||
const handleReportDiffs = (state: State, reports: APIReport[]) =>
|
||||
const handleReportDiffs = (state: State, reports: Array<MinifiedReport>) =>
|
||||
// Note: the reports here aren't full report objects
|
||||
// hence the need for a new function.
|
||||
state.withMutations(state => {
|
||||
reports.forEach(report => {
|
||||
switch (report.state) {
|
||||
case 'open':
|
||||
switch (report.action_taken) {
|
||||
case false:
|
||||
state.update('openReports', orderedSet => orderedSet.add(report.id));
|
||||
break;
|
||||
default:
|
||||
|
@ -187,25 +148,21 @@ const admin = (state: State = ReducerRecord(), action: AnyAction): State => {
|
|||
return importConfigs(state, action.configs);
|
||||
case ADMIN_REPORTS_FETCH_SUCCESS:
|
||||
return importReports(state, action.reports);
|
||||
case ADMIN_REPORTS_PATCH_REQUEST:
|
||||
case ADMIN_REPORTS_PATCH_SUCCESS:
|
||||
case ADMIN_REPORT_PATCH_REQUEST:
|
||||
case ADMIN_REPORT_PATCH_SUCCESS:
|
||||
return handleReportDiffs(state, action.reports);
|
||||
case ADMIN_USERS_FETCH_SUCCESS:
|
||||
return importUsers(state, action.users, action.filters, action.page);
|
||||
case ADMIN_USERS_DELETE_REQUEST:
|
||||
case ADMIN_USERS_DELETE_SUCCESS:
|
||||
return importUsers(state, action.users, action.params);
|
||||
case ADMIN_USER_DELETE_REQUEST:
|
||||
case ADMIN_USER_DELETE_SUCCESS:
|
||||
return deleteUsers(state, action.accountIds);
|
||||
case ADMIN_USERS_APPROVE_REQUEST:
|
||||
case ADMIN_USER_APPROVE_REQUEST:
|
||||
return state.update('awaitingApproval', set => set.subtract(action.accountIds));
|
||||
case ADMIN_USERS_APPROVE_SUCCESS:
|
||||
case ADMIN_USER_APPROVE_SUCCESS:
|
||||
return approveUsers(state, action.users);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
type ReducerAdminAccount,
|
||||
type ReducerAdminReport,
|
||||
admin as default,
|
||||
};
|
||||
export { admin as default };
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
List as ImmutableList,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
|
@ -239,24 +238,20 @@ const makeGetReport = () => {
|
|||
return createSelector(
|
||||
[
|
||||
(state: RootState, id: string) => state.admin.reports.get(id),
|
||||
(state: RootState, id: string) => selectAccount(state, state.admin.reports.get(id)?.account || ''),
|
||||
(state: RootState, id: string) => selectAccount(state, state.admin.reports.get(id)?.target_account || ''),
|
||||
(state: RootState, id: string) => ImmutableList(fromJS(state.admin.reports.get(id)?.statuses)).map(
|
||||
statusId => state.statuses.get(normalizeId(statusId)))
|
||||
.filter((s: any) => s)
|
||||
.map((s: any) => getStatus(state, s.toJS())),
|
||||
(state: RootState, id: string) => selectAccount(state, state.admin.reports.get(id)?.account_id || ''),
|
||||
(state: RootState, id: string) => selectAccount(state, state.admin.reports.get(id)?.target_account_id || ''),
|
||||
(state: RootState, id: string) => state.admin.reports.get(id)!.status_ids
|
||||
.map((id) => getStatus(state, { id }))
|
||||
.filter((status): status is SelectedStatus => status !== null),
|
||||
],
|
||||
|
||||
(report, account, targetAccount, statuses) => {
|
||||
(report, account, target_account, statuses) => {
|
||||
if (!report) return null;
|
||||
return report.withMutations((report) => {
|
||||
// @ts-ignore
|
||||
report.set('account', account);
|
||||
// @ts-ignore
|
||||
report.set('target_account', targetAccount);
|
||||
// @ts-ignore
|
||||
report.set('statuses', statuses);
|
||||
});
|
||||
return {
|
||||
...report,
|
||||
account,
|
||||
target_account,
|
||||
statuses,
|
||||
};
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,17 +1,7 @@
|
|||
import { AdminAccountRecord, AdminReportRecord } from 'soapbox/normalizers';
|
||||
|
||||
type AdminAccount = ReturnType<typeof AdminAccountRecord>;
|
||||
type AdminReport = ReturnType<typeof AdminReportRecord>;
|
||||
|
||||
// Utility types
|
||||
type APIEntity = Record<string, any>;
|
||||
type EmbeddedEntity<T extends object> = null | string | T;
|
||||
|
||||
export {
|
||||
AdminAccount,
|
||||
AdminReport,
|
||||
|
||||
// Utility types
|
||||
APIEntity,
|
||||
EmbeddedEntity,
|
||||
};
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
import z from 'zod';
|
||||
|
||||
const makeEmojiMap = (emojis: any) => emojis.reduce((obj: any, emoji: any) => {
|
||||
obj[`:${emoji.shortcode}:`] = emoji;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
/** Normalize entity ID */
|
||||
const normalizeId = (id: any): string | null => z.string().nullable().catch(null).parse(id);
|
||||
|
||||
export {
|
||||
makeEmojiMap,
|
||||
normalizeId,
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ import { VitePWA } from 'vite-plugin-pwa';
|
|||
import vitePluginRequire from 'vite-plugin-require';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
|
||||
const config = defineConfig(({ command }) => ({
|
||||
const config = defineConfig(({ command }) => ({
|
||||
build: {
|
||||
assetsDir: 'packs',
|
||||
assetsInlineLimit: 0,
|
||||
|
|
|
@ -8385,10 +8385,10 @@ pkg-types@^1.0.3:
|
|||
mlly "^1.2.0"
|
||||
pathe "^1.1.0"
|
||||
|
||||
pl-api@^0.0.16:
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.16.tgz#de6ea3ac4a612dd1f8f5ac8a2d49eab1128c1a33"
|
||||
integrity sha512-z5w8bXr2fi2K4PPEIry+4pHmSmLKJ4rwktxiRaAe7VOU4xTSiiAyHhriUGcPUxD7QKh3QnIGj8sk/CWsBoSKnw==
|
||||
pl-api@^0.0.17:
|
||||
version "0.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.17.tgz#4659be98c65bcf1cebab43b38b28a1f306c1eef4"
|
||||
integrity sha512-qD/Qn+FGM5LX8y7Ud9mfnNMj8ZbXPGWs3Y2J3vVBAOxhkpIdhHX2JwETx08kPu1Mq7Kt3yTWDxREHSQc3XR10A==
|
||||
dependencies:
|
||||
blurhash "^2.0.5"
|
||||
http-link-header "^1.1.3"
|
||||
|
|
Loading…
Reference in a new issue