diff --git a/web/src/icons/door-open.svg b/web/src/icons/door-open.svg new file mode 100644 index 0000000..d4a5719 --- /dev/null +++ b/web/src/icons/door-open.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/mark-read.svg b/web/src/icons/mark-read.svg new file mode 100644 index 0000000..3819d33 --- /dev/null +++ b/web/src/icons/mark-read.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/mark-unread.svg b/web/src/icons/mark-unread.svg new file mode 100644 index 0000000..2228f31 --- /dev/null +++ b/web/src/icons/mark-unread.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/menu/RoomMenu.tsx b/web/src/ui/menu/RoomMenu.tsx index 6adb172..a0ccd20 100644 --- a/web/src/ui/menu/RoomMenu.tsx +++ b/web/src/ui/menu/RoomMenu.tsx @@ -16,9 +16,13 @@ import { CSSProperties, use } from "react" import { RoomListEntry, RoomStateStore, useAccountData } from "@/api/statestore" import { RoomID } from "@/api/types" +import { useEventAsState } from "@/util/eventdispatcher.ts" import ClientContext from "../ClientContext.ts" -import { ModalContext } from "../modal" +import { ModalCloseContext, ModalContext } from "../modal" import SettingsView from "../settings/SettingsView.tsx" +import DoorOpenIcon from "@/icons/door-open.svg?react" +import MarkReadIcon from "@/icons/mark-read.svg?react" +import MarkUnreadIcon from "@/icons/mark-unread.svg?react" import NotificationsOffIcon from "@/icons/notifications-off.svg?react" import NotificationsIcon from "@/icons/notifications.svg?react" import SettingsIcon from "@/icons/settings.svg?react" @@ -36,6 +40,7 @@ const hasNotifyingActions = (actions: unknown) => { const MuteButton = ({ roomID }: { roomID: RoomID }) => { const client = use(ClientContext)! + const closeModal = use(ModalCloseContext) const roomRules = useAccountData(client.store, "m.push_rules")?.global?.room const pushRule = Array.isArray(roomRules) ? roomRules.find(rule => rule?.rule_id === roomID) : null const muted = pushRule?.enabled === true && !hasNotifyingActions(pushRule.actions) @@ -44,6 +49,7 @@ const MuteButton = ({ roomID }: { roomID: RoomID }) => { console.error("Failed to mute room", err) window.alert(`Failed to ${muted ? "unmute" : "mute"} room: ${err}`) }) + closeModal() } return } +const MarkReadButton = ({ room }: { room: RoomStateStore }) => { + const meta = useEventAsState(room.meta) + const client = use(ClientContext)! + const closeModal = use(ModalCloseContext) + const read = !meta.marked_unread && meta.unread_messages === 0 + const markRead = () => { + const evt = room.eventsByRowID.get( + room.timeline[room.timeline.length-1]?.event_rowid ?? meta.preview_event_rowid, + ) + if (!evt) { + window.alert("Can't mark room as read: last event not found in cache") + return + } + const rrType = room.preferences.send_read_receipts ? "m.read" : "m.read.private" + client.rpc.markRead(room.roomID, evt.event_id, rrType).catch(err => { + console.error("Failed to mark room as read", err) + window.alert(`Failed to mark room as read: ${err}`) + }) + closeModal() + } + const markUnread = () => { + client.rpc.setAccountData("m.marked_unread", { unread: true }, room.roomID).catch(err => { + console.error("Failed to mark room as unread", err) + window.alert(`Failed to mark room as unread: ${err}`) + }) + closeModal() + } + return +} + export const RoomMenu = ({ room, style }: RoomMenuProps) => { const openModal = use(ModalContext) + const closeModal = use(ModalCloseContext) + const client = use(ClientContext)! const openSettings = () => { openModal({ dimmed: true, @@ -61,8 +102,17 @@ export const RoomMenu = ({ room, style }: RoomMenuProps) => { content: , }) } + const leaveRoom = () => { + client.rpc.leaveRoom(room.roomID).catch(err => { + console.error("Failed to leave room", err) + window.alert(`Failed to leave room: ${err}`) + }) + closeModal() + } return
+ +
} diff --git a/web/src/ui/roomlist/Entry.tsx b/web/src/ui/roomlist/Entry.tsx index 6e4c7f4..020517b 100644 --- a/web/src/ui/roomlist/Entry.tsx +++ b/web/src/ui/roomlist/Entry.tsx @@ -85,6 +85,7 @@ const Entry = ({ room, isActive, hidden }: RoomListEntryProps) => { const onContextMenu = (evt: React.MouseEvent) => { const realRoom = client.store.rooms.get(room.room_id) if (!realRoom) { + // TODO implement separate menu for invite rooms console.error("Room state store not found for", room.room_id) return }