Display rooms list
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
import { APIClient } from "../ApiClient";
|
import { APIClient } from "../ApiClient";
|
||||||
|
import type { UserInfo } from "../AuthApi";
|
||||||
import type { MatrixEvent } from "./MatrixApiEvent";
|
import type { MatrixEvent } from "./MatrixApiEvent";
|
||||||
|
import type { UsersMap } from "./MatrixApiProfile";
|
||||||
|
|
||||||
export interface Room {
|
export interface Room {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -12,6 +14,32 @@ export interface Room {
|
|||||||
latest_event?: MatrixEvent;
|
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 {
|
export class MatrixApiRoom {
|
||||||
/**
|
/**
|
||||||
* Get the list of joined rooms
|
* Get the list of joined rooms
|
||||||
|
|||||||
@@ -7,3 +7,12 @@ body,
|
|||||||
#root {
|
#root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root > div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DashboardSidebar
|
<DashboardSidebar
|
||||||
|
|||||||
@@ -81,7 +81,11 @@ export default function DashboardHeader({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
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 } }}>
|
<Toolbar sx={{ backgroundColor: "inherit", mx: { xs: -0.75, sm: -1 } }}>
|
||||||
<Stack
|
<Stack
|
||||||
direction="row"
|
direction="row"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { MatrixSyncApi } from "../../api/MatrixSyncApi";
|
|||||||
import { AsyncWidget } from "../AsyncWidget";
|
import { AsyncWidget } from "../AsyncWidget";
|
||||||
import { SpaceSelector } from "./SpaceSelector";
|
import { SpaceSelector } from "./SpaceSelector";
|
||||||
import { Divider } from "@mui/material";
|
import { Divider } from "@mui/material";
|
||||||
|
import { RoomSelector } from "./RoomSelector";
|
||||||
|
|
||||||
export function MainMessageWidget(): React.ReactElement {
|
export function MainMessageWidget(): React.ReactElement {
|
||||||
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
const [rooms, setRooms] = React.useState<Room[] | undefined>();
|
||||||
@@ -44,10 +45,27 @@ function _MainMessageWidget(p: {
|
|||||||
users: UsersMap;
|
users: UsersMap;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
const [space, setSpace] = React.useState<string | undefined>();
|
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 (
|
return (
|
||||||
<div style={{ display: "flex", height: "100%" }}>
|
<div style={{ display: "flex", height: "100%" }}>
|
||||||
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
|
<RoomSelector
|
||||||
|
{...p}
|
||||||
|
rooms={spaceRooms}
|
||||||
|
currRoom={room}
|
||||||
|
onChange={setRoom}
|
||||||
|
/>
|
||||||
|
<Divider orientation="vertical" />
|
||||||
<span style={{ flex: 1 }}>todo</span>
|
<span style={{ flex: 1 }}>todo</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { Icon } from "@mui/material";
|
import { Avatar } from "@mui/material";
|
||||||
import { MatrixApiMedia } from "../../api/matrix/MatrixApiMedia";
|
import { MatrixApiMedia } from "../../api/matrix/MatrixApiMedia";
|
||||||
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
|
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 { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
|
||||||
import GroupIcon from "@mui/icons-material/Group";
|
|
||||||
|
|
||||||
export function RoomIcon(p: {
|
export function RoomIcon(p: {
|
||||||
room: Room;
|
room: Room;
|
||||||
@@ -13,20 +16,18 @@ export function RoomIcon(p: {
|
|||||||
|
|
||||||
let url = p.room.avatar;
|
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 (
|
return (
|
||||||
<img
|
<Avatar
|
||||||
src={MatrixApiMedia.MediaURL(url, true)}
|
variant={p.room.is_space ? "square" : undefined}
|
||||||
style={{ maxWidth: "1em", maxHeight: "1em" }}
|
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