| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | /* | 
					
						
							|  |  |  | 	GoToSocial | 
					
						
							| 
									
										
										
										
											2023-03-12 18:49:06 +01:00
										 |  |  | 	Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | 	SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | import React, { useState } from "react"; | 
					
						
							| 
									
										
										
										
											2024-04-24 12:12:47 +02:00
										 |  |  | import { useParams } from "wouter"; | 
					
						
							|  |  |  | import FormWithData from "../../../lib/form/form-with-data"; | 
					
						
							|  |  |  | import BackButton from "../../../components/back-button"; | 
					
						
							|  |  |  | import { useValue, useTextInput } from "../../../lib/form"; | 
					
						
							|  |  |  | import useFormSubmit from "../../../lib/form/submit"; | 
					
						
							|  |  |  | import { TextArea } from "../../../components/form/inputs"; | 
					
						
							|  |  |  | import MutationButton from "../../../components/form/mutation-button"; | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | import Username from "./username"; | 
					
						
							| 
									
										
										
										
											2024-04-24 12:12:47 +02:00
										 |  |  | import { useGetReportQuery, useResolveReportMutation } from "../../../lib/query/admin/reports"; | 
					
						
							|  |  |  | import { useBaseUrl } from "../../../lib/navigation/util"; | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | export default function ReportDetail({ }) { | 
					
						
							| 
									
										
										
										
											2023-03-29 12:18:45 +02:00
										 |  |  | 	const baseUrl = useBaseUrl(); | 
					
						
							| 
									
										
										
										
											2024-04-24 12:12:47 +02:00
										 |  |  | 	const params = useParams(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="reports"> | 
					
						
							|  |  |  | 			<h1><BackButton to={`~${baseUrl}`}/> Report Details</h1> | 
					
						
							|  |  |  | 			<FormWithData | 
					
						
							|  |  |  | 				dataQuery={useGetReportQuery} | 
					
						
							|  |  |  | 				queryArg={params.reportId} | 
					
						
							|  |  |  | 				DataForm={ReportDetailForm} | 
					
						
							|  |  |  | 			/> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | function ReportDetailForm({ data: report }) { | 
					
						
							|  |  |  | 	const from = report.account; | 
					
						
							|  |  |  | 	const target = report.target_account; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="report detail"> | 
					
						
							| 
									
										
										
										
											2023-02-09 09:26:35 +01:00
										 |  |  | 			<div className="usernames"> | 
					
						
							| 
									
										
										
										
											2024-04-25 18:24:24 +02:00
										 |  |  | 				<Username | 
					
						
							|  |  |  | 					user={from} | 
					
						
							|  |  |  | 					link={`~/settings/moderation/accounts/${from.id}`} | 
					
						
							|  |  |  | 				/> | 
					
						
							|  |  |  | 				<> reported </> | 
					
						
							|  |  |  | 				<Username | 
					
						
							|  |  |  | 					user={target} | 
					
						
							|  |  |  | 					link={`~/settings/moderation/accounts/${target.id}`} | 
					
						
							|  |  |  | 				/> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 			</div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			{report.action_taken && | 
					
						
							|  |  |  | 				<div className="info"> | 
					
						
							|  |  |  | 					<h3>Resolved by @{report.action_taken_by_account.account.acct}</h3> | 
					
						
							|  |  |  | 					<span className="timestamp">at {new Date(report.action_taken_at).toLocaleString()}</span> | 
					
						
							|  |  |  | 					<br /> | 
					
						
							|  |  |  | 					<b>Comment: </b><span>{report.action_taken_comment}</span> | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			<div className="info-block"> | 
					
						
							|  |  |  | 				<h3>Report info:</h3> | 
					
						
							|  |  |  | 				<div className="details"> | 
					
						
							|  |  |  | 					<b>Created: </b> | 
					
						
							|  |  |  | 					<span>{new Date(report.created_at).toLocaleString()}</span> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					<b>Forwarded: </b> <span>{report.forwarded ? "Yes" : "No"}</span> | 
					
						
							|  |  |  | 					<b>Category: </b> <span>{report.category}</span> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					<b>Reason: </b> | 
					
						
							|  |  |  | 					{report.comment.length > 0 | 
					
						
							|  |  |  | 						? <p>{report.comment}</p> | 
					
						
							|  |  |  | 						: <i className="no-comment">none provided</i> | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 			</div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			{!report.action_taken && <ReportActionForm report={report} />} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				report.statuses.length > 0 && | 
					
						
							|  |  |  | 				<div className="info-block"> | 
					
						
							|  |  |  | 					<h3>Reported toots ({report.statuses.length}):</h3> | 
					
						
							|  |  |  | 					<div className="reported-toots"> | 
					
						
							|  |  |  | 						{report.statuses.map((status) => ( | 
					
						
							|  |  |  | 							<ReportedToot key={status.id} toot={status} /> | 
					
						
							|  |  |  | 						))} | 
					
						
							|  |  |  | 					</div> | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function ReportActionForm({ report }) { | 
					
						
							|  |  |  | 	const form = { | 
					
						
							|  |  |  | 		id: useValue("id", report.id), | 
					
						
							|  |  |  | 		comment: useTextInput("action_taken_comment") | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	const [submit, result] = useFormSubmit(form, useResolveReportMutation(), { changedOnly: false }); | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<form onSubmit={submit} className="info-block"> | 
					
						
							|  |  |  | 			<h3>Resolving this report</h3> | 
					
						
							|  |  |  | 			<p> | 
					
						
							|  |  |  | 				An optional comment can be included while resolving this report. | 
					
						
							|  |  |  | 				Useful for providing an explanation about what action was taken (if any) before the report was marked as resolved.<br /> | 
					
						
							|  |  |  | 				<b>This will be visible to the user that created the report!</b> | 
					
						
							|  |  |  | 			</p> | 
					
						
							|  |  |  | 			<TextArea | 
					
						
							|  |  |  | 				field={form.comment} | 
					
						
							|  |  |  | 				label="Comment" | 
					
						
							|  |  |  | 			/> | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 			<MutationButton | 
					
						
							|  |  |  | 				disabled={false} | 
					
						
							|  |  |  | 				label="Resolve" | 
					
						
							|  |  |  | 				result={result} | 
					
						
							|  |  |  | 			/> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 		</form> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function ReportedToot({ toot }) { | 
					
						
							|  |  |  | 	const account = toot.account; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							| 
									
										
										
										
											2023-12-27 11:23:52 +01:00
										 |  |  | 		<article className="status expanded"> | 
					
						
							|  |  |  | 			<header className="status-header"> | 
					
						
							|  |  |  | 				<address> | 
					
						
							|  |  |  | 					<a style={{margin: 0}}> | 
					
						
							|  |  |  | 						<img className="avatar" src={account.avatar} alt="" /> | 
					
						
							|  |  |  | 						<dl className="author-strap"> | 
					
						
							|  |  |  | 							<dt className="sr-only">Display name</dt> | 
					
						
							|  |  |  | 							<dd className="displayname text-cutoff"> | 
					
						
							|  |  |  | 								{account.display_name.trim().length > 0 ? account.display_name : account.username} | 
					
						
							|  |  |  | 							</dd> | 
					
						
							|  |  |  | 							<dt className="sr-only">Username</dt> | 
					
						
							|  |  |  | 							<dd className="username text-cutoff">@{account.username}</dd> | 
					
						
							|  |  |  | 						</dl> | 
					
						
							|  |  |  | 					</a> | 
					
						
							|  |  |  | 				</address> | 
					
						
							|  |  |  | 			</header> | 
					
						
							|  |  |  | 			<section className="status-body"> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 				<div className="text"> | 
					
						
							|  |  |  | 					<div className="content"> | 
					
						
							|  |  |  | 						{toot.spoiler_text?.length > 0 | 
					
						
							|  |  |  | 							? <TootCW content={toot.content} note={toot.spoiler_text} /> | 
					
						
							|  |  |  | 							: toot.content | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					</div> | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 				{toot.media_attachments?.length > 0 && | 
					
						
							|  |  |  | 					<TootMedia media={toot.media_attachments} sensitive={toot.sensitive} /> | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 			</section> | 
					
						
							| 
									
										
										
										
											2023-12-27 11:23:52 +01:00
										 |  |  | 			<aside className="status-info"> | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 				<dl className="status-stats"> | 
					
						
							|  |  |  | 					<div className="stats-grouping"> | 
					
						
							|  |  |  | 						<div className="stats-item published-at text-cutoff"> | 
					
						
							|  |  |  | 							<dt className="sr-only">Published</dt> | 
					
						
							| 
									
										
										
										
											2023-12-27 11:23:52 +01:00
										 |  |  | 							<dd> | 
					
						
							|  |  |  | 								<time dateTime={toot.created_at}>{new Date(toot.created_at).toLocaleString()}</time> | 
					
						
							|  |  |  | 							</dd> | 
					
						
							|  |  |  | 						</div> | 
					
						
							|  |  |  | 					</div> | 
					
						
							|  |  |  | 				</dl> | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 			</aside> | 
					
						
							|  |  |  | 		</article> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function TootCW({ note, content }) { | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 	const [visible, setVisible] = useState(false); | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	function toggleVisible() { | 
					
						
							|  |  |  | 		setVisible(!visible); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<> | 
					
						
							|  |  |  | 			<div className="spoiler"> | 
					
						
							|  |  |  | 				<span>{note}</span> | 
					
						
							|  |  |  | 				<label className="button spoiler-label" onClick={toggleVisible}>Show {visible ? "less" : "more"}</label> | 
					
						
							|  |  |  | 			</div> | 
					
						
							|  |  |  | 			{visible && content} | 
					
						
							|  |  |  | 		</> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function TootMedia({ media, sensitive }) { | 
					
						
							|  |  |  | 	let classes = (media.length % 2 == 0) ? "even" : "odd"; | 
					
						
							|  |  |  | 	if (media.length == 1) { | 
					
						
							|  |  |  | 		classes += " single"; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className={`media photoswipe-gallery ${classes}`}> | 
					
						
							|  |  |  | 			{media.map((m) => ( | 
					
						
							|  |  |  | 				<div key={m.id} className="media-wrapper"> | 
					
						
							|  |  |  | 					{sensitive && <> | 
					
						
							|  |  |  | 						<input id={`sensitiveMedia-${m.id}`} type="checkbox" className="sensitive-checkbox hidden" /> | 
					
						
							|  |  |  | 						<div className="sensitive"> | 
					
						
							|  |  |  | 							<div className="open"> | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 								<label htmlFor={`sensitiveMedia-${m.id}`} className="button" role="button" tabIndex={0}> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 									<i className="fa fa-eye-slash" title="Hide sensitive media"></i> | 
					
						
							|  |  |  | 								</label> | 
					
						
							|  |  |  | 							</div> | 
					
						
							|  |  |  | 							<div className="closed" title={m.description}> | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 								<label htmlFor={`sensitiveMedia-${m.id}`} className="button" role="button" tabIndex={0}> | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 									Show sensitive media | 
					
						
							|  |  |  | 								</label> | 
					
						
							|  |  |  | 							</div> | 
					
						
							|  |  |  | 						</div> | 
					
						
							|  |  |  | 					</>} | 
					
						
							|  |  |  | 					<a | 
					
						
							|  |  |  | 						href={m.url} | 
					
						
							|  |  |  | 						title={m.description} | 
					
						
							|  |  |  | 						target="_blank" | 
					
						
							|  |  |  | 						rel="noreferrer" | 
					
						
							|  |  |  | 						data-cropped="true" | 
					
						
							|  |  |  | 						data-pswp-width={`${m.meta?.original.width}px`} | 
					
						
							|  |  |  | 						data-pswp-height={`${m.meta?.original.height}px`} | 
					
						
							|  |  |  | 					> | 
					
						
							|  |  |  | 						<img | 
					
						
							|  |  |  | 							alt={m.description} | 
					
						
							|  |  |  | 							src={m.url} | 
					
						
							|  |  |  | 							// thumb={m.preview_url}
 | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | 							sizes={m.meta?.original} | 
					
						
							| 
									
										
										
										
											2023-02-06 09:33:47 +01:00
										 |  |  | 						/> | 
					
						
							|  |  |  | 					</a> | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 			))} | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2024-04-13 13:25:10 +02:00
										 |  |  | } |