web/main: make room view and list separate screens on mobile

This commit is contained in:
Tulir Asokan 2024-10-15 13:54:22 +03:00
parent 747a015bcc
commit b31eb2ea75
5 changed files with 51 additions and 7 deletions

1
web/src/icons/back.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/></svg>

After

Width:  |  Height:  |  Size: 190 B

View file

@ -4,4 +4,28 @@ main.matrix-main {
display: grid; display: grid;
grid-template: "roomlist roomview" 1fr / 300px 1fr; grid-template: "roomlist roomview" 1fr / 300px 1fr;
@media screen and (max-width: 1000px) {
grid-template: "roomlist roomview" 1fr / 250px 1fr;
}
@media screen and (max-width: 900px) {
grid-template: "roomlist roomview" 1fr / 200px 1fr;
}
@media screen and (max-width: 750px) {
&.room-selected {
grid-template: "roomview" 1fr / 1fr;
> div.room-list-wrapper {
display: none;
}
}
&:not(.room-selected) {
grid-template: "roomlist" 1fr / 1fr;
> div.room-view {
display: none;
}
}
}
} }

View file

@ -14,7 +14,7 @@
// 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 { use, useCallback, useState } from "react" import { use, useCallback, useState } from "react"
import type { RoomID } from "../api/types" import type { RoomID } from "@/api/types"
import { ClientContext } from "./ClientContext.ts" import { ClientContext } from "./ClientContext.ts"
import RoomView from "./RoomView.tsx" import RoomView from "./RoomView.tsx"
import RoomList from "./roomlist/RoomList.tsx" import RoomList from "./roomlist/RoomList.tsx"
@ -31,9 +31,10 @@ const MainScreen = () => {
.catch(err => console.error("Failed to load room state", err)) .catch(err => console.error("Failed to load room state", err))
} }
}, [client]) }, [client])
return <main className="matrix-main"> const clearActiveRoom = useCallback(() => setActiveRoomID(null), [])
return <main className={`matrix-main ${activeRoom ? "room-selected" : ""}`}>
<RoomList setActiveRoom={setActiveRoom} activeRoomID={activeRoomID} /> <RoomList setActiveRoom={setActiveRoom} activeRoomID={activeRoomID} />
{activeRoom && <RoomView key={activeRoomID} room={activeRoom} />} {activeRoom && <RoomView key={activeRoomID} clearActiveRoom={clearActiveRoom} room={activeRoom} />}
</main> </main>
} }

View file

@ -13,11 +13,26 @@ div.room-view {
display: flex; display: flex;
align-items: center; align-items: center;
gap: .5rem; gap: .5rem;
padding-left: 1rem; margin-left: .5rem;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
> span.room-name { > span.room-name {
font-weight: bold; font-weight: bold;
} }
> button.back {
background: none;
border: none;
height: 2.5rem;
width: 2.5rem;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
&:hover, &:focus {
background-color: #eee;
}
}
} }
} }

View file

@ -21,15 +21,18 @@ import { useNonNullEventAsState } from "@/util/eventdispatcher.ts"
import { LightboxContext } from "./Lightbox.tsx" import { LightboxContext } from "./Lightbox.tsx"
import MessageComposer from "./MessageComposer.tsx" import MessageComposer from "./MessageComposer.tsx"
import TimelineView from "./timeline/TimelineView.tsx" import TimelineView from "./timeline/TimelineView.tsx"
import BackIcon from "@/icons/back.svg?react"
import "./RoomView.css" import "./RoomView.css"
interface RoomViewProps { interface RoomViewProps {
room: RoomStateStore room: RoomStateStore
clearActiveRoom: () => void
} }
const RoomHeader = ({ room }: RoomViewProps) => { const RoomHeader = ({ room, clearActiveRoom }: RoomViewProps) => {
const roomMeta = useNonNullEventAsState(room.meta) const roomMeta = useNonNullEventAsState(room.meta)
return <div className="room-header"> return <div className="room-header">
<button className="back" onClick={clearActiveRoom}><BackIcon/></button>
<img <img
className="avatar" className="avatar"
loading="lazy" loading="lazy"
@ -49,12 +52,12 @@ const onKeyDownRoomView = (evt: React.KeyboardEvent) => {
} }
} }
const RoomView = ({ room }: RoomViewProps) => { const RoomView = ({ room, clearActiveRoom }: RoomViewProps) => {
const [replyTo, setReplyTo] = useState<MemDBEvent | null>(null) const [replyTo, setReplyTo] = useState<MemDBEvent | null>(null)
const [textRows, setTextRows] = useState(1) const [textRows, setTextRows] = useState(1)
const closeReply = useCallback(() => setReplyTo(null), []) const closeReply = useCallback(() => setReplyTo(null), [])
return <div className="room-view" onKeyDown={onKeyDownRoomView} tabIndex={-1}> return <div className="room-view" onKeyDown={onKeyDownRoomView} tabIndex={-1}>
<RoomHeader room={room}/> <RoomHeader room={room} clearActiveRoom={clearActiveRoom}/>
<TimelineView room={room} textRows={textRows} replyTo={replyTo} setReplyTo={setReplyTo}/> <TimelineView room={room} textRows={textRows} replyTo={replyTo} setReplyTo={setReplyTo}/>
<MessageComposer room={room} setTextRows={setTextRows} replyTo={replyTo} closeReply={closeReply}/> <MessageComposer room={room} setTextRows={setTextRows} replyTo={replyTo} closeReply={closeReply}/>
</div> </div>