mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 10:33:41 -05:00
web/timeline: add click handlers for matrix URIs
This commit is contained in:
parent
c6992b0fca
commit
c2d0020c8c
5 changed files with 62 additions and 17 deletions
|
@ -43,6 +43,7 @@ class ContextFields implements MainScreenContextFields {
|
||||||
) {
|
) {
|
||||||
this.keybindings = new Keybindings(client.store, this)
|
this.keybindings = new Keybindings(client.store, this)
|
||||||
client.store.switchRoom = this.setActiveRoom
|
client.store.switchRoom = this.setActiveRoom
|
||||||
|
window.mainScreenContext = this
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveRoom = (roomID: RoomID | null) => {
|
setActiveRoom = (roomID: RoomID | null) => {
|
||||||
|
|
|
@ -27,7 +27,7 @@ export interface MainScreenContextFields {
|
||||||
clickRightPanelOpener: (evt: React.MouseEvent) => void
|
clickRightPanelOpener: (evt: React.MouseEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainScreenContext = createContext<MainScreenContextFields>({
|
const stubContext = {
|
||||||
get setActiveRoom(): never {
|
get setActiveRoom(): never {
|
||||||
throw new Error("MainScreenContext used outside main screen")
|
throw new Error("MainScreenContext used outside main screen")
|
||||||
},
|
},
|
||||||
|
@ -46,6 +46,9 @@ const MainScreenContext = createContext<MainScreenContextFields>({
|
||||||
get clickRightPanelOpener(): never {
|
get clickRightPanelOpener(): never {
|
||||||
throw new Error("MainScreenContext used outside main screen")
|
throw new Error("MainScreenContext used outside main screen")
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const MainScreenContext = createContext<MainScreenContextFields>(stubContext)
|
||||||
|
window.mainScreenContext = stubContext
|
||||||
|
|
||||||
export default MainScreenContext
|
export default MainScreenContext
|
||||||
|
|
|
@ -14,43 +14,55 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import { JSX, use } from "react"
|
import { JSX, use } from "react"
|
||||||
|
import type { UserID } from "@/api/types"
|
||||||
import MainScreenContext from "../MainScreenContext.ts"
|
import MainScreenContext from "../MainScreenContext.ts"
|
||||||
import PinnedMessages from "./PinnedMessages.tsx"
|
import PinnedMessages from "./PinnedMessages.tsx"
|
||||||
import CloseButton from "@/icons/close.svg?react"
|
import CloseButton from "@/icons/close.svg?react"
|
||||||
import "./RightPanel.css"
|
import "./RightPanel.css"
|
||||||
|
|
||||||
export type RightPanelType = "pinned-messages" | "members"
|
export type RightPanelType = "pinned-messages" | "members" | "user"
|
||||||
|
|
||||||
export interface RightPanelProps {
|
interface RightPanelSimpleProps {
|
||||||
type: RightPanelType
|
type: "pinned-messages" | "members"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RightPanelUserProps {
|
||||||
|
type: "user"
|
||||||
|
userID: UserID
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RightPanelProps = RightPanelUserProps | RightPanelSimpleProps
|
||||||
|
|
||||||
function getTitle(type: RightPanelType): string {
|
function getTitle(type: RightPanelType): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "pinned-messages":
|
case "pinned-messages":
|
||||||
return "Pinned Messages"
|
return "Pinned Messages"
|
||||||
case "members":
|
case "members":
|
||||||
return "Room Members"
|
return "Room Members"
|
||||||
|
case "user":
|
||||||
|
return "User Info"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRightPanelContent({ type }: RightPanelProps): JSX.Element | null {
|
function renderRightPanelContent(props: RightPanelProps): JSX.Element | null {
|
||||||
switch (type) {
|
switch (props.type) {
|
||||||
case "pinned-messages":
|
case "pinned-messages":
|
||||||
return <PinnedMessages />
|
return <PinnedMessages />
|
||||||
case "members":
|
case "members":
|
||||||
return <>Member list is not yet implemented</>
|
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">
|
return <div className="right-panel">
|
||||||
<div className="right-panel-header">
|
<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>
|
<button onClick={use(MainScreenContext).closeRightPanel}><CloseButton/></button>
|
||||||
</div>
|
</div>
|
||||||
<div className={`right-panel-content ${type}`}>
|
<div className={`right-panel-content ${props.type}`}>
|
||||||
{renderRightPanelContent({ type, ...rest })}
|
{renderRightPanelContent(props)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,42 @@ function isImageElement(elem: EventTarget): elem is HTMLImageElement {
|
||||||
return (elem as HTMLImageElement).tagName === "IMG"
|
return (elem as HTMLImageElement).tagName === "IMG"
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickHTML = (evt: React.MouseEvent<HTMLDivElement>) => {
|
function isAnchorElement(elem: EventTarget): elem is HTMLAnchorElement {
|
||||||
if (isImageElement(evt.target)) {
|
return (elem as HTMLAnchorElement).tagName === "A"
|
||||||
window.openLightbox({
|
}
|
||||||
src: evt.target.src,
|
|
||||||
alt: evt.target.alt,
|
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],
|
||||||
})
|
})
|
||||||
} else if ((evt.target as HTMLElement).closest?.("span.hicli-spoiler")?.classList.toggle("spoiler-revealed")) {
|
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>) => {
|
||||||
|
const targetElem = evt.target as HTMLElement
|
||||||
|
if (isImageElement(targetElem)) {
|
||||||
|
window.openLightbox({
|
||||||
|
src: targetElem.src,
|
||||||
|
alt: targetElem.alt,
|
||||||
|
})
|
||||||
|
} else if (targetElem.closest?.("span.hicli-spoiler")?.classList.toggle("spoiler-revealed")) {
|
||||||
// When unspoilering, don't trigger links and other clickables inside the spoiler
|
// When unspoilering, don't trigger links and other clickables inside the spoiler
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
|
} else if (isAnchorElement(targetElem) && targetElem.href.startsWith("matrix:")) {
|
||||||
|
onClickMatrixURI(targetElem.href)
|
||||||
|
evt.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
web/src/vite-env.d.ts
vendored
2
web/src/vite-env.d.ts
vendored
|
@ -2,10 +2,12 @@
|
||||||
/// <reference types="vite-plugin-svgr/client" />
|
/// <reference types="vite-plugin-svgr/client" />
|
||||||
|
|
||||||
import type Client from "@/api/client.ts"
|
import type Client from "@/api/client.ts"
|
||||||
|
import type { MainScreenContextFields } from "@/ui/MainScreenContext.ts"
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
client: Client
|
client: Client
|
||||||
|
mainScreenContext: MainScreenContextFields
|
||||||
openLightbox: (params: { src: string, alt: string }) => void
|
openLightbox: (params: { src: string, alt: string }) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue