Skip to content

Commit

Permalink
feat: Implement open rooms when has invited
Browse files Browse the repository at this point in the history
  • Loading branch information
criss8X committed Nov 11, 2024
1 parent e201397 commit ff25f3d
Show file tree
Hide file tree
Showing 13 changed files with 14,366 additions and 9,637 deletions.
Binary file added .yarn/install-state.gz
Binary file not shown.
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div align="center">
<img width="200" alt="Mirage's logo" src="./public/logo-white.svg" />
<img width="80" alt="Mirage's logo" src="./public/new-logo.svg" />
<br />
<br />
<img alt="User-interface preview" src="./github/preview.png" />
Expand Down
4 changes: 3 additions & 1 deletion src/components/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const Loader: FC<LoaderProps> = ({text, className}) => {
)}>
<div className="size-8 animate-rotation rounded-full border-4 border-white border-t-slate-500" />

<Text>{text}</Text>
<Text align="center" className="max-w-max">
{text}
</Text>
</div>
)
}
Expand Down
10 changes: 8 additions & 2 deletions src/containers/NavigationSection/RoomNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {WIDTH_FILL_NAVIGATOR_ANIM} from "@/utils/animations"
import {trim} from "@/utils/util"
import {SearchInput} from "@/components/ui/input"
import {Accordion} from "@/components/ui/accordion"

type AccordionRoomSectionProps = {
title: string
Expand Down Expand Up @@ -170,6 +171,7 @@ export const RoomNavigator: FC<RoomNavigatorProps> = ({
}) => {
const {t} = useTranslation()
const [searchResult, setSearchResult] = useState<RoomSections | null>(null)
const [accordionValue, setAccordionValue] = useState([t(LangKey.Rooms)])

const allRoomsLength =
sections.groups.length +
Expand Down Expand Up @@ -226,7 +228,11 @@ export const RoomNavigator: FC<RoomNavigatorProps> = ({
</div>
)}

<AccordionPrimitive.Root className="p-1" type="multiple">
<Accordion
className="p-1"
type="multiple"
value={accordionValue}
onValueChange={setAccordionValue}>
<ToggleGroup
className="flex flex-col items-start gap-4"
type="single"
Expand Down Expand Up @@ -276,7 +282,7 @@ export const RoomNavigator: FC<RoomNavigatorProps> = ({
</AccordionRoomSection>
)}
</ToggleGroup>
</AccordionPrimitive.Root>
</Accordion>
</div>
)
}
Expand Down
13 changes: 11 additions & 2 deletions src/containers/NavigationSection/hooks/useNotifications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {type NotificationProps} from "@/components/Notification"
import useActiveRoomIdStore from "@/hooks/matrix/useActiveRoomIdStore"
import useEventListener from "@/hooks/matrix/useEventListener"
import useTranslation from "@/hooks/util/useTranslation"
import {LangKey} from "@/lang/allKeys"
Expand Down Expand Up @@ -35,6 +36,7 @@ const useNotifications = (
client: MatrixClient | null
): UseNotificationsReturnType => {
const {t} = useTranslation()
const {setActiveRoomId} = useActiveRoomIdStore()

const [notificationsState, setNotificationsState] = useState(
NotificationState.Waiting
Expand Down Expand Up @@ -122,7 +124,9 @@ const useNotifications = (
...cachedNotification,
onDelete: deleteNotificationById,
markAsRead: markAsReadByNotificationId,
action() {},
action() {
setActiveRoomId(cachedNotification.roomId)
},
}
}

Expand All @@ -132,7 +136,12 @@ const useNotifications = (
markAsRead: markAsReadByNotificationId,
}
}),
[cachedNotifications, deleteNotificationById, markAsReadByNotificationId]
[
cachedNotifications,
deleteNotificationById,
markAsReadByNotificationId,
setActiveRoomId,
]
)

