Add a button to remove all loaded timeline events from user

This commit is contained in:
nexy7574 2025-04-09 03:25:07 +01:00
parent fac69d1b63
commit 9814c3a525
No known key found for this signature in database
GPG key ID: 0FA334385D0B689F

View file

@ -15,13 +15,15 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { use } from "react"
import Client from "@/api/client.ts"
import { RoomStateStore } from "@/api/statestore"
import { RoomStateStore, useRoomTimeline } from "@/api/statestore"
import { MemDBEvent, MembershipAction } from "@/api/types"
import { useRoomContext } from "@/ui/roomview/roomcontext.ts"
import ConfirmWithMessageModal from "../menu/ConfirmWithMessageModal.tsx"
import { getPowerLevels } from "../menu/util.ts"
import { ModalContext } from "../modal"
import StartDMButton from "./StartDMButton.tsx"
import UserIgnoreButton from "./UserIgnoreButton.tsx"
import DeleteIcon from "@/icons/delete.svg?react"
import BanIcon from "@/icons/gavel.svg?react"
import InviteIcon from "@/icons/person-add.svg?react"
import KickIcon from "@/icons/person-remove.svg?react"
@ -35,7 +37,9 @@ interface UserModerationProps {
const UserModeration = ({ userID, client, member, room }: UserModerationProps) => {
const openModal = use(ModalContext)
const hasPL = (action: "invite" | "kick" | "ban") => {
const roomCtx = useRoomContext()
const timeline = useRoomTimeline(roomCtx.store)
const hasPL = (action: "invite" | "kick" | "ban" | "redact") => {
if (!room) {
throw new Error("hasPL called without room")
}
@ -44,7 +48,7 @@ const UserModeration = ({ userID, client, member, room }: UserModerationProps) =
return ownPL >= (pls.invite ?? 0)
}
const otherUserPL = pls.users?.[userID] ?? pls.users_default ?? 0
return ownPL >= (pls[action] ?? 50) && ownPL > otherUserPL
return ownPL >= (pls[action] ?? pls.state_default ?? 50) && ownPL > otherUserPL
}
const runAction = (action: MembershipAction) => {
@ -76,6 +80,63 @@ const UserModeration = ({ userID, client, member, room }: UserModerationProps) =
})
}
}
const calculateRedactions = () => {
if (!room) {
return []
}
return timeline.filter(evt => {
return evt !== null && evt.room_id == room.roomID && evt.sender === userID && !evt.redacted_by
}) as MemDBEvent[] // there's no nulls in this one
}
const redactRecentMessages = () => {
if (!room) {
throw new Error("redactRecentMessages called without room")
}
const callback = async (reason: string) => {
const tasks = []
for (const evt of calculateRedactions()) {
// for(let i=0;i<3;i++) {
// try {
// await client.rpc.redactEvent(evt.room_id, evt.event_id, reason)
// break
// } catch (e) {
// // If the error is a 429, retry.
// // TODO: get the ratelimit from the error and use it for a more precise sleep
// if (e instanceof Error && e.message.includes("M_LIMIT_EXCEEDED")) {
// const retryAfter = (2 ** i) + Math.random()
// console.warn("Rate limited, retrying in %.2f seconds...", retryAfter)
// await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
// } else {
// throw e
// }
// }
// }
tasks.push(client.rpc.redactEvent(evt.room_id, evt.event_id, reason))
}
Promise.all(tasks).catch((e) => {
console.error(e)
window.alert(`Failed to redact events: ${e}`)
})
return true
}
const evtCount = calculateRedactions().length
return () => {
openModal({
dimmed: true,
boxed: true,
innerBoxClass: "confirm-message-modal",
content: <ConfirmWithMessageModal
title={`Redact recent timeline events of ${userID}`}
description={
<>Are you sure you want to redact all currently loaded timeline events
of <code>{userID}</code>? This will remove approximately {evtCount} events.</>}
placeholder="Reason (optional)"
confirmButton={`Redact ~${evtCount} events`}
onConfirm={callback}
/>,
})
}
}
const membership = member?.content.membership || "leave"
return <div className="user-moderation">
@ -111,6 +172,12 @@ const UserModeration = ({ userID, client, member, room }: UserModerationProps) =
<span>Unban</span>
</button>
)}
{room && hasPL("redact") && (
<button className="moderation-action dangerous" onClick={redactRecentMessages()}>
<DeleteIcon />
<span>Redact recent messages</span>
</button>
)}
<UserIgnoreButton userID={userID} client={client} />
</div>
}