Skip to content

Commit

Permalink
web/timeline: add click handlers for matrix URIs
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Nov 9, 2024
1 parent c6992b0 commit c2d0020
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 15 deletions.
1 change: 1 addition & 0 deletions web/src/ui/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ContextFields implements MainScreenContextFields {
) {
this.keybindings = new Keybindings(client.store, this)
client.store.switchRoom = this.setActiveRoom
window.mainScreenContext = this
}

setActiveRoom = (roomID: RoomID | null) => {
Expand Down
7 changes: 5 additions & 2 deletions web/src/ui/MainScreenContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface MainScreenContextFields {
clickRightPanelOpener: (evt: React.MouseEvent) => void
}

const MainScreenContext = createContext<MainScreenContextFields>({
const stubContext = {
get setActiveRoom(): never {
throw new Error("MainScreenContext used outside main screen")
},
Expand All @@ -46,6 +46,9 @@ const MainScreenContext = createContext<MainScreenContextFields>({
get clickRightPanelOpener(): never {
throw new Error("MainScreenContext used outside main screen")
},
})
}

const MainScreenContext = createContext<MainScreenContextFields>(stubContext)
window.mainScreenContext = stubContext

export default MainScreenContext
30 changes: 21 additions & 9 deletions web/src/ui/rightpanel/RightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,55 @@
// 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 { JSX, use } from "react"
import type { UserID } from "@/api/types"
import MainScreenContext from "../MainScreenContext.ts"
import PinnedMessages from "./PinnedMessages.tsx"
import CloseButton from "@/icons/close.svg?react"
import "./RightPanel.css"

export type RightPanelType = "pinned-messages" | "members"
export type RightPanelType = "pinned-messages" | "members" | "user"

export interface RightPanelProps {
type: RightPanelType
interface RightPanelSimpleProps {
type: "pinned-messages" | "members"
}

interface RightPanelUserProps {
type: "user"
userID: UserID
}

export type RightPanelProps = RightPanelUserProps | RightPanelSimpleProps

function getTitle(type: RightPanelType): string {
switch (type) {
case "pinned-messages":
return "Pinned Messages"
case "members":
return "Room Members"
case "user":
return "User Info"
}
}

function renderRightPanelContent({ type }: RightPanelProps): JSX.Element | null {
switch (type) {
function renderRightPanelContent(props: RightPanelProps): JSX.Element | null {
switch (props.type) {
case "pinned-messages":
return <PinnedMessages />
case "members":
return <>Member list is not yet implemented</>
case "user":
return <>{props.userID}</>
}
}

const RightPanel = ({ type, ...rest }: RightPanelProps) => {
const RightPanel = (props: RightPanelProps) => {
return <div className="right-panel">
<div className="right-panel-header">
<div className="panel-name">{getTitle(type)}</div>
<div className="panel-name">{getTitle(props.type)}</div>
<button onClick={use(MainScreenContext).closeRightPanel}><CloseButton/></button>
</div>
<div className={`right-panel-content ${type}`}>
{renderRightPanelContent({ type, ...rest })}
<div className={`right-panel-content ${props.type}`}>
{renderRightPanelContent(props)}
</div>
</div>
}
Expand Down
35 changes: 31 additions & 4 deletions web/src/ui/timeline/content/TextMessageBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,42 @@ function isImageElement(elem: EventTarget): elem is HTMLImageElement {
return (elem as HTMLImageElement).tagName === "IMG"
}

function isAnchorElement(elem: EventTarget): elem is HTMLAnchorElement {
return (elem as HTMLAnchorElement).tagName === "A"
}

function onClickMatrixURI(href: string) {
const url = new URL(href)
const pathParts = url.pathname.split("/")
switch (pathParts[0]) {
case "u":
return window.mainScreenContext.setRightPanel({
type: "user",
userID: pathParts[1],
})
case "roomid":
return window.mainScreenContext.setActiveRoom(pathParts[1])
case "r":
return window.client.rpc.resolveAlias(`#${pathParts[1]}`).then(
res => window.mainScreenContext.setActiveRoom(res.room_id),
err => window.alert(`Failed to resolve room alias #${pathParts[1]}: ${err}`),
)
}
}

const onClickHTML = (evt: React.MouseEvent<HTMLDivElement>) => {
if (isImageElement(evt.target)) {
const targetElem = evt.target as HTMLElement
if (isImageElement(targetElem)) {
window.openLightbox({
src: evt.target.src,
alt: evt.target.alt,
src: targetElem.src,
alt: targetElem.alt,
})
} else if ((evt.target as HTMLElement).closest?.("span.hicli-spoiler")?.classList.toggle("spoiler-revealed")) {
} else if (targetElem.closest?.("span.hicli-spoiler")?.classList.toggle("spoiler-revealed")) {
// When unspoilering, don't trigger links and other clickables inside the spoiler
evt.preventDefault()
} else if (isAnchorElement(targetElem) && targetElem.href.startsWith("matrix:")) {
onClickMatrixURI(targetElem.href)
evt.preventDefault()
}
}

Expand Down
2 changes: 2 additions & 0 deletions web/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
/// <reference types="vite-plugin-svgr/client" />

import type Client from "@/api/client.ts"
import type { MainScreenContextFields } from "@/ui/MainScreenContext.ts"

declare global {
interface Window {
client: Client
mainScreenContext: MainScreenContextFields
openLightbox: (params: { src: string, alt: string }) => void
}
}

0 comments on commit c2d0020

Please sign in to comment.