diff --git a/web/src/App.tsx b/web/src/App.tsx index e8a8972..f3d0912 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -18,9 +18,10 @@ import { ScaleLoader } from "react-spinners" import Client from "./api/client.ts" import WSClient from "./api/wsclient.ts" import { ClientContext } from "./ui/ClientContext.ts" -import { LightboxWrapper } from "./ui/Lightbox.tsx" import MainScreen from "./ui/MainScreen.tsx" import { LoginScreen, VerificationScreen } from "./ui/login" +import { LightboxWrapper } from "./ui/modal/Lightbox.tsx" +import { ModalWrapper } from "./ui/modal/Modal.tsx" import { useEventAsState } from "./util/eventdispatcher.ts" function App() { @@ -53,7 +54,9 @@ function App() { } else { return - + + + } diff --git a/web/src/ui/RoomView.tsx b/web/src/ui/RoomView.tsx index 26a610c..45251ba 100644 --- a/web/src/ui/RoomView.tsx +++ b/web/src/ui/RoomView.tsx @@ -17,8 +17,8 @@ import { use, useRef } from "react" import { getAvatarURL } from "@/api/media.ts" import { RoomStateStore } from "@/api/statestore" import { useNonNullEventAsState } from "@/util/eventdispatcher.ts" -import { LightboxContext } from "./Lightbox.tsx" import MessageComposer from "./composer/MessageComposer.tsx" +import { LightboxContext } from "./modal/Lightbox.tsx" import { RoomContext, RoomContextData } from "./roomcontext.ts" import TimelineView from "./timeline/TimelineView.tsx" import BackIcon from "@/icons/back.svg?react" diff --git a/web/src/ui/Lightbox.css b/web/src/ui/modal/Lightbox.css similarity index 82% rename from web/src/ui/Lightbox.css rename to web/src/ui/modal/Lightbox.css index 502045e..7a03c4a 100644 --- a/web/src/ui/Lightbox.css +++ b/web/src/ui/modal/Lightbox.css @@ -1,12 +1,17 @@ div.overlay { position: fixed; inset: 0; - background-color: rgba(0, 0, 0, 0.75); display: flex; align-items: center; justify-content: center; - &.lightbox > div.controls { + &.dimmed { + background-color: rgba(0, 0, 0, 0.75); + } +} + +div.lightbox { + > div.controls { position: fixed; top: .5rem; right: .5rem; @@ -29,7 +34,7 @@ div.overlay { } } - &.lightbox > img { + > img { max-width: 75%; max-height: 75%; diff --git a/web/src/ui/Lightbox.tsx b/web/src/ui/modal/Lightbox.tsx similarity index 93% rename from web/src/ui/Lightbox.tsx rename to web/src/ui/modal/Lightbox.tsx index 83f2e9d..9c39f03 100644 --- a/web/src/ui/Lightbox.tsx +++ b/web/src/ui/modal/Lightbox.tsx @@ -14,12 +14,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import React, { Component, RefObject, createContext, createRef, useCallback, useState } from "react" -import CloseIcon from "../icons/close.svg?react" -import DownloadIcon from "../icons/download.svg?react" -import RotateLeftIcon from "../icons/rotate-left.svg?react" -import RotateRightIcon from "../icons/rotate-right.svg?react" -import ZoomInIcon from "../icons/zoom-in.svg?react" -import ZoomOutIcon from "../icons/zoom-out.svg?react" +import CloseIcon from "@/icons/close.svg?react" +import DownloadIcon from "@/icons/download.svg?react" +import RotateLeftIcon from "@/icons/rotate-left.svg?react" +import RotateRightIcon from "@/icons/rotate-right.svg?react" +import ZoomInIcon from "@/icons/zoom-in.svg?react" +import ZoomOutIcon from "@/icons/zoom-out.svg?react" import "./Lightbox.css" const isTouchDevice = window.ontouchstart !== undefined @@ -158,7 +158,7 @@ export class Lightbox extends Component { render() { return
diff --git a/web/src/ui/modal/Modal.tsx b/web/src/ui/modal/Modal.tsx new file mode 100644 index 0000000..43b6078 --- /dev/null +++ b/web/src/ui/modal/Modal.tsx @@ -0,0 +1,47 @@ +// 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 . +import React, { JSX, createContext, useCallback, useState } from "react" + +export interface ModalState { + content: JSX.Element + dimmed?: boolean + wrapperClass?: string + onClose?: () => void +} + +type openModal = (state: ModalState) => void + +export const ModalContext = createContext(() => + console.error("Tried to open modal without being inside context")) + +export const ModalWrapper = ({ children }: { children: React.ReactNode }) => { + const [state, setState] = useState(null) + const onClose = useCallback(() => { + setState(null) + state?.onClose?.() + }, [state]) + return <> + + {children} + + {state &&
+ {state.content} +
} + +} diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index eb5da5d..57270f4 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -19,7 +19,7 @@ import { useRoomState } from "@/api/statestore" import { MemDBEvent, MemberEventContent, UnreadType } from "@/api/types" import { isEventID } from "@/util/validation.ts" import { ClientContext } from "../ClientContext.ts" -import { LightboxContext } from "../Lightbox.tsx" +import { LightboxContext } from "../modal/Lightbox.tsx" import { useRoomContext } from "../roomcontext.ts" import EventMenu from "./EventMenu.tsx" import { ReplyIDBody } from "./ReplyBody.tsx" diff --git a/web/src/ui/timeline/content/MemberBody.tsx b/web/src/ui/timeline/content/MemberBody.tsx index 7840724..af9bdeb 100644 --- a/web/src/ui/timeline/content/MemberBody.tsx +++ b/web/src/ui/timeline/content/MemberBody.tsx @@ -16,7 +16,7 @@ import React, { use } from "react" import { getAvatarURL } from "@/api/media.ts" import { MemberEventContent, UserID } from "@/api/types" -import { LightboxContext } from "../../Lightbox.tsx" +import { LightboxContext } from "../../modal/Lightbox.tsx" import EventContentProps from "./props.ts" function useChangeDescription( diff --git a/web/src/ui/timeline/content/useMediaContent.tsx b/web/src/ui/timeline/content/useMediaContent.tsx index b33e309..90cc573 100644 --- a/web/src/ui/timeline/content/useMediaContent.tsx +++ b/web/src/ui/timeline/content/useMediaContent.tsx @@ -17,7 +17,7 @@ import { CSSProperties, use } from "react" import { getEncryptedMediaURL, getMediaURL } from "@/api/media.ts" import type { EventType, MediaMessageEventContent } from "@/api/types" import { ImageContainerSize, calculateMediaSize } from "@/util/mediasize.ts" -import { LightboxContext } from "../../Lightbox.tsx" +import { LightboxContext } from "../../modal/Lightbox.tsx" import DownloadIcon from "@/icons/download.svg?react" export const useMediaContent = (