web/mainscreen: add animation for entering rooms

This commit is contained in:
Tulir Asokan 2024-12-15 16:27:18 +02:00
parent 5455c88c2f
commit 5f880b2487
3 changed files with 43 additions and 23 deletions

View file

@ -162,6 +162,7 @@ body {
html {
touch-action: none;
background-color: var(--background-color);
}
#root {

View file

@ -17,25 +17,24 @@ main.matrix-main {
}
@media screen and (max-width: 45rem) {
&, &.right-panel-open {
grid-template:
"roomlist roomview rightpanel" 1fr
/ 100% 100% 100%;
}
/* Note: this timeout must match the one in MainScreen.tsx */
transition: .3s;
@media (prefers-reduced-motion: reduce) {
transition: none;
}
&.room-selected {
translate: -100% 0;
}
&.right-panel-open {
grid-template: "rightpanel" 1fr / 1fr;
> div.room-list-wrapper {
display: none;
}
> div.room-view {
display: none;
}
}
&.room-selected:not(.right-panel-open) {
grid-template: "roomview" 1fr / 1fr;
> div.room-list-wrapper {
display: none;
}
}
&:not(.room-selected):not(.right-panel-open) {
grid-template: "roomlist" 1fr / 1fr;
translate: -200% 0;
}
}

View file

@ -13,7 +13,7 @@
//
// 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, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useState } from "react"
import { JSX, use, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useState } from "react"
import { SyncLoader } from "react-spinners"
import Client from "@/api/client.ts"
import { RoomStateStore } from "@/api/statestore"
@ -208,8 +208,20 @@ const handleURLHash = (client: Client) => {
return history.state
}
type ActiveRoomType = [RoomStateStore | null, RoomStateStore | null]
const activeRoomReducer = (prev: ActiveRoomType, active: RoomStateStore | "clear-animation" | null): ActiveRoomType => {
if (active === "clear-animation") {
return prev[1] === null ? [null, null] : prev
} else if (window.innerWidth > 720 || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return [null, active]
} else {
return [prev[1], active]
}
}
const MainScreen = () => {
const [activeRoom, directSetActiveRoom] = useState<RoomStateStore | null>(null)
const [[prevActiveRoom, activeRoom], directSetActiveRoom] = useReducer(activeRoomReducer, [null, null] as const)
const [rightPanel, directSetRightPanel] = useState<RightPanelProps | null>(null)
const client = use(ClientContext)!
const syncStatus = useEventAsState(client.syncStatus)
@ -292,16 +304,24 @@ const MainScreen = () => {
Sync is failing
</div>
}
const renderedRoom = activeRoom ?? prevActiveRoom
useEffect(() => {
if (prevActiveRoom !== null && activeRoom === null) {
// Note: this timeout must match the one in MainScreen.css
const timeout = setTimeout(() => directSetActiveRoom("clear-animation"), 300)
return () => clearTimeout(timeout)
}
}, [activeRoom, prevActiveRoom])
return <MainScreenContext value={context}>
<ModalWrapper>
<StylePreferences client={client} activeRoom={activeRoom}/>
<main className={classNames.join(" ")} style={extraStyle}>
<RoomList activeRoomID={activeRoom?.roomID ?? null}/>
{resizeHandle1}
{activeRoom
{renderedRoom
? <RoomView
key={activeRoom.roomID}
room={activeRoom}
key={renderedRoom.roomID}
room={renderedRoom}
rightPanel={rightPanel}
rightPanelResizeHandle={resizeHandle2}
/>