Display rooms list
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { APIClient } from "../ApiClient";
|
||||
import type { UserInfo } from "../AuthApi";
|
||||
import type { MatrixEvent } from "./MatrixApiEvent";
|
||||
import type { UsersMap } from "./MatrixApiProfile";
|
||||
|
||||
export interface Room {
|
||||
id: string;
|
||||
@@ -12,6 +14,32 @@ export interface Room {
|
||||
latest_event?: MatrixEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find main member of room
|
||||
*/
|
||||
export function mainRoomMember(user: UserInfo, r: Room): string | undefined {
|
||||
if (r.members.length <= 1) return r.members[0];
|
||||
|
||||
if (r.members.length < 2)
|
||||
return r.members[0] == user.matrix_user_id ? r.members[1] : r.members[0];
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find room name
|
||||
*/
|
||||
export function roomName(user: UserInfo, r: Room, users: UsersMap): string {
|
||||
if (r.name) return r.name;
|
||||
|
||||
const name = r.members
|
||||
.filter((m) => m !== user.matrix_user_id)
|
||||
.map((m) => users.get(m)?.display_name ?? m)
|
||||
.join(",");
|
||||
|
||||
return name === "" ? "Empty room" : name;
|
||||
}
|
||||
|
||||
export class MatrixApiRoom {
|
||||
/**
|
||||
* Get the list of joined rooms
|
||||
|
||||
@@ -7,3 +7,12 @@ body,
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#root > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<DashboardSidebar
|
||||
|
||||
@@ -81,7 +81,11 @@ export default function DashboardHeader({
|
||||
);
|
||||
|
||||
return (
|
||||
<AppBar color="inherit" position="static" sx={{ displayPrint: "none" }}>
|
||||
<AppBar
|
||||
color="inherit"
|
||||
position="static"
|
||||
sx={{ displayPrint: "none", overflow: "hidden" }}
|
||||
>
|
||||
<Toolbar sx={{ backgroundColor: "inherit", mx: { xs: -0.75, sm: -1 } }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { MatrixSyncApi } from "../../api/MatrixSyncApi";
|
||||
import { AsyncWidget } from "../AsyncWidget";
|
||||
import { SpaceSelector } from "./SpaceSelector";
|
||||
import { Divider } from "@mui/material";
|
||||
import { RoomSelector } from "./RoomSelector";
|
||||
|
||||
export function MainMessageWidget(): React.ReactElement {
|
||||
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
||||
@@ -44,10 +45,27 @@ function _MainMessageWidget(p: {
|
||||
users: UsersMap;
|
||||
}): React.ReactElement {
|
||||
const [space, setSpace] = React.useState<string | undefined>();
|
||||
const [room, setRoom] = React.useState<Room | undefined>();
|
||||
|
||||
const spaceRooms = React.useMemo(() => {
|
||||
return p.rooms
|
||||
.filter((r) => !r.is_space && (!space || r.parents.includes(space)))
|
||||
.sort(
|
||||
(a, b) => (b.latest_event?.time ?? 0) - (a.latest_event?.time ?? 0)
|
||||
);
|
||||
}, [space, p.rooms]);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", height: "100%" }}>
|
||||
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
||||
<Divider orientation="vertical" />
|
||||
<RoomSelector
|
||||
{...p}
|
||||
rooms={spaceRooms}
|
||||
currRoom={room}
|
||||
onChange={setRoom}
|
||||
/>
|
||||
<Divider orientation="vertical" />
|
||||
<span style={{ flex: 1 }}>todo</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Icon } from "@mui/material";
|
||||
import { Avatar } from "@mui/material";
|
||||
import { MatrixApiMedia } from "../../api/matrix/MatrixApiMedia";
|
||||
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
|
||||
import type { Room } from "../../api/matrix/MatrixApiRoom";
|
||||
import {
|
||||
mainRoomMember,
|
||||
roomName,
|
||||
type Room,
|
||||
} from "../../api/matrix/MatrixApiRoom";
|
||||
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
||||
import GroupIcon from "@mui/icons-material/Group";
|
||||
|
||||
export function RoomIcon(p: {
|
||||
room: Room;
|
||||
@@ -13,20 +16,18 @@ export function RoomIcon(p: {
|
||||
|
||||
let url = p.room.avatar;
|
||||
|
||||
if (!url && p.room.members.length <= 1) url = p.room.members[0];
|
||||
if (!url) {
|
||||
const member = mainRoomMember(user.info, p.room);
|
||||
if (member) url = p.users.get(member)?.avatar;
|
||||
}
|
||||
const name = roomName(user.info, p.room, p.users);
|
||||
|
||||
if (!url && p.room.members.length < 2)
|
||||
url =
|
||||
p.room.members[0] == user.info.matrix_user_id
|
||||
? p.room.members[1]
|
||||
: p.room.members[0];
|
||||
|
||||
if (!url) return <GroupIcon />;
|
||||
else
|
||||
return (
|
||||
<img
|
||||
src={MatrixApiMedia.MediaURL(url, true)}
|
||||
style={{ maxWidth: "1em", maxHeight: "1em" }}
|
||||
/>
|
||||
<Avatar
|
||||
variant={p.room.is_space ? "square" : undefined}
|
||||
src={url ? MatrixApiMedia.MediaURL(url, true) : undefined}
|
||||
>
|
||||
{name.slice(0, 1)}
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
|
||||
69
matrixgw_frontend/src/widgets/messages/RoomSelector.tsx
Normal file
69
matrixgw_frontend/src/widgets/messages/RoomSelector.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
Chip,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
} from "@mui/material";
|
||||
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
|
||||
import { roomName, type Room } from "../../api/matrix/MatrixApiRoom";
|
||||
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
||||
import { RoomIcon } from "./RoomIcon";
|
||||
|
||||
const ROOM_SELECTOR_WIDTH = "300px";
|
||||
|
||||
export function RoomSelector(p: {
|
||||
users: UsersMap;
|
||||
rooms: Room[];
|
||||
currRoom?: Room;
|
||||
onChange: (r: Room) => void;
|
||||
}): React.ReactElement {
|
||||
const user = useUserInfo();
|
||||
|
||||
if (p.rooms.length === 0)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: ROOM_SELECTOR_WIDTH,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
No room to display.
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<List
|
||||
style={{
|
||||
width: ROOM_SELECTOR_WIDTH,
|
||||
}}
|
||||
>
|
||||
{p.rooms.map((r) => (
|
||||
<ListItem
|
||||
key={r.id}
|
||||
secondaryAction={
|
||||
r.number_unread_messages === 0 ? undefined : (
|
||||
<Chip color="error" label={r.number_unread_messages} />
|
||||
)
|
||||
}
|
||||
disablePadding
|
||||
>
|
||||
<ListItemButton
|
||||
role={undefined}
|
||||
onClick={() => p.onChange(r)}
|
||||
dense
|
||||
selected={p.currRoom?.id === r.id}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<RoomIcon room={r} {...p} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={roomName(user.info, r, p.users)} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user