feat: Integrate forum layout support across various components and enhance room handling logic
This commit is contained in:
8
src/app/features/room/ForumRoomView.css.ts
Normal file
8
src/app/features/room/ForumRoomView.css.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { color } from 'folds';
|
||||
|
||||
export const ForumBanner = style({
|
||||
borderLeft: `3px solid ${color.Primary.Main}`,
|
||||
background: color.Primary.Container,
|
||||
color: color.Primary.OnContainer,
|
||||
});
|
||||
50
src/app/features/room/ForumRoomView.tsx
Normal file
50
src/app/features/room/ForumRoomView.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useSpaceOptionally } from '../../hooks/useSpace';
|
||||
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
||||
import { findForumRootSpace, shouldShowForumLobby } from '../../utils/room';
|
||||
import { ForumSpaceShell } from '../forum/ForumSpaceShell';
|
||||
import { RoomView } from './RoomView';
|
||||
|
||||
type ForumRoomViewProps = {
|
||||
room: Room;
|
||||
eventId?: string;
|
||||
};
|
||||
|
||||
export function ForumRoomView({ room, eventId }: ForumRoomViewProps) {
|
||||
const mx = useMatrixClient();
|
||||
const space = useSpaceOptionally();
|
||||
const roomToParents = useAtomValue(roomToParentsAtom);
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
|
||||
const forumSpaceId =
|
||||
space && shouldShowForumLobby(space)
|
||||
? space.roomId
|
||||
: findForumRootSpace(mx, room.roomId, roomToParents);
|
||||
|
||||
const forumSpace = forumSpaceId ? mx.getRoom(forumSpaceId) : null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!eventId) return;
|
||||
setSearchParams(
|
||||
(prev) => {
|
||||
const next = new URLSearchParams(prev);
|
||||
next.set('post', eventId);
|
||||
if (!next.get('topic') && room.roomId !== forumSpaceId) {
|
||||
next.set('topic', room.roomId);
|
||||
}
|
||||
return next;
|
||||
},
|
||||
{ replace: true }
|
||||
);
|
||||
}, [eventId, setSearchParams, room.roomId, forumSpaceId]);
|
||||
|
||||
if (forumSpace && shouldShowForumLobby(forumSpace)) {
|
||||
return <ForumSpaceShell />;
|
||||
}
|
||||
|
||||
return <RoomView room={room} eventId={eventId} />;
|
||||
}
|
||||
@@ -15,6 +15,8 @@ import { useMarkAsRead } from '../../hooks/useMarkAsRead';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useRoomMembers } from '../../hooks/useRoomMembers';
|
||||
import { activeRoomIdAtom } from '../../state/activeRoom';
|
||||
import { isForum } from '../../utils/room';
|
||||
import { ForumRoomView } from './ForumRoomView';
|
||||
|
||||
export function Room() {
|
||||
const { eventId } = useParams();
|
||||
@@ -28,6 +30,7 @@ export function Room() {
|
||||
const powerLevels = usePowerLevels(room);
|
||||
const members = useRoomMembers(mx, room.roomId);
|
||||
const markAsRead = useMarkAsRead(mx);
|
||||
const forumRoom = isForum(room);
|
||||
|
||||
// Update titlebar with current room ID
|
||||
useEffect(() => {
|
||||
@@ -50,7 +53,7 @@ export function Room() {
|
||||
return (
|
||||
<PowerLevelsContextProvider value={powerLevels}>
|
||||
<Box grow="Yes">
|
||||
<RoomView room={room} eventId={eventId} />
|
||||
{forumRoom ? <ForumRoomView room={room} eventId={eventId} /> : <RoomView room={room} eventId={eventId} />}
|
||||
{screenSize === ScreenSize.Desktop && isDrawer && (
|
||||
<>
|
||||
<Line variant="Background" direction="Vertical" size="300" />
|
||||
|
||||
@@ -132,9 +132,10 @@ interface RoomInputProps {
|
||||
room: Room;
|
||||
/** When provided, all messages are sent as replies in this thread. */
|
||||
threadRootId?: string;
|
||||
onMessageSent?: () => void;
|
||||
}
|
||||
export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
({ editor, fileDropContainerRef, roomId, room, threadRootId }, ref) => {
|
||||
({ editor, fileDropContainerRef, roomId, room, threadRootId, onMessageSent }, ref) => {
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const [enterForNewline] = useSetting(settingsAtom, 'enterForNewline');
|
||||
@@ -480,7 +481,8 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
setReplyDraft(undefined);
|
||||
setCommandHint(undefined);
|
||||
sendTypingStatus(false);
|
||||
}, [mx, roomId, threadRootId, editor, replyDraft, sendTypingStatus, setReplyDraft, isMarkdown, commands, autoConvertEmoticons]);
|
||||
onMessageSent?.();
|
||||
}, [mx, roomId, threadRootId, editor, replyDraft, sendTypingStatus, setReplyDraft, isMarkdown, commands, autoConvertEmoticons, onMessageSent]);
|
||||
|
||||
const handleKeyDown: KeyboardEventHandler = useCallback(
|
||||
(evt) => {
|
||||
|
||||
@@ -48,6 +48,7 @@ import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { roomToUnreadAtom } from '../../state/room/roomToUnread';
|
||||
import { useMarkAsRead } from '../../hooks/useMarkAsRead';
|
||||
import { copyToClipboard } from '../../utils/dom';
|
||||
import { SpeechBubble } from '../../components/speech-bubble/SpeechBubble';
|
||||
import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
|
||||
import { useRoomAvatar, useRoomName, useRoomTopic } from '../../hooks/useRoomMeta';
|
||||
import { mDirectAtom } from '../../state/mDirectList';
|
||||
@@ -316,35 +317,19 @@ function CallIndicator({ roomId }: CallIndicatorProps) {
|
||||
position="Bottom"
|
||||
offset={8}
|
||||
tooltip={
|
||||
<Box
|
||||
direction="Column"
|
||||
gap="50"
|
||||
style={{
|
||||
padding: `${toRem(8)} ${toRem(12)}`,
|
||||
backgroundColor: color.SurfaceVariant.Container,
|
||||
color: color.SurfaceVariant.OnContainer,
|
||||
borderRadius: toRem(8),
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Speech bubble arrow */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: toRem(-6),
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderLeft: `${toRem(6)} solid transparent`,
|
||||
borderRight: `${toRem(6)} solid transparent`,
|
||||
borderBottom: `${toRem(6)} solid ${color.SurfaceVariant.Container}`,
|
||||
}}
|
||||
/>
|
||||
<Text size="T300" style={{ fontWeight: 600, color: color.SurfaceVariant.OnContainer }}>{displayName}</Text>
|
||||
<Text size="T200" style={{ opacity: 0.7, color: color.SurfaceVariant.OnContainer }}>{member.userId}</Text>
|
||||
</Box>
|
||||
<SpeechBubble tail="top" tailX="50%">
|
||||
<Box direction="Column" gap="50">
|
||||
<Text
|
||||
size="T300"
|
||||
style={{ fontWeight: 600, color: color.SurfaceVariant.OnContainer }}
|
||||
>
|
||||
{displayName}
|
||||
</Text>
|
||||
<Text size="T200" style={{ opacity: 0.7, color: color.SurfaceVariant.OnContainer }}>
|
||||
{member.userId}
|
||||
</Text>
|
||||
</Box>
|
||||
</SpeechBubble>
|
||||
}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
@@ -490,7 +475,11 @@ function RoomCallButtons({ roomId }: RoomCallButtonsProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function RoomViewHeader() {
|
||||
type RoomViewHeaderProps = {
|
||||
forumLayout?: boolean;
|
||||
};
|
||||
|
||||
export function RoomViewHeader({ forumLayout = false }: RoomViewHeaderProps) {
|
||||
const navigate = useNavigate();
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
@@ -564,9 +553,16 @@ export function RoomViewHeader() {
|
||||
</Avatar>
|
||||
)}
|
||||
<Box direction="Column">
|
||||
<Text size={topic ? 'H5' : 'H3'} truncate>
|
||||
{name}
|
||||
</Text>
|
||||
<Box alignItems="Center" gap="100">
|
||||
<Text size={topic ? 'H5' : 'H3'} truncate>
|
||||
{name}
|
||||
</Text>
|
||||
{forumLayout && (
|
||||
<Badge variant="Secondary" fill="Soft" outlined>
|
||||
<Text size="T200">Forum</Text>
|
||||
</Badge>
|
||||
)}
|
||||
</Box>
|
||||
{topic && (
|
||||
<UseStateProvider initial={false}>
|
||||
{(viewTopic, setViewTopic) => (
|
||||
|
||||
Reference in New Issue
Block a user