From a37d04b300cb32f887ea63c692676f86e4c4e34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 25 Aug 2024 20:17:52 +0200 Subject: [PATCH] Migrate most of the dashboard to pl-api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- package.json | 2 +- src/actions/admin.ts | 459 +++++------------- src/actions/importer/index.ts | 7 +- src/actions/moderation.tsx | 6 +- src/actions/notifications.ts | 2 +- src/api/index.ts | 15 - src/components/sidebar-navigation.tsx | 2 +- src/components/status-content.tsx | 2 +- .../components/latest-accounts-panel.tsx | 14 +- .../admin/components/report-status.tsx | 6 +- src/features/admin/components/report.tsx | 57 ++- .../admin/components/unapproved-account.tsx | 6 +- src/features/admin/tabs/awaiting-approval.tsx | 5 +- src/features/ui/components/modal-root.tsx | 2 +- src/features/ui/index.tsx | 5 +- src/features/video/index.tsx | 2 +- src/normalizers/admin-account.ts | 55 --- src/normalizers/admin-report.ts | 57 +-- src/normalizers/index.ts | 3 +- src/reducers/admin-user-index.ts | 14 +- src/reducers/admin.ts | 133 ++--- src/selectors/index.ts | 29 +- src/types/entities.ts | 10 - src/utils/normalizers.ts | 6 - vite.config.ts | 2 +- yarn.lock | 8 +- 26 files changed, 260 insertions(+), 649 deletions(-) delete mode 100644 src/normalizers/admin-account.ts diff --git a/package.json b/package.json index 8db3a4ac8..bdbb57755 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/actions/admin.ts b/src/actions/admin.ts index 86e2161f5..0e9c8da4a 100644 --- a/src/actions/admin.ts +++ b/src/actions/admin.ts @@ -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) => return dispatch(updateConfig(params)); }; -const fetchMastodonReports = (params: Record) => - (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) => - (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 = {}) => +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 = { - 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 = { 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, diff --git a/src/actions/importer/index.ts b/src/actions/importer/index.ts index bf245172c..e30374b4b 100644 --- a/src/actions/importer/index.ts +++ b/src/actions/importer/index.ts @@ -110,18 +110,19 @@ const isBroken = (status: BaseStatus) => { } }; -const importFetchedStatuses = (statuses: Array) => (dispatch: AppDispatch) => { +const importFetchedStatuses = (statuses: Array & { account: BaseAccount | null }>) => (dispatch: AppDispatch) => { const accounts: Array = []; const normalStatuses: Array = []; const polls: Array = []; 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) => (dispatch: AppDis } }; - statuses.forEach(processStatus); + (statuses as Array).forEach(processStatus); dispatch(importPolls(polls)); dispatch(importFetchedAccounts(accounts)); diff --git a/src/actions/moderation.tsx b/src/actions/moderation.tsx index ae218ffd2..d0e85fe3f 100644 --- a/src/actions/moderation.tsx +++ b/src/actions/moderation.tsx @@ -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); diff --git a/src/actions/notifications.ts b/src/actions/notifications.ts index 74846dd3f..fe9c18759 100644 --- a/src/actions/notifications.ts +++ b/src/actions/notifications.ts @@ -209,7 +209,7 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an } if (maxId?.includes('+')) { - const ids = maxId.split('+'); + const ids = maxId.split('+'); maxId = ids[ids.length - 1]; } diff --git a/src/api/index.ts b/src/api/index.ts index 52aefd291..104048a63 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -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 = 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): LinkHeader => - new LinkHeader(response.headers?.get('link') || undefined); - -const getNextLink = (response: Pick): 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, }; diff --git a/src/components/sidebar-navigation.tsx b/src/components/sidebar-navigation.tsx index b4f6ae45b..39830efa8 100644 --- a/src/components/sidebar-navigation.tsx +++ b/src/components/sidebar-navigation.tsx @@ -212,7 +212,7 @@ const SidebarNavigation = () => { text={} /> - {account.is_admin || account.is_moderator && ( + {(account.is_admin || account.is_moderator) && ( = React.memo(({ if (spoilerText) { output.push( - + {expandable && (