mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 10:33:41 -05:00
170 lines
5.7 KiB
TypeScript
170 lines
5.7 KiB
TypeScript
// gomuks - A Matrix client written in Go.
|
|
// Copyright (C) 2024 Tulir Asokan
|
|
//
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
import { use } from "react"
|
|
import Client from "@/api/client.ts"
|
|
import { useRoomState } from "@/api/statestore"
|
|
import { MemDBEvent } from "@/api/types"
|
|
import { ModalCloseContext, ModalContext } from "../../modal"
|
|
import { RoomContext, RoomContextData } from "../../roomview/roomcontext.ts"
|
|
import JSONView from "../../util/JSONView.tsx"
|
|
import ConfirmWithMessageModal from "./ConfirmWithMessageModal.tsx"
|
|
import ShareModal from "./ShareModal.tsx"
|
|
import { getPending, getPowerLevels } from "./util.ts"
|
|
import ViewSourceIcon from "@/icons/code.svg?react"
|
|
import DeleteIcon from "@/icons/delete.svg?react"
|
|
import PinIcon from "@/icons/pin.svg?react"
|
|
import ReportIcon from "@/icons/report.svg?react"
|
|
import ShareIcon from "@/icons/share.svg?react"
|
|
import UnpinIcon from "@/icons/unpin.svg?react"
|
|
|
|
export const useSecondaryItems = (
|
|
client: Client,
|
|
roomCtx: RoomContextData,
|
|
evt: MemDBEvent,
|
|
names = true,
|
|
) => {
|
|
const closeModal = use(ModalCloseContext)
|
|
const openModal = use(ModalContext)
|
|
const onClickViewSource = () => {
|
|
openModal({
|
|
dimmed: true,
|
|
boxed: true,
|
|
content: <JSONView data={evt}/>,
|
|
})
|
|
}
|
|
const onClickReport = () => {
|
|
openModal({
|
|
dimmed: true,
|
|
boxed: true,
|
|
innerBoxClass: "confirm-message-modal",
|
|
content: <RoomContext value={roomCtx}>
|
|
<ConfirmWithMessageModal
|
|
evt={evt}
|
|
title="Report Message"
|
|
description="Report this message to your homeserver administrator?"
|
|
placeholder="Reason for report"
|
|
confirmButton="Send report"
|
|
onConfirm={reason => {
|
|
client.rpc.reportEvent(evt.room_id, evt.event_id, reason)
|
|
.catch(err => window.alert(`Failed to report message: ${err}`))
|
|
}}
|
|
/>
|
|
</RoomContext>,
|
|
})
|
|
}
|
|
const onClickRedact = () => {
|
|
openModal({
|
|
dimmed: true,
|
|
boxed: true,
|
|
innerBoxClass: "confirm-message-modal",
|
|
content: <RoomContext value={roomCtx}>
|
|
<ConfirmWithMessageModal
|
|
evt={evt}
|
|
title="Remove Message"
|
|
description="Permanently remove the content of this event?"
|
|
placeholder="Reason for removal"
|
|
confirmButton="Remove"
|
|
onConfirm={reason => {
|
|
client.rpc.redactEvent(evt.room_id, evt.event_id, reason)
|
|
.catch(err => window.alert(`Failed to redact message: ${err}`))
|
|
}}
|
|
/>
|
|
</RoomContext>,
|
|
})
|
|
}
|
|
const onClickPin = (pin: boolean) => () => {
|
|
closeModal()
|
|
client.pinMessage(roomCtx.store, evt.event_id, pin)
|
|
.catch(err => window.alert(`Failed to ${pin ? "pin" : "unpin"} message: ${err}`))
|
|
}
|
|
|
|
const onClickShareEvent = () => {
|
|
const generateLink = (useMatrixTo: boolean, includeEvent: boolean) => {
|
|
const isRoomIDLink = true
|
|
let generatedURL = useMatrixTo ? "https://matrix.to/#/" : "matrix:roomid/"
|
|
if (useMatrixTo) {
|
|
generatedURL += evt.room_id
|
|
} else {
|
|
generatedURL += `${evt.room_id.slice(1)}`
|
|
}
|
|
if (includeEvent) {
|
|
if (useMatrixTo) {
|
|
generatedURL += `/${evt.event_id}`
|
|
} else {
|
|
generatedURL += `/e/${evt.event_id.slice(1)}`
|
|
}
|
|
}
|
|
if (isRoomIDLink) {
|
|
generatedURL += "?" + new URLSearchParams(
|
|
roomCtx.store.getViaServers().map(server => ["via", server]),
|
|
).toString()
|
|
}
|
|
return generatedURL
|
|
}
|
|
openModal({
|
|
dimmed: true,
|
|
boxed: true,
|
|
innerBoxClass: "confirm-message-modal",
|
|
content: <RoomContext value={roomCtx}>
|
|
<ShareModal
|
|
evt={evt}
|
|
title="Share Message"
|
|
confirmButton="Copy to clipboard"
|
|
onConfirm={(useMatrixTo: boolean, includeEvent: boolean) => {
|
|
navigator.clipboard.writeText(generateLink(useMatrixTo, includeEvent)).catch(
|
|
err => window.alert(`Failed to copy link: ${err}`),
|
|
)
|
|
}}
|
|
generateLink={generateLink}
|
|
/>
|
|
</RoomContext>,
|
|
})
|
|
}
|
|
|
|
const [isPending, pendingTitle] = getPending(evt)
|
|
useRoomState(roomCtx.store, "m.room.power_levels", "")
|
|
// We get pins from getPinnedEvents, but use the hook anyway to subscribe to changes
|
|
useRoomState(roomCtx.store, "m.room.pinned_events", "")
|
|
const [pls, ownPL] = getPowerLevels(roomCtx.store, client)
|
|
const pins = roomCtx.store.getPinnedEvents()
|
|
const pinPL = pls.events?.["m.room.pinned_events"] ?? pls.state_default ?? 50
|
|
const redactEvtPL = pls.events?.["m.room.redaction"] ?? pls.events_default ?? 0
|
|
const redactOtherPL = pls.redact ?? 50
|
|
const canRedact = !evt.redacted_by
|
|
&& ownPL >= redactEvtPL
|
|
&& (evt.sender === client.userID || ownPL >= redactOtherPL)
|
|
|
|
return <>
|
|
<button onClick={onClickViewSource}><ViewSourceIcon/>{names && "View source"}</button>
|
|
<button onClick={onClickShareEvent}><ShareIcon/>{names && "Share"}</button>
|
|
{ownPL >= pinPL && (pins.includes(evt.event_id)
|
|
? <button onClick={onClickPin(false)}>
|
|
<UnpinIcon/>{names && "Unpin message"}
|
|
</button>
|
|
: <button onClick={onClickPin(true)} title={pendingTitle} disabled={isPending}>
|
|
<PinIcon/>{names && "Pin message"}
|
|
</button>)}
|
|
<button onClick={onClickReport} disabled={isPending} title={pendingTitle}>
|
|
<ReportIcon/>{names && "Report"}
|
|
</button>
|
|
{canRedact && <button
|
|
onClick={onClickRedact}
|
|
disabled={isPending}
|
|
title={pendingTitle}
|
|
className="redact-button"
|
|
><DeleteIcon/>{names && "Remove"}</button>}
|
|
</>
|
|
}
|