const fetchInvitedNotifications = useCallback(() => {
Expand Down
77 changes: 46 additions & 31 deletions src/containers/RoomContainer/RoomContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ import useBreakpoint from "@/hooks/util/useMediaQuery"
import RoomInvitedSplash from "./RoomInvitedSplash"

const RoomContainer: FC = () => {
const {activeRoomId, roomState} = useActiveRoom()
const [isRosterExpanded, setIsRosterExpanded] = useState(true)
const {isSmall} = useBreakpoint()

const {
activeRoomId,
roomState,
clearActiveRoomId,
roomInvitedDetail,
onJoinRoom,
} = useActiveRoom()

const {membersState, onReloadMembers, onLazyReload, isLazyLoading} =
useRoomMembers(activeRoomId)

Expand All @@ -28,39 +35,47 @@ const RoomContainer: FC = () => {
}

return (
<div className="size-full flex-col sm:flex">
{roomState === RoomState.Joined && activeRoomId !== null ? (
<div className="flex size-full">
<ChatContainer
className="flex size-full flex-col"
roomId={activeRoomId}
isRosterExpanded={isRosterExpanded}
onRosterExpanded={setIsRosterExpanded}
/>
<>
{roomState === RoomState.Invited && (
<RoomInvitedSplash
onClose={clearActiveRoomId}
roomDetailPreview={roomInvitedDetail}
onJoinRoom={onJoinRoom}
/>
)}

<motion.div animate={{width: isRosterExpanded ? "15rem" : 0}}>
<Roster
className="max-w-60"
isLazyLoading={isLazyLoading}
membersState={membersState}
onReloadMembers={onReloadMembers}
onLazyLoad={onLazyReload}
onUserClick={function (_userId: string): void {
throw new Error("`onUserClick` function not implemented.")
}}
<div className="size-full flex-col sm:flex">
{roomState === RoomState.Joined && activeRoomId !== null ? (
<div className="flex size-full">
<ChatContainer
className="flex size-full flex-col"
roomId={activeRoomId}
isRosterExpanded={isRosterExpanded}
onRosterExpanded={setIsRosterExpanded}
/>
</motion.div>
</div>
) : roomState === RoomState.Invited ? (
<RoomInvitedSplash />
) : roomState === RoomState.NotFound ? (
<RoomNotFoundSplash />
) : (
<WelcomeSplash />
)}

<SmartActionBar />
</div>
<motion.div animate={{width: isRosterExpanded ? "15rem" : 0}}>
<Roster
className="max-w-60"
isLazyLoading={isLazyLoading}
membersState={membersState}
onReloadMembers={onReloadMembers}
onLazyLoad={onLazyReload}
onUserClick={function (_userId: string): void {
throw new Error("`onUserClick` function not implemented.")
}}
/>
</motion.div>
</div>
) : roomState === RoomState.NotFound ? (
<RoomNotFoundSplash />
) : (
<WelcomeSplash />
)}

<SmartActionBar />
</div>
</>
)
}

Expand Down
6 changes: 4 additions & 2 deletions src/containers/RoomContainer/RoomInvitedSplash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import React, {FC, useState} from "react"
import {IoShield} from "react-icons/io5"
import {LiaSlackHash} from "react-icons/lia"

class RoomInvitedError extends Error {
export class RoomInvitedError extends Error {
constructor(message: string, name?: string) {
super()

Expand All @@ -29,7 +29,7 @@ class RoomInvitedError extends Error {
}
}

type RoomDetailOwner = {
export type RoomDetailOwner = {
displayName: string
avatarUrl?: string
}
Expand Down Expand Up @@ -77,6 +77,8 @@ const RoomInvitedSplash: FC<RoomInvitedSplashProps> = ({
onClose()
})
.catch((error: Error) => {
setIsJoiningRoom(false)

const secureError =
error instanceof RoomInvitedError ? error : DEFAULT_JOIN_ROOM_ERROR

Expand Down
137 changes: 135 additions & 2 deletions src/containers/RoomContainer/hooks/useActiveRoom.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import useActiveRoomIdStore from "@/hooks/matrix/useActiveRoomIdStore"
import {useEffect, useState} from "react"
import {useCallback, useEffect, useState} from "react"
import {KnownMembership} from "matrix-js-sdk/lib/@types/membership"
import {type MatrixClient, RoomMemberEvent} from "matrix-js-sdk"
import {type MatrixClient, Room, RoomMemberEvent} from "matrix-js-sdk"
import useEventListener from "@/hooks/matrix/useEventListener"
import useMatrixClient from "@/hooks/matrix/useMatrixClient"
import useValueState, {ValueState} from "@/hooks/util/useValueState"
import {
RoomDetailOwner,
RoomDetailPreview,
RoomInvitedError,
} from "../RoomInvitedSplash"
import {getImageUrl, getRoomTopic} from "@/utils/matrix"
import {isDirectRoom} from "@/utils/rooms"
import {getOwnersIdWithPowerLevels} from "@/utils/members"
import {strCapitalize} from "@/utils/util"

export enum RoomState {
Idle,
Expand All @@ -15,14 +25,134 @@ export enum RoomState {
type UseActiveRoomReturnType = {
client: MatrixClient | null
roomState: RoomState
roomInvitedDetail: ValueState<RoomDetailPreview>
activeRoomId: string | null
clearActiveRoomId: () => void
onJoinRoom: () => Promise<void>
}

async function getChipsFromRoom(room: Room): Promise<string[]> {
const chips: string[] = []

const isDirect = isDirectRoom(room.client, room.roomId)
const isSpace = room.isSpaceRoom()
const membersCount = room.getJoinedMemberCount()

if (isDirect && !isSpace) {
chips.push("Direct")
}

if (membersCount > 0 && !isDirect) {
chips.push(`+${membersCount} Members`)
}

if (isSpace) {
chips.push("Space")
} else {
chips.push("Room")
}

if (isSpace) {
try {
const hierarchy = await room.client.getRoomHierarchy(room.roomId)

chips.push(`+${hierarchy.rooms.length} Rooms`)
} catch {
console.error(`Could not load the space hierarchy ${room.name}`)
}
}

chips.push(strCapitalize(room.getJoinRule()))

return chips
}

async function getRoomOwners(room: Room): Promise<RoomDetailOwner[] | null> {
const owners: RoomDetailOwner[] = []
try {
const ownersId = getOwnersIdWithPowerLevels(room)

const roomMembers = await room.client.getJoinedRoomMembers(room.roomId)

for (const {userId} of ownersId) {
const ownerMember = roomMembers.joined[userId]

if (ownerMember === undefined) {
continue
}

owners.push({
displayName: ownerMember.display_name,
avatarUrl: getImageUrl(ownerMember.avatar_url, room.client),
})
}
} catch (error) {
console.error(error)

return null
}

return owners.length === 0 ? null : owners
}

const useActiveRoom = (): UseActiveRoomReturnType => {
const client = useMatrixClient()
const [roomState, setRoomState] = useState(RoomState.Idle)
const {activeRoomId, clearActiveRoomId} = useActiveRoomIdStore()

const [roomInvitedDetail, setRoomInvitedDetail] =
useValueState<RoomDetailPreview>()

const setRoomInvitedDetailScope = useCallback(
(execute: () => Promise<RoomDetailPreview>) => {
execute()
.then(value => {
setRoomInvitedDetail({status: "success", data: value})
})
.catch((error: Error) => {
setRoomInvitedDetail({status: "error", error})
})
},
[setRoomInvitedDetail]
)

const onJoinRoom = useCallback(async () => {
if (client === null || activeRoomId === null) {
return
}

await client.joinRoom(activeRoomId)
}, [activeRoomId, client])

useEffect(() => {
setRoomState(RoomState.Idle)
}, [activeRoomId])

useEffect(() => {
if (client === null || activeRoomId === null) {
return
}

const room = client.getRoom(activeRoomId)

setRoomInvitedDetailScope(async () => {
if (room === null) {
throw new RoomInvitedError("You not have access to this room.")
}

const chips = await getChipsFromRoom(room)
const owners = await getRoomOwners(room)

return {
name: room.name,
topic: getRoomTopic(room) ?? undefined,
avatarUrl: getImageUrl(room.getMxcAvatarUrl(), client),
detailChips: chips,
owners: owners ?? undefined,
}
})
}, [activeRoomId, client, setRoomInvitedDetailScope])

useEffect(() => {
if (client === null || activeRoomId === null) {
return
Expand Down Expand Up @@ -82,7 +212,10 @@ const useActiveRoom = (): UseActiveRoomReturnType => {
return {
client,
roomState,
roomInvitedDetail,
activeRoomId,
clearActiveRoomId,
onJoinRoom,
}
}

Expand Down
Loading

0 comments on commit ff25f3d

Please sign in to comment.