[feature/frontend] Reports frontend v2 (#3022)

* use apiutil + paging in admin processor+handlers

* we're making it happen

* fix little whoopsie

* styling for report list

* don't youuuu forget about meee don't don't don't don't

* last bits

* sanitize content before showing in report statuses

* update report docs
This commit is contained in:
tobi
2024-06-18 18:18:00 +02:00
committed by GitHub
parent b08c1bd0cb
commit d2b3d37724
56 changed files with 1389 additions and 726 deletions

View File

@@ -21,29 +21,51 @@ import { gtsApi } from "../../gts-api";
import type {
AdminReport,
AdminReportListParams,
AdminSearchReportParams,
AdminReportResolveParams,
AdminSearchReportResp,
} from "../../../types/report";
import parse from "parse-link-header";
const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({
listReports: build.query<AdminReport[], AdminReportListParams | void>({
query: (params) => ({
url: "/api/v1/admin/reports",
params: {
// Override provided limit.
limit: 100,
...params
searchReports: build.query<AdminSearchReportResp, AdminSearchReportParams>({
query: (form) => {
const params = new(URLSearchParams);
Object.entries(form).forEach(([k, v]) => {
if (v !== undefined) {
params.append(k, v);
}
});
let query = "";
if (params.size !== 0) {
query = `?${params.toString()}`;
}
}),
providesTags: [{ type: "Reports", id: "LIST" }]
return {
url: `/api/v1/admin/reports${query}`
};
},
// Headers required for paging.
transformResponse: (apiResp: AdminReport[], meta) => {
const accounts = apiResp;
const linksStr = meta?.response?.headers.get("Link");
const links = parse(linksStr);
return { accounts, links };
},
// Only provide LIST tag id since this model is not the
// same as getReport model (due to transformResponse).
providesTags: [{ type: "Report", id: "TRANSFORMED" }]
}),
getReport: build.query<AdminReport, string>({
query: (id) => ({
url: `/api/v1/admin/reports/${id}`
}),
providesTags: (_res, _error, id) => [{ type: "Reports", id }]
providesTags: (_result, _error, id) => [
{ type: 'Report', id }
],
}),
resolveReport: build.mutation<AdminReport, AdminReportResolveParams>({
@@ -55,8 +77,8 @@ const extended = gtsApi.injectEndpoints({
}),
invalidatesTags: (res) =>
res
? [{ type: "Reports", id: "LIST" }, { type: "Reports", id: res.id }]
: [{ type: "Reports", id: "LIST" }]
? [{ type: "Report", id: "LIST" }, { type: "Report", id: res.id }]
: [{ type: "Report", id: "LIST" }]
})
})
});
@@ -64,7 +86,7 @@ const extended = gtsApi.injectEndpoints({
/**
* List reports received on this instance, filtered using given parameters.
*/
const useListReportsQuery = extended.useListReportsQuery;
const useLazySearchReportsQuery = extended.useLazySearchReportsQuery;
/**
* Get a single report by its ID.
@@ -77,7 +99,7 @@ const useGetReportQuery = extended.useGetReportQuery;
const useResolveReportMutation = extended.useResolveReportMutation;
export {
useListReportsQuery,
useLazySearchReportsQuery,
useGetReportQuery,
useResolveReportMutation,
};

View File

@@ -136,7 +136,7 @@ export const gtsApi = createApi({
tagTypes: [
"Auth",
"Emoji",
"Reports",
"Report",
"Account",
"InstanceRules",
"HTTPHeaderAllows",

View File

@@ -17,6 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Links } from "parse-link-header";
import { AdminAccount } from "./account";
import { Status } from "./status";
/**
* Admin model of a report. Differs from the client
* model, which contains less detailed information.
@@ -56,29 +60,25 @@ export interface AdminReport {
updated_at: string;
/**
* Account that created the report.
* TODO: model this properly.
*/
account: Object;
account: AdminAccount;
/**
* Reported account.
* TODO: model this properly.
*/
target_account: Object;
target_account: AdminAccount;
/**
* Admin account assigned to handle this report, if any.
* TODO: model this properly.
*/
assigned_account?: Object;
assigned_account?: AdminAccount;
/**
* Admin account that has taken action on this report, if any.
* TODO: model this properly.
*/
action_taken_by_account?: Object;
action_taken_by_account?: AdminAccount;
/**
* Statuses cited by this report, if any.
* TODO: model this properly.
*/
statuses: Object[];
statuses: Status[];
/**
* Rules broken according to the reporter, if any.
* TODO: model this properly.
@@ -108,7 +108,7 @@ export interface AdminReportResolveParams {
/**
* Parameters for GET to /api/v1/admin/reports.
*/
export interface AdminReportListParams {
export interface AdminSearchReportParams {
/**
* If set, show only resolved (true) or only unresolved (false) reports.
*/
@@ -142,3 +142,8 @@ export interface AdminReportListParams {
*/
limit?: number;
}
export interface AdminSearchReportResp {
accounts: AdminReport[];
links: Links | null;
}

View File

@@ -0,0 +1,83 @@
/*
GoToSocial
Copyright (C) GoToSocial Authors admin@gotosocial.org
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Account } from "./account";
import { CustomEmoji } from "./custom-emoji";
export interface Status {
id: string;
created_at: string;
in_reply_to_id: string | null;
in_reply_to_account_id: string | null;
sensitive: boolean;
spoiler_text: string;
visibility: string;
language: string;
uri: string;
url: string;
replies_count: number;
reblogs_count: number;
favourites_count: number;
favourited: boolean;
reblogged: boolean;
muted: boolean;
bookmarked: boolean;
pinned: boolean;
content: string,
reblog: Status | null,
account: Account,
media_attachments: MediaAttachment[],
mentions: [];
tags: [];
emojis: CustomEmoji[];
card: null;
poll: null;
}
export interface MediaAttachment {
id: string;
type: string;
url: string;
text_url: string;
preview_url: string;
remote_url: string | null;
preview_remote_url: string | null;
meta: MediaAttachmentMeta;
description: string;
blurhash: string;
}
interface MediaAttachmentMeta {
original: {
width: number;
height: number;
size: string;
aspect: number;
},
small: {
width: number;
height: number;
size: string;
aspect: number;
},
focus: {
x: number;
y: number;
}
}

View File

@@ -0,0 +1,43 @@
/*
GoToSocial
Copyright (C) GoToSocial Authors admin@gotosocial.org
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useMemo } from "react";
import { AdminAccount } from "../types/account";
import { store } from "../../redux/store";
export function yesOrNo(b: boolean): string {
return b ? "yes" : "no";
}
export function UseOurInstanceAccount(account: AdminAccount): boolean {
// Pull our own URL out of storage so we can
// tell if account is our instance account.
const ourDomain = useMemo(() => {
const instanceUrlStr = store.getState().oauth.instanceUrl;
if (!instanceUrlStr) {
return "";
}
const instanceUrl = new URL(instanceUrlStr);
return instanceUrl.host;
}, []);
return !account.domain && account.username == ourDomain;
